《Flex & Bison》阅读笔记

0.056字数 710阅读 1149

最近在看 《Flex & Bison》 这本书, 针对书中的例子进行解读和笔记, 以消化每一个知识细节, 所以本文会持续更新, 直到《Flex & Bison》读完.

因为本文只是针对源码程序进行简单的笔记备忘, 不会完全讲解, 所以为了弄懂每行代码, 建议阅读《Flex & Bison》原文.

运行环境:

  • Mac 10.13
  • flex 2.5.35 Apple(flex-31)
  • bison (GNU Bison) 2.3

单词统计程序

wc.l 源代码:

%{
    int chars = 0;
    int words = 0;
    int lines = 0;
%}

%%
[^ \t\n\r\f\v]+ { words++; chars += strlen(yytext); }
\n              { chars++; lines++; }
.               { chars++; }
%%

int main(int argc, char **argv) {
    yylex();
    printf("%8d%8d%8d\n", lines, words, chars);
}

编译命令:


compile wc.l

源码备注:

  • %{ ... %} 是直接拷贝到 C 文件开头
  • %% ... %% 是模式匹配的代码区域, 左边是正则表达式, 右边是匹配的 C 代码
  • yytext 代表匹配正则表达式的字符串
  • flex 的匹配默认是从最长匹配开始, 如果有多个匹配的正则表达式, 从最早的那个开始匹配, 所以上面的模式匹配, 首先是按照单词 -> 行尾符 -> 剩余字符串的顺序进行匹配的, 不会产生重复统计的问题
  • yylex 是调用 flex 的词法分析函数 yylex 进行计算
  • Linux 系统上用 -lfl 选项编译, Mac 的编译选项是 -ll

英美式英语转换

convert.l 源代码:

%%
"colour"        { printf("color"); }
"flavour"       { printf("flavor"); }
"clever"        { printf("smart"); }
"conservative"  { printf("libreal"); }
.               { printf("%s", yytext); }
%%

int main(int argc, char **argv) {
    yylex();
}

编译命令:


compile convert.l

源码备注:

  • 匹配英式单词后, 转换称模式后的美式英语
  • 最后的点表示不转换单词

计算器

calculator.y

%{
#define YYSTYPE double
#include<stdio.h>
#include<math.h>
int yylex();
void yyerror(char *s);
%}

%token NUMBER
%token ADD SUB MUL DIV ABS
%token EOL
%token OP CP
%token POW SQRT

%%
calclist:
| calclist exp EOL { printf("= %f\n>>> ", $2); }
| calclist EOL { printf(">>> "); } /* blank line or a comment */
;

exp: factor { $$ = $1; }
| exp ADD factor { $$ = $1 + $3; }
| exp SUB factor { $$ = $1 - $3; }
;

factor: another_factor { $$ =$1; }
| factor MUL another_factor { $$ = $1 * $3; }
| factor DIV another_factor { $$ = $1 / $3; }
;

another_factor: term { $$ = $1; }
| SUB another_factor { $$ = -$2; }
| another_factor POW another_factor { $$ = pow($1,$3); }
;

term: NUMBER { $$ = $1; }
| ABS exp ABS { $$ = fabs($2); }
| OP exp CP { $$ = $2; }
| SQRT OP exp CP { $$ = sqrt($3); }
;
%%

int main(int argc, char **argv) {
    printf(">>> ");
    yyparse();
}

void yyerror(char *s) {
    fprintf(stderr,"error : %s\n",s);
}

calculator.l

%{
#define YYSTYPE double
#include "calculator.tab.h"
#include <stdlib.h>
YYSTYPE yylval;
%}

%%
"+"                             { return ADD; }
"-"                             { return SUB; }
"*"                             { return MUL; }
"/"                             { return DIV; }
"|"                             { return ABS; }
"("                             { return OP; }
")"                             { return CP; }
([0-9]*\.?[0-9]+|[0-9]+\.)      { yylval = atof(yytext); return NUMBER;}
"sqrt"                          { return SQRT; }
"**"                            { return POW; }
\n                              { return EOL; }
[ \t]                           {}
"//".*                          {}
%%

编译命令:


compile calculator

源码备注:

  • #define YYSTYPE double 要放在顶部, 这样就可以把默认的 YYSTYPE 从 int 改成 double 来支持浮点数运算
  • calculator.y 顶部定义函数 yylex 和 yyerror 的声明, 避免 cc 编译的时候报警告
  • calculator.l 模式中, 只要 { ... } 是空白就表示不处理标记 (token) , 也就可以实现忽略空格和注释字符串的功能
  • calculator.y 中越靠下的匹配规则在语法树的优先级越高, 因为它被别人应用的越多, 在语法树中越靠近树枝的部位

Flex 的正则表达式

Flex 有几个正则表达式和传统的正则表达式规则还是有点区别:

  • [a-z]{}[jv] 表示 a-z 里面再排除 j 和 v
  • / 尾部上下文, 0/1 表示匹配 01 中的 0 , / 后面的只用于尾部模式匹配, 匹配出来的是斜线前面的内容, 但斜线后面的内容并不会消耗掉, 会继续给余下的规则匹配

统计文件中的单词数

wc-file.l 源代码:

%option noyywrap

%{
    int chars = 0;
    int words = 0;
    int lines = 0;

    int totchars = 0;
    int totwords = 0;
    int totlines = 0;
%}

%%
[a-zA-Z]+       { words++; chars += strlen(yytext); }
\n              { chars++; lines++; }
.               { chars++; }
%%

int main(int argc, char **argv) {
    printf("%8s%8s%8s%8s\n", "lines", "words", "chars", "file");

    if (argc < 2) {
        yylex();
        printf("%8d%8d%8d\n", lines, words, chars);
        return 0;
    }

    for (int i = 1; i < argc; i++) {
        FILE *f = fopen(argv[i], "r");

        if (!f) {
            perror(argv[i]);
            return 1;
        }

        yyrestart(f);
        yylex();
        fclose(f);

        printf("%8d%8d%8d %s\n", lines, words, chars, argv[i]);

        totchars += chars; chars = 0;
        totwords += words; words = 0;
        totlines += lines; lines = 0;
    }

    if (argc > 1) {
        printf("%8d%8d%8d total\n", totlines, totwords, totchars);
    }

    return 0;
}

编译命令:


compile wc-file.l

阅读笔记:

  • %option noyywrap 是用于关闭 yywrap 这个鸡肋的函数, yywrap 主要用于调整 yyin 的值来读取新文件的内容
  • yyrestart(f) 放在 yylex 之前, 告诉 flex 读取文件 f 的内容

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
  • 两个月前,我夜不能寐,每天夜里会不由自主的流泪,因为委屈,因为跟身边的室友处不好关系,我总能感到自己被排...
  • 昨日夜,家里老大一直在埋头手工,半夜十一点,终于完成作品。她说是代表班级同学们赠送给老师的贺卡,封面四朵粉色卷筒式...
  • 下面是泥泞,上面是晚霞,中间流走的岁月偶尔泛起风波,又在思绪中搓成剪不断的麻,系住每一个唯美或糟糕的图画。 草汁清...
  • 上一章 (一)报名 终于,他们来到了目的地——星梦中学,这个即将占据他们人生中三年...