Jasper Ji

开口不在舌头上

0%

Flex与Bison

最早是读了《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有关系的编译问题,比如atoic99下不可用,需要使用atoll。总的来说,问题不大,算是都运行起来了,毕竟原书是2009年出版的。最后难能可贵的是书中例子的ftp下载地址居然还可以用,代码下载

书中也提到使用Flex和Bison,当然也可以手写。其实对于我这样的多年写代码的人,很多时候太关注实现了,往往失去了高层次的思考,使用Flex和Bison这样的工具是让你直接关注问题本身,比如语言的一些问题。