小白说编译原理-3-yacc基本用法
2016-04-23 13:40
453 查看
前提说明
前面lex的例子说明了可以通过定义正规式的方式,实现一个词法分析器。 它主要是通过lex编译器将用户定义的正规式以及相应的action编译成对应的c++代码,交给c++编译器生成可执行程序exe。同样的,应该可以定义语法规则,并由语法编译器将语法规则以及action编译成相应的c++代码,并执行。
这就是我们这节要说明的yacc。
yacc规则
声明部分C++头文件,函数声明
文法记号的声明(类似于lex中词法token的声明),例如%token DIGIT
%%
翻译规则
%%
翻译规则主要文法产生式和对应的动作组成。
例如 expr : expr ‘+’ term {$$ = $1 + $2}
其他c++语言的代码
yacc示例,简单计算器
%{ %} %name yacc // class definition { // place any extra class members here virtual int yygettoken(); } // constructor { // place any extra initialisation code here } // destructor { // place any extra cleanup code here } // place any declarations here %include { #ifndef YYSTYPE #define YYSTYPE double #endif } %% lines : lines expr '\n' {printf("%f\n", $2);} | lines '\n' |//empty line ; expr : expr '+' term {$$ = $1 + $3;} | expr '-' term {$$ = $1 - $3;} | term ; term : term '*' factor {$$ = $1 * $3;} | term '/' factor {$$ = $1 / $3;} | factor {$$=$1;} ; factor : '(' expr ')' {$$=$2;} | DIGIT ; DIGIT : '0' { $$ = 0.0; } | '1' { $$ = 1.0; } | '2' { $$ = 2.0; } | '3' { $$ = 3.0; } | '4' { $$ = 4.0; } | '5' { $$ = 5.0; } | '6' { $$ = 6.0; } | '7' { $$ = 7.0; } | '8' { $$ = 8.0; } | '9' { $$ = 9.0; } ; %% int YYPARSERNAME::yygettoken() { return getchar(); } int main() { //create a lexer, and call the lex function. //it will read from stdin and parser the tokens. int n = 1; YYPARSERNAME parser; if (parser.yycreate()) { n = parser.yyparse(); } return n; }
计算器解释
添加gettoken函数virtual int yygettoken();
expr : expr ‘+’ term {$$ = $1 + $3;}
计算加法,$$表示当返回值,\$1表示第一个参数,\$3表示第三个参数
main
创建parser语法分析器,调用gettoken分析,即可。
巧妙的地方
1,支持多行多行的关键在于lines的表示,lines既可以表示为空,又可以表示为lines expr ‘\n’
2,支持算符优先级
term和factor的使用,先使用term,再使用factor
3,支持括号
最终的factor因子的表示,既可以表示单个数字,还可以表示(expr)。
3,只支持单个数字的加减乘除
这是由于对于DIGIT的解释只支持单个数字
更新版本支持浮点数
%{ #include <stdio.h> %} %name yacc // class definition { // place any extra class members here virtual int yygettoken(); double yyvalue; } // constructor { // place any extra initialisation code here } // destructor { // place any extra cleanup code here } // place any declarations here %include { #ifndef YYSTYPE #define YYSTYPE double #endif } %token NUMBER %% lines : lines expr '\n' {printf("%f\n", $2);} | lines '\n' |//empty line ; expr : expr '+' term {$$ = $1 + $3;} | expr '-' term {$$ = $1 - $3;} | term ; term : term '*' factor {$$ = $1 * $3;} | term '/' factor {$$ = $1 / $3;} | factor {$$=$1;} ; factor : '(' expr ')' {$$=$2;} | NUMBER {$$=yyvalue;} ; %% int YYPARSERNAME::yygettoken() { int c; double value; while((c = getchar()) == ' ');//jump the empty //here not the blank if(isdigit(c) || c == '.'){ ungetc(c, stdin); cin>>value; yyvalue = value; return NUMBER; } return c; } int main() { //create a lexer, and call the lex function. //it will read from stdin and parser the tokens. int n = 1; YYPARSERNAME parser; if (parser.yycreate()) { n = parser.yyparse(); } return n; }
修改的地方
删除了DIGIT采用语法分析器进行的表达,过去gettoken直接返回char型字符,由DIGIT或者+,-等进行语法翻译。现在使用一个token标记NUMBER, 它代表一个数字。这个NUMBER标记是由对应的词法分析gettoken进行返回的。gettoken中,如果遇到数字,则使用cin方式读取double型变量,保存到变量yylvalue中,这样当语法分析的过程中,遇到number,将yylvalue赋值给当前返回即可,即可以读取到浮点值。
为了更好的适应性,词法分析中还进行了跳过空格的操作,对于非数字不进行解析,直接返回。
本人lpstudy,转载请注明出处: http://blog.csdn.net/lpstudy/article/details/51225953
相关文章推荐
- 自定义控件:自定义组合控件+自定义属性
- 记录在项目中碰到的问题和需要总结的东西(1)
- LA 3983 Robotruck
- 软件更新系统
- 阅读Android源码
- XMPP学习笔记
- 码农小汪剑指Offer之39-和为S的连续正数序列
- apache的/var/www/html无法访问
- C++继承方式(转载)
- php 扩展学习笔记
- 不花一分钱,网站日均UV过百万!
- android 远程显示设备与本地设备坐标转换
- 一个非常棒的jQuery 评分插件
- centos 7下安装vsftp
- Session是什么
- 使用Adblock Plus屏蔽极客学院网站上的悬浮页面元素
- TL431基本应用
- 在SpringMVC+mybatis框架中,servlet调用的service注解
- socket初接触---多用户服务器简单交互(下)
- Android利用canvas画各种图形(点、直线、弧、圆、椭圆、文字、矩形、多边形、曲线、圆角矩形)