写个demo练练手
我们的第一个同时使用flex和bison的程序将是一个桌面计算器(desk calculator) 。首先,我们编写一个词法分析器,接着我们编写一个语法分析器并把两者接合起来。
为了让事情变得简单,我们的计算器只需要识别整数、基本算术运算符和一元绝对值操作符。
例子:一个简单的flex词法分析器cal.l
%%
"+" { printf("PLUS\n"); }
"-" { printf("MINUS\n”); }
"*" { printf("TIMES\n"); }
"/" { printf("DIVIDE\n"); }
"|" { printf("ABS\n"); }
[0-9]+ { printf("NUMBER %s\n", yytext); }
\n { printf("NEWLINE\n"); }
[\t] { }
. { printf("Xue xi zhe wan yi you sha yong %s\n", yytext); }
%%
前5个模式就是操作符本身,用引号引起,而目前的动作仅仅是打印出匹配的内容。引号告诉flex使用引号内文本的原义,而不是把它们解释称正则表达式。
第6个模式匹配一个整数。这种方括号括起的模式[0-9]可以匹配任意一个数字,接着的+这个符号表示匹配一个或者多个前面的项,也就是由一个或者多个数字组成的字符串。相关的动作利用语法分析器每次匹配后所设置的yytext来打印出匹配的字符串。
第7个模式匹配一个换行符,它使用C语言通常使用的序列:\n。
第8个模式用来忽略空白字符。它匹配任意一个空格或者tab(\t),相关的动作无需做任何事情。
最后一个模式用来匹配其他模式所没有匹配的内容。相关的动作打印出恰当的瞎话。
这个程序咱就简简单单来一下子,菜慢慢上,饭慢慢吃。咋运行我就不用说了吧,不会的看一下上一篇文章。
稍微添加一点剂量
作为协同程序的语法分析器:大多数(包含flex词法分析器)的程序使用词法分析器来获取一个记号流,这样可以方便语法分析器的处理。每当程序需要一个记号时,它调用yylex()来读取一小部分输入然后返回相应的记号。每次它返回的时候,它会记住当前处理的位置,并从这个位置开始去处理下一次调用。如果一个模式不能够产生一个用于调用程序且不可以返回的记号时,词法分析器会在这次yylex()调用中继续分析下去。
下面这个例子,包含了两个可以返回记号的模式,一个返回+操作符,另一个返回数字,还包含一个不做任何事情的空白字符的模式,这个模式用来忽略它所匹配的内容。
"+" { return ADD; }
[0-9] { return NUMBER; }
[ \t] { /* 忽略空白字符*/ }
其实到现在你应该确定一件事情,没错这个返回值就是给语法分析器(bison)用的。要不他俩怎么链接在一起的嘛。
现在我们开始修改上面的那个简单的词法分析器。来,改成我这样!
%{
enum yytokentype {
NUMBER = 258,
ADD = 259,
SUB = 260,
MUL = 261,
DIV = 262,
ABS = 263,
EOL = 264
};
int yylval;
%}
%%
"+" { return ADD; }
"-" { return SUB; }
"*" { return MUL; }
"/" { return DIV; }
"|" { return ABS; }
[0-9]+ { yylval = atoi(yytext); return NUMBER; }
\n { return EOL; }
[ \t] { }
. { printf("Mystery character %c\n", *yytext); }
%%
main(int argc, char **argv)
{
int tok;
while(tok = yylex()) {
printf("%d", tok);
if (tok == NUMBER) printf(" = %d\n", yylval);
else printf("\n");
}
}
我们在一个C语言的enum中定义记号编号。接着我们把yylval(存储记号值)定义为整型,这对于我们第一版的计算器来说已经够用。现在我们就有了一个能够工作的词法分析器,下一篇语法分析器走起。