最早是读了《Unix编程环境》,在书中有展示如何用Lex和Yacc制作一门编程语言,后来就买了《Lex与Yacc》,Flex、Bison分别是Lex、Yacc的现代版本,于是又买了《Flex与Bison》,《Flex与Bison》实际上是《Lex与Yacc》的续作,都是同样的作者。书虽然简单读了下,但里面的例子一直没有尝试的运行下。
运行环境
电脑:macOS Monterey,12.5
Flex版本2.6.4,Bison版本3.8.2。Mac下直接使用brew安装,安装完成后记得根据提示把环境配置下。
brew install flex
brew install bison
GCC版本,我使用gcc -v
命令后,实际出现的是clang的版本。
gcc -v
Apple clang version 14.0.0 (clang-1400.0.29.102)
Target: x86_64-apple-darwin21.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
WC例子
书中第一个例子类似unix下wc的例子。
%{
int chars = 0;
int words = 0;
int lines = 0;
%}
%%
[a-zA-Z]+ { words++; chars += strlen(yytext);}
\n { chars++; lines++; }
. { chars++; }
%%
int main(int argc, char** aggv) {
yylex();
printf("%8d%8d%8d\n", lines, words, chars);
return 0;
}
书中的例子main函数都没有写返回值,编译的时候回报warn了,我都加了返回参数。
flex fb1-1.l # 上面的例子保存为l后缀的文件
gcc lex.yy.c -ll # 这一步会升a.out文件
./a.out # 执行文件,输入一些字符,以control+d结束后,便会输出结果
这里需要注意的是原书中gcc lex.yy.cc -lfl
,结果一直报错。后来找到Mac应该使用-ll
替代-lfl
,参考Reddit上的这个问答:Does anyone know how to resolve this error when compiling Flex and Bison?
计算器例子
第二例子是一个计算器例子,结合了Flex和Bison
fb1-5.l
文件代码
%{
# include "fb1-5.tab.h"
%}
%%
"+" { return ADD; }
"-" { return SUB; }
"*" { return MUL; }
"/" { return DIV; }
"|" { return ABS; }
"(" { return OP; }
")" { return CP; }
[0-9]+ { yylval = atoll(yytext); return NUMBER; }
\n { return EOL; }
"//".*
[ \t] { /* ignore white space */ }
%%
fb1-5.y
文件代码,需要注意的是书中的例子类似factor default $$ = $1
,实际运行报错修改为factor { $$ = $1; }
格式。
/**/
%{
#include <stdio.h>
void yyerror(); // 新加
int main(); // 新加
int yylex(); // 新加
%}
%token NUMBER
%token ADD SUB MUL DIV ABS
%token OP CP
%token EOL
%%
calclist: /* 空规则 */
| calclist exp EOL { printf("= %d\n", $2); }
;
exp: factor { $$ = $1; }
| exp ADD factor { $$ = $1 + $3; }
| exp SUB factor { $$ = $1 - $3; }
;
factor: term { $$ = $1; }
| factor MUL term { $$ = $1 * $3; }
| factor DIV term { $$ = $1 / $3; }
;
term: NUMBER { $$ = $1; }
| ABS term { $$ = $2 >= 0? $2 : -$2; }
;
%%
void yyerror(char *s) {
fprintf(stderr, "error: %s\n", s);
}
int main(int argc, char **argv) {
yyparse();
return 0;
}
按一下顺序运行
bison -d fb1-5.y # 一定要加-d,加了以后会生成fb1-5.tab.h文件
flex fb1-5.l # 生成lex.yy.c文件
gcc fb1-5.tab.c lex.yy.c -ll # 这个也跟原书的命令不太一样,生成a.out文件
总结
其实还遇到一些跟c有关系的编译问题,比如atoi
在c99
下不可用,需要使用atoll
。总的来说,问题不大,算是都运行起来了,毕竟原书是2009年出版的。最后难能可贵的是书中例子的ftp下载地址居然还可以用,代码下载。
书中也提到使用Flex和Bison,当然也可以手写。其实对于我这样的多年写代码的人,很多时候太关注实现了,往往失去了高层次的思考,使用Flex和Bison这样的工具是让你直接关注问题本身,比如语言的一些问题。