利用Yacc生成LR语法分析器的关键点…
2014-02-24 15:03
155 查看
实际上利用Yacc生成LR语法分析器(一般是Look
ahead分析方法)还是比较简单的,而且写一个文法满足Yacc的要求也非常容易,主要的工作量体现在一个产生式被识别后的动作如何编写,尤其是有时候需要在一个产生式中嵌入一个动作,这个动作产生的时机是什么,产生这个动作时当前的token是什么一定要非常清楚,通过这几天的研究,基本上对这个问题已经有了一个了解,这里做一些总结。
首先,Yacc中判断如果DFA的当前状态中的某一项S->a.发生归约时,执行产生式S->a后面带的动作。但是S->a.归约发生前可能需要获得下一个token,也有可能不需要获得,这个要具体情况具体分析。另外对于嵌入动作,Yacc会在产生式中插入一个新的非终结符E,其中E->空,当E->空.归约发生时,嵌入动作执行。
例1:
%{
#include <ctype.h>
#include <stdio.h>
#define YYSTYPE double
int c;
%}
%token NUMBER
%%
lines :
expr
{ printf("%d\n", $1); }
;
expr
: expr{printf("a%c\n",c);} '+'
NUMBER
{ $$ = $1 + $4; }
| expr {printf("b%c\n",c);}'-'
NUMBER
{ $$ = $1 - $4; }
| NUMBER{printf("c%c\n",c);}
;
%%
int main(void)
{
return
yyparse();
}
int yylex(void)
{
while ((c =
getchar()) == ' ');
if
(isdigit(c)) {
ungetc(c, stdin);
scanf("%d", &yylval);
return NUMBER;
}
if(c=='\n')
{
c='$';
return
0;
}
return
c;
}
如果输入:2
输出结果如下:
2
c2
2
也就是说在执行printf("c%c\n",c);}时,当前token是2,这是因为在当前状态下只有一个exp->number.可以执行归约,因为不需要获得下一个token.
例2:
expr
: NUMBER{printf("a%c\n",c);} '+'
NUMBER
{ $$ = $1 + $4; }
| NUMBER {printf("b%c\n",c);}'-'
NUMBER
{ $$ = $1 - $4; }
| NUMBER{printf("c%c\n",c);}
;
如果输入:2
输出结果如下:
2
c$
2
也就是说在执行也就是说在执行printf("c%c\n",c);}时,当前token是$,这是因为当前状态下除了exp->number.可以以执行归约之外,还有E1->空(第一个嵌入动作)和E2->空(第二个嵌入动作)也都可以执行归约,到底需要执行哪个归约需要看下一个token。
例3:
expr
: NUMBER{printf("a%c\n",c);} '+'
NUMBER
{ printf("aa%c\n",c);$$ = $1 + $4; }
| NUMBER {printf("b%c\n",c);}'-'
NUMBER
{ $$ = $1 - $4; }
| NUMBER{printf("c%c\n",c);}
如果输入:2+3
输出结果如下:
2+3
a+
aa3
5
从上可以看出,在执行printf("a%c\n",c);}时,当前token是+,这是因为当前状态下有E1->空(第一个嵌入动作)和E2->空(第二个嵌入动作)都可以执行归约,到底需要执行哪个归约需要看下一个token,如果是+号则需要执行第一个嵌入动作,如果是-号则需要执行第二个嵌入动作。至于执行printf("aa%c\n",c)时,当前token是3原因也很简单,那就是当前状态下只有exp->NUMBER
E1 '+' NUMBER 可以执行归约。
例4:
expr
: NUMBER{printf("a%c\n",c);} '+'
NUMBER
{ printf("aa%c\n",c);$$ = $1 + $4; }
如果输入:2+3
输出结果如下:
2+3
a2
aa3
5
从上可以看出,在执行printf("a%c\n",c);}时,当前token是2,这是因为当前状态下只有E1->空(第一个嵌入动作)可以执行归约。
例5:
expr
: ID{printf("a%c\n",c);} '+'
ID
{ printf("aa%c\n",c);$$ = $1 + $4; }
;
ID
: NUMBER{printf("c%c\n",c);$$=$1;}
如果输入:2+3
输出结果如下:
2+3
c2 //执行printf("c%c\n",c);}时,当前token是2
a2 //执行printf("a%c\n",c);}时,当前token是2
c3
aa3
5
例6:
expr
: ID{printf("a%c\n",c);} '+'
ID
{ printf("aa%c\n",c);$$ = $1 + $4; }
| ID{printf("b%c\n",c);} '-'
ID
{ $$ = $1 - $4; }
;
ID
: NUMBER{printf("c%c\n",c);$$=$1;}
如果输入:2+3
输出结果如下:
2+3
c2 ///执行printf("c%c\n",c);}时,当前token是2
a+ //执行printf("a%c\n",c);}时,当前token是+
c3
aa3
5
ahead分析方法)还是比较简单的,而且写一个文法满足Yacc的要求也非常容易,主要的工作量体现在一个产生式被识别后的动作如何编写,尤其是有时候需要在一个产生式中嵌入一个动作,这个动作产生的时机是什么,产生这个动作时当前的token是什么一定要非常清楚,通过这几天的研究,基本上对这个问题已经有了一个了解,这里做一些总结。
首先,Yacc中判断如果DFA的当前状态中的某一项S->a.发生归约时,执行产生式S->a后面带的动作。但是S->a.归约发生前可能需要获得下一个token,也有可能不需要获得,这个要具体情况具体分析。另外对于嵌入动作,Yacc会在产生式中插入一个新的非终结符E,其中E->空,当E->空.归约发生时,嵌入动作执行。
例1:
%{
#include <ctype.h>
#include <stdio.h>
#define YYSTYPE double
int c;
%}
%token NUMBER
%%
lines :
expr
{ printf("%d\n", $1); }
;
expr
: expr{printf("a%c\n",c);} '+'
NUMBER
{ $$ = $1 + $4; }
| expr {printf("b%c\n",c);}'-'
NUMBER
{ $$ = $1 - $4; }
| NUMBER{printf("c%c\n",c);}
;
%%
int main(void)
{
return
yyparse();
}
int yylex(void)
{
while ((c =
getchar()) == ' ');
if
(isdigit(c)) {
ungetc(c, stdin);
scanf("%d", &yylval);
return NUMBER;
}
if(c=='\n')
{
c='$';
return
0;
}
return
c;
}
如果输入:2
输出结果如下:
2
c2
2
也就是说在执行printf("c%c\n",c);}时,当前token是2,这是因为在当前状态下只有一个exp->number.可以执行归约,因为不需要获得下一个token.
例2:
expr
: NUMBER{printf("a%c\n",c);} '+'
NUMBER
{ $$ = $1 + $4; }
| NUMBER {printf("b%c\n",c);}'-'
NUMBER
{ $$ = $1 - $4; }
| NUMBER{printf("c%c\n",c);}
;
如果输入:2
输出结果如下:
2
c$
2
也就是说在执行也就是说在执行printf("c%c\n",c);}时,当前token是$,这是因为当前状态下除了exp->number.可以以执行归约之外,还有E1->空(第一个嵌入动作)和E2->空(第二个嵌入动作)也都可以执行归约,到底需要执行哪个归约需要看下一个token。
例3:
expr
: NUMBER{printf("a%c\n",c);} '+'
NUMBER
{ printf("aa%c\n",c);$$ = $1 + $4; }
| NUMBER {printf("b%c\n",c);}'-'
NUMBER
{ $$ = $1 - $4; }
| NUMBER{printf("c%c\n",c);}
如果输入:2+3
输出结果如下:
2+3
a+
aa3
5
从上可以看出,在执行printf("a%c\n",c);}时,当前token是+,这是因为当前状态下有E1->空(第一个嵌入动作)和E2->空(第二个嵌入动作)都可以执行归约,到底需要执行哪个归约需要看下一个token,如果是+号则需要执行第一个嵌入动作,如果是-号则需要执行第二个嵌入动作。至于执行printf("aa%c\n",c)时,当前token是3原因也很简单,那就是当前状态下只有exp->NUMBER
E1 '+' NUMBER 可以执行归约。
例4:
expr
: NUMBER{printf("a%c\n",c);} '+'
NUMBER
{ printf("aa%c\n",c);$$ = $1 + $4; }
如果输入:2+3
输出结果如下:
2+3
a2
aa3
5
从上可以看出,在执行printf("a%c\n",c);}时,当前token是2,这是因为当前状态下只有E1->空(第一个嵌入动作)可以执行归约。
例5:
expr
: ID{printf("a%c\n",c);} '+'
ID
{ printf("aa%c\n",c);$$ = $1 + $4; }
;
ID
: NUMBER{printf("c%c\n",c);$$=$1;}
如果输入:2+3
输出结果如下:
2+3
c2 //执行printf("c%c\n",c);}时,当前token是2
a2 //执行printf("a%c\n",c);}时,当前token是2
c3
aa3
5
例6:
expr
: ID{printf("a%c\n",c);} '+'
ID
{ printf("aa%c\n",c);$$ = $1 + $4; }
| ID{printf("b%c\n",c);} '-'
ID
{ $$ = $1 - $4; }
;
ID
: NUMBER{printf("c%c\n",c);$$=$1;}
如果输入:2+3
输出结果如下:
2+3
c2 ///执行printf("c%c\n",c);}时,当前token是2
a+ //执行printf("a%c\n",c);}时,当前token是+
c3
aa3
5
相关文章推荐
- 利用Yacc生成LR语法分析器的关键点剖析
- 利用LR生成图表分析性能
- Syngram Helper实验:读入文法文件动态生成语法分析器
- 利用php访问数据库数据表,生成javabean.java文件,符合javabean语法
- LL语法分析器和LR语法分析器的比较
- 编译原理课设——《TINY编译器》,用yacc生成语法扫描实现编译器
- LR(1)语法分析表生成
- 语法分析器自动生成工具一览
- LR(1)语法分析表生成
- 构造LR(1)项目集,生成LR(1)分析表、进行相应的语法分析
- 利用flex &amp; bison (lex &amp; yacc)创建可重入(线程安全)的词法分析和语法解析器
- eclipse ERMaster插件安装 利用ERMaster快速生成db维护文档
- 利用python生成一个导出数据库的bat脚本文件
- 利用 Oracle EM 企业管理器 进行oracle SQL的优化(自动生成索引)
- 基于IdeaIDE的ssm整合框架的maven管理项目,利用映射自动生成dao层,pojo层,mappers层
- 利用ItextPdf、core-renderer-R8 来生成PDF
- 利用java自动生成验证码
- MyEclipse把数据库中的表生成java实体类--利用Hibernate
- 在Windows、Linux下,分别利用BAT批处理文件和Shell脚本,生成类似“20150605_111327.txt”命名的文件
- 利用mybatis-generator自动生成JavaBean,mapping映射文件等