实验3:基于YACC的TINY语法分析器的构建
目录
3、lex与yacc数据传递方法(Windows下为flex与bison):
1. parsing table分析表部分截图(具体结果上文中已展示)
(2)分析和总结(对用工具和手工编写程序进行比较,给出结论)
(3)实验中出现的冲突及解决过程描述(理论,描述有哪些可能的冲突,操作中,描述具体遇到的冲突和解决方案)
一、实验要求
运用YACC,针对TINY语言,构造一个语法分析器。给出实验方案,实施并描述结果。
二、实验方案
(评价依据:实验方案设计是否合理,描述的输入、输出的设计是否合理,重点检查如何设计lex和yacc这二个阶段数据传递方法)
1、输入设计
TINY源程序,保存在文本文件test.txt中,案例参照书本:
2、输出设计
输出TINY语法树,用缩进格式表示,深度为1的节点缩进1个tab,深度为2节点缩进2个tab,深度为3的节点缩进3个tab……以此类推。
利用yydebug为程序设计生成的Yacc分析程序描绘关于Token序列(如输入序列:2+3)、语法词法分析结果(分析结束,该输入串符合LALR(1)文法)、执行步骤解释说明(这里展示每一步的action动作以及state状态,也是输出设计中最复杂最麻烦的环节),其中所有的执行步骤解释说明需要根据LALR(1)分析表进行逐级输出。总结:如果上述串w在L(G)中,则输出w的自底向上分析过程中的规约步骤;否则给出一个错误提示,即以下语法树。
3、lex与yacc数据传递方法(Windows下为flex与bison):
① Windows下的lex工具对应flex工具,yacc工具对应bison工具,由于Yacc有许多不同的实现以及存在着通常称作bison的若干个公共领域版本,所以在它的运算细节中有许多变化,而这又可能与这里所用的版本有些不同。
② 两者都是在Windows下通过命令行进行编译的,即先写好.l文件和.y文件,然后在命令行中cd到当前文件的路径下,而.y文件即yacc产生的子程序申请读入下一个indentifier时会调用yylex(),将相关的属性值存入全局量yylval。
③ 使用flex命令(flex工具对应lex)“flex .l文件”编译.l文件,使之变成lex.yy.c文件备用,再使用bison命令(bison工具对应yacc)“bison -d .y文件”编译.y文件,使之变成yacc.tab.c、yacc.tab.h文件备用,其中yacc.tab.h包含所有yacc描述文件中的语句种别,它又会被包含在lex描述文件中。
④ Windows下,修改yychar方法,使之指针不再指向yylex方法,而是指向getToken自定义的函数方法以进行模块衔接联调。
⑤ 再用文本文件(如devc++等)编写一个main.c文件,在其头文件上加入lex.yy.c和yacc.tab.c、yacc.tab.h的文件的引入,最后通过mingw32-make命令去编译main.c文件,就可以完成lex和yacc的同时使用,构造 LALR(1)分析器——tiny.out编译器,在里面输入我们需要测试的文件名即可进行自底向上的分析。
四、分析表parsing table问题
理论和设计(描述parsing table在实验方案中的作用,观察并输出parsing table)
parsing table用于帮助LALR(1)词法分析器,根据当前所处的状态和输入流中的下一个token,决定下一步动作(移入或用某一条产生式进行归约),并判断何时结束语法分析(到达accept状态),如同人工手写的LALR(1)文法分析表。
对于Yacc输出的语法分析函数yyparse()对一个文件(文本文件)进行自底向上分析时,如果出现找不到对应的parsing table中的动作,则会调用yyerror()报错,需要人工检查parsing table对比文本文件进行检查排除非法错误,保证正确分析。
其中针对某一文法的parsing table可以通过“bison -v”命令获得,即本程序中的tiny.output,具体内容如下所示:
Terminals unused in grammar
ERROR
Grammar
0 $accept: program $end
1 program: stmt_seq
2 stmt_seq: stmt_seq SEMI stmt
3 | stmt
4 stmt: if_stmt
5 | repeat_stmt
6 | assign_stmt
7 | read_stmt
8 | write_stmt
9 | error
10 if_stmt: IF exp THEN stmt_seq END
11 | IF exp THEN stmt_seq ELSE stmt_seq END
12 repeat_stmt: REPEAT stmt_seq UNTIL exp
13 $@1: /* empty */
14 assign_stmt: ID $@1 ASSIGN exp
15 read_stmt: READ ID
16 write_stmt: WRITE exp
17 exp: simple_exp LT simple_exp
18 | simple_exp EQ simple_exp
19 | simple_exp
20 simple_exp: simple_exp PLUS term
21 | simple_exp MINUS term
22 | term
23 term: term TIMES factor
24 | term OVER factor
25 | factor
26 factor: LPAREN exp RPAREN
27 | NUM
28 | ID
29 | error
Terminals, with rules where they appear
$end (0) 0
error (256) 9 29
IF (258) 10 11
THEN (259) 10 11
ELSE (260) 11
END (261) 10 11
REPEAT (262) 12
UNTIL (263) 12
READ (264) 15
WRITE (265) 16
ID (266) 14 15 28
NUM (267) 27
ASSIGN (268) 14
EQ (269) 18
LT (270) 17
PLUS (271) 20
MINUS (272) 21
TIMES (273) 23
OVER (274) 24
LPAREN (275) 26
RPAREN (276) 26
SEMI (277) 2
ERROR (278)
Nonterminals, with rules where they appear
$accept (24)
on left: 0
program (25)
on left: 1, on right: 0
stmt_seq (26)
on left: 2 3, on right: 1 2 10 11 12
stmt (27)
on left: 4 5 6 7 8 9, on right: 2 3
if_stmt (28)
on left: 10 11, on right: 4
repeat_stmt (29)
on left: 12, on right: 5
assign_stmt (30)
on left: 14, on right: 6
$@1 (31)
on left: 13, on right: 14
read_stmt (32)
on left: 15, on right: 7
write_stmt (33)
on left: 16, on right: 8
exp (34)
on left: 17 18 19, on right: 10 11 12 14 16 26
simple_exp (35)
on left: 20 21 22, on right: 17 18 19 20 21
term (36)
on left: 23 24 25, on right: 20 21 22 23 24
factor (37)
on left: 26 27 28 29, on right: 23 24 25
state 0
0 $accept: . program $end
error shift, and go to state 1
IF shift, and go to state 2
REPEAT shift, and go to state 3
READ shift, and go to state 4
WRITE shift, and go to state 5
ID shift, and go to state 6
program go to state 7
stmt_seq go to state 8
stmt go to state 9
if_stmt go to state 10
repeat_stmt go to state 11
assign_stmt go to state 12
read_stmt go to state 13
write_stmt go to state 14
state 1
9 stmt: error .
$default reduce using rule 9 (stmt)
state 2
10 if_stmt: IF . exp THEN stmt_seq END
11 | IF . exp THEN stmt_seq ELSE stmt_seq END
error shift, and go to state 15
ID shift, and go to state 16
NUM shift, and go to state 17
LPAREN shift, and go to state 18
exp go to state 19
simple_exp go to state 20
term go to state 21
factor go to state 22
state 3
12 repeat_stmt: REPEAT . stmt_seq UNTIL exp
error shift, and go to state 1
IF shift, and go to state 2
REPEAT shift, and go to state 3
READ shift, and go to state 4
WRITE shift, and go to state 5
ID shift, and go to state 6
stmt_seq go to state 23
stmt go to state 9
if_stmt go to state 10
repeat_stmt go to state 11
assign_stmt go to state 12
read_stmt go to state 13
write_stmt go to state 14
state 4
15 read_stmt: READ . ID
ID shift, and go to state 24
state 5
16 write_stmt: WRITE . exp
error shift, and go to state 15
ID shift, and go to state 16
NUM shift, and go to state 17
LPAREN shift, and go to state 18
exp go to state 25
simple_exp go to state 20
term go to state 21
factor go to state 22
state 6
14 assign_stmt: ID . $@1 ASSIGN exp
$default reduce using rule 13 ($@1)
$@1 go to state 26
state 7
0 $accept: program . $end
$end shift, and go to state 27
state 8
1 program: stmt_seq .
2 stmt_seq: stmt_seq . SEMI stmt
SEMI shift, and go to state 28
$default reduce using rule 1 (program)
state 9
3 stmt_seq: stmt .
$default reduce using rule 3 (stmt_seq)
state 10
4 stmt: if_stmt .
$default reduce using rule 4 (stmt)
state 11
5 stmt: repeat_stmt .
$default reduce using rule 5 (stmt)
state 12
6 stmt: assign_stmt .
$default reduce using rule 6 (stmt)
state 13
7 stmt: read_stmt .
$default reduce using rule 7 (stmt)
state 14
8 stmt: write_stmt .
$default reduce using rule 8 (stmt)
state 15
29 factor: error .
$default reduce using rule 29 (factor)
state 16
28 factor: ID .
$default reduce using rule 28 (factor)
state 17
27 factor: NUM .
$default reduce using rule 27 (factor)
state 18
26 factor: LPAREN . exp RPAREN
error shift, and go to state 15
ID shift, and go to state 16
NUM shift, and go to state 17
LPAREN shift, and go to state 18
exp go to state 29
simple_exp go to state 20
term go to state 21
factor go to state 22
state 19
10 if_stmt: IF exp . THEN stmt_seq END
11 | IF exp . THEN stmt_seq ELSE stmt_seq END
THEN shift, and go to state 30
state 20
17 exp: simple_exp . LT simple_exp
18 | simple_exp . EQ simple_exp
19 | simple_exp .
20 simple_exp: simple_exp . PLUS term
21 | simple_exp . MINUS term
EQ shift, and go to state 31
LT shift, and go to state 32
PLUS shift, and go to state 33
MINUS shift, and go to state 34
$default reduce using rule 19 (exp)
state 21
22 simple_exp: term .
23 term: term . TIMES factor
24 | term . OVER factor
TIMES shift, and go to state 35
OVER shift, and go to state 36
$default reduce using rule 22 (simple_exp)
state 22
25 term: factor .
$default reduce using rule 25 (term)
state 23
2 stmt_seq: stmt_seq . SEMI stmt
12 repeat_stmt: REPEAT stmt_seq . UNTIL exp
UNTIL shift, and go to state 37
SEMI shift, and go to state 28
state 24
15 read_stmt: READ ID .
$default reduce using rule 15 (read_stmt)
state 25
16 write_stmt: WRITE exp .
$default reduce using rule 16 (write_stmt)
state 26
14 assign_stmt: ID $@1 . ASSIGN exp
ASSIGN shift, and go to state 38
state 27
0 $accept: program $end .
$default accept
state 28
2 stmt_seq: stmt_seq SEMI . stmt
error shift, and go to state 1
IF shift, and go to state 2
REPEAT shift, and go to state 3
READ shift, and go to state 4
WRITE shift, and go to state 5
ID shift, and go to state 6
stmt go to state 39
if_stmt go to state 10
repeat_stmt go to state 11
assign_stmt go to state 12
read_stmt go to state 13
write_stmt go to state 14
state 29
26 factor: LPAREN exp . RPAREN
RPAREN shift, and go to state 40
state 30
10 if_stmt: IF exp THEN . stmt_seq END
11 | IF exp THEN . stmt_seq ELSE stmt_seq END
error shift, and go to state 1
IF shift, and go to state 2
REPEAT shift, and go to state 3
READ shift, and go to state 4
WRITE shift, and go to state 5
ID shift, and go to state 6
stmt_seq go to state 41
stmt go to state 9
if_stmt go to state 10
repeat_stmt go to state 11
assign_stmt go to state 12
read_stmt go to state 13
write_stmt go to state 14
state 31
18 exp: simple_exp EQ . simple_exp
error shift, and go to state 15
ID shift, and go to state 16
NUM shift, and go to state 17
LPAREN shift, and go to state 18
simple_exp go to state 42
term go to state 21
factor go to state 22
state 32
17 exp: simple_exp LT . simple_exp
error shift, and go to state 15
ID shift, and go to state 16
NUM shift, and go to state 17
LPAREN shift, and go to state 18
simple_exp go to state 43
term go to state 21
factor go to state 22
state 33
20 simple_exp: simple_exp PLUS . term
error shift, and go to state 15
ID shift, and go to state 16
NUM shift, and go to state 17
LPAREN shift, and go to state 18
term go to state 44
factor go to state 22
state 34
21 simple_exp: simple_exp MINUS . term
error shift, and go to state 15
ID shift, and go to state 16
NUM shift, and go to state 17
LPAREN shift, and go to state 18
term go to state 45
factor go to state 22
state 35
23 term: term TIMES . factor
error shift, and go to state 15
ID shift, and go to state 16
NUM shift, and go to state 17
LPAREN shift, and go to state 18
factor go to state 46
state 36
24 term: term OVER . factor
error shift, and go to state 15
ID shift, and go to state 16
NUM shift, and go to state 17
LPAREN shift, and go to state 18
factor go to state 47
state 37
12 repeat_stmt: REPEAT stmt_seq UNTIL . exp
error shift, and go to state 15
ID shift, and go to state 16
NUM shift, and go to state 17
LPAREN shift, and go to state 18
exp go to state 48
simple_exp go to state 20
term go to state 21
factor go to state 22
state 38
14 assign_stmt: ID $@1 ASSIGN . exp
error shift, and go to state 15
ID shift, and go to state 16
NUM shift, and go to state 17
LPAREN shift, and go to state 18
exp go to state 49
simple_exp go to state 20
term go to state 21
factor go to state 22
state 39
2 stmt_seq: stmt_seq SEMI stmt .
$default reduce using rule 2 (stmt_seq)
state 40
26 factor: LPAREN exp RPAREN .
$default reduce using rule 26 (factor)
state 41
2 stmt_seq: stmt_seq . SEMI stmt
10 if_stmt: IF exp THEN stmt_seq . END
11 | IF exp THEN stmt_seq . ELSE stmt_seq END
ELSE shift, and go to state 50
END shift, and go to state 51
SEMI shift, and go to state 28
state 42
18 exp: simple_exp EQ simple_exp .
20 simple_exp: simple_exp . PLUS term
21 | simple_exp . MINUS term
PLUS shift, and go to state 33
MINUS shift, and go to state 34
$default reduce using rule 18 (exp)
state 43
17 exp: simple_exp LT simple_exp .
20 simple_exp: simple_exp . PLUS term
21 | simple_exp . MINUS term
PLUS shift, and go to state 33
MINUS shift, and go to state 34
$default reduce using rule 17 (exp)
state 44
20 simple_exp: simple_exp PLUS term .
23 term: term . TIMES factor
24 | term . OVER factor
TIMES shift, and go to state 35
OVER shift, and go to state 36
$default reduce using rule 20 (simple_exp)
state 45
21 simple_exp: simple_exp MINUS term .
23 term: term . TIMES factor
24 | term . OVER factor
TIMES shift, and go to state 35
OVER shift, and go to state 36
$default reduce using rule 21 (simple_exp)
state 46
23 term: term TIMES factor .
$default reduce using rule 23 (term)
state 47
24 term: term OVER factor .
$default reduce using rule 24 (term)
state 48
12 repeat_stmt: REPEAT stmt_seq UNTIL exp .
$default reduce using rule 12 (repeat_stmt)
state 49
14 assign_stmt: ID $@1 ASSIGN exp .
$default reduce using rule 14 (assign_stmt)
state 50
11 if_stmt: IF exp THEN stmt_seq ELSE . stmt_seq END
error shift, and go to state 1
IF shift, and go to state 2
REPEAT shift, and go to state 3
READ shift, and go to state 4
WRITE shift, and go to state 5
ID shift, and go to state 6
stmt_seq go to state 52
stmt go to state 9
if_stmt go to state 10
repeat_stmt go to state 11
assign_stmt go to state 12
read_stmt go to state 13
write_stmt go to state 14
state 51
10 if_stmt: IF exp THEN stmt_seq END .
$default reduce using rule 10 (if_stmt)
state 52
2 stmt_seq: stmt_seq . SEMI stmt
11 if_stmt: IF exp THEN stmt_seq ELSE stmt_seq . END
END shift, and go to state 53
SEMI shift, and go to state 28
state 53
11 if_stmt: IF exp THEN stmt_seq ELSE stmt_seq END .
$default reduce using rule 11 (if_stmt)
观察上表不难发现,TINY文法的LALR分析表中不存在移入归约冲突和归约归约冲突,所以TINY文法是LALR(1)文法。
五、内容和步骤
1、针对TINY语言给出 yacc的.y文件的代码
%{
#define YYPARSER /* YYPARSER解析器识别别的文件传输的代码 */
#include <stdio.h>
#include "others.h" /* 引入头文件包 */
#define YYSTYPE TreeNode * /* 定义语法树节点 */
static char * savedName;
static int savedLineNo;
static TreeNode * savedTree; /* 存储语法树节点 */
%}
/* 定义终结符token序列 */
%token IF THEN ELSE END REPEAT UNTIL READ WRITE /* TINY语言的保留字 */
%token ID NUM /* identifier和number终结符 */
%token ASSIGN EQ LT PLUS MINUS TIMES OVER LPAREN RPAREN SEMI /* 相关算数运算符以及括号分号等终结符 */
%token ERROR /* error错误终结符 */
%% /* TINY语言的文法规则 */
/* program文法规则定义如下 */
program : stmt_seq
{
savedTree = $1;
}; /* 从属性栈中取出savedTree的属性值归约 */
/* stmt_seq文法规则定义如下 */
stmt_seq : stmt_seq SEMI stmt
{ YYSTYPE t = $1;
if (t != NULL) /* 节点存在 */
{
while (t->sibling != NULL) /* 其兄弟分支存在 */
{
t = t->sibling;
}
t->sibling = $3;
$$ = $1;
}
else
{
$$ = $3;
}
}
| stmt { $$ = $1; };
/* stmt文法规则定义如下 */
stmt : if_stmt { $$ = $1; }
| repeat_stmt { $$ = $1; }
| assign_stmt { $$ = $1; }
| read_stmt { $$ = $1; }
| write_stmt { $$ = $1; }
| error { $$ = NULL; };
/* if_stmt文法规则定义如下 */
if_stmt : IF exp THEN stmt_seq END /* 单if情况 */
{
$$ = newStmtNode(IfK);
$$->child[0] = $2;
$$->child[1] = $4;
}
| IF exp THEN stmt_seq ELSE stmt_seq END /* 存在分支情况 */
{
$$ = newStmtNode(IfK);
$$->child[0] = $2;
$$->child[1] = $4;
$$->child[2] = $6;
};
/* repeat_stmt文法规则定义如下 */
repeat_stmt : REPEAT stmt_seq UNTIL exp
{
$$ = newStmtNode(RepeatK);
$$->child[0] = $2;
$$->child[1] = $4;
};
/* assign_stmt文法规则定义如下 */
assign_stmt : ID
{
savedName = copyString(tokenString);
savedLineNo = lineno;
}
ASSIGN exp
{
$$ = newStmtNode(AssignK);
$$->child[0] = $4;
$$->attr.name = savedName;
$$->lineno = savedLineNo;
};
/* read_stmt文法规则定义如下 */
read_stmt : READ ID
{
$$ = newStmtNode(ReadK);
$$->attr.name = copyString(tokenString);
};
/* write_stmt文法规则定义如下 */
write_stmt : WRITE exp
{
$$ = newStmtNode(WriteK);
$$->child[0] = $2;
};
/* exp文法规则定义如下 */
exp : simple_exp LT simple_exp
{
$$ = newExpNode(OpK);
$$->child[0] = $1;
$$->child[1] = $3;
$$->attr.op = LT;
}
| simple_exp EQ simple_exp
{
$$ = newExpNode(OpK);
$$->child[0] = $1;
$$->child[1] = $3;
$$->attr.op = EQ;
}
| simple_exp
{
$$ = $1;
};
/* simple_exp文法规则定义如下 */
simple_exp : simple_exp PLUS term
{
$$ = newExpNode(OpK);
$$->child[0] = $1;
$$->child[1] = $3;
$$->attr.op = PLUS;
}
| simple_exp MINUS term
{
$$ = newExpNode(OpK);
$$->child[0] = $1;
$$->child[1] = $3;
$$->attr.op = MINUS;
}
| term { $$ = $1; };
/* term文法规则定义如下 */
term : term TIMES factor
{
$$ = newExpNode(OpK);
$$->child[0] = $1;
$$->child[1] = $3;
$$->attr.op = TIMES;
}
| term OVER factor
{
$$ = newExpNode(OpK);
$$->child[0] = $1;$$->
child[1] = $3;
$$->attr.op = OVER;
}
| factor { $$ = $1; };
/* factor文法规则定义如下 */
factor : LPAREN exp RPAREN
{
$$ = $2;
}
| NUM
{
$$ = newExpNode(ConstK);
$$->attr.val = atoi(tokenString);
}
| ID
{
$$ = newExpNode(IdK);
$$->attr.name = copyString(tokenString);
}
| error
{
$$ = NULL;
};
%%
/* 定义yyerror来提供错误提示,返回错误行以及当前的token序列 */
int yyerror(char * message)
{ fprintf(listing,"Syntax error at line %d: %s\n",lineno,message);
fprintf(listing,"Current token: ");
printToken(yychar,tokenString);
Error = TRUE;
return 0;
}
/* 定义语法分析树进行parse()识别 */
TreeNode * parse(void)
{ yyparse();
return savedTree;
}
2、给出.l文件的代码
%{
#include <stdio.h>
#include "others.h" /* 引入头文件包 */
/* lexeme of identifier or reserved word */
char tokenString[MAXTOKENLEN+1];
%}
%option noyywrap
DIGIT [0-9]
NUMBER {digit}+
LETTER [a-zA-Z]
IDENTIFIER {letter}+
LINE \n
SPACE [ \t]+
%%
"if" {return IF;}
"then" {return THEN;}
"else" {return ELSE;}
"end" {return END;}
"repeat" {return REPEAT;}
"until" {return UNTIL;}
"read" {return READ;}
"write" {return WRITE;}
":=" {return ASSIGN;}
"=" {return EQ;}
"<" {return LT;}
"+" {return PLUS;}
"-" {return MINUS;}
"*" {return TIMES;}
"/" {return OVER;}
"(" {return LPAREN;}
")" {return RPAREN;}
";" {return SEMI;}
{NUMBER} {return NUM;}
{IDENTIFIER} {return ID;}
{LINE} {lineno++;}
{SPACE} {/* 跳过空白 */}
"{" { char c;
do
{ c = input();
if (c == EOF) break;
if (c == '\n') lineno++;
} while (c != '}');
}
. {return ERROR;}
%%
TokenType getToken(void)
{
static int firstTime = TRUE;
TokenType currentToken;
if (firstTime)
{
firstTime = FALSE;
lineno++;
yyin = source;
yyout = listing;
}
currentToken = yylex();
strncpy(tokenString,yytext,MAXTOKENLEN);
if (TraceScan)
{
fprintf(listing,"\t%d: ",lineno);
printToken(currentToken,tokenString);
}
return currentToken;
}
3、实验具体步骤
① 分析实验题目需求,确定输入设计和输出设计。针对输入设计中的字符串序列预定义token,根据书本案例输入test.txt 中。针对输出设计中的语法树选择对应的 .y文件中的辅助函数数据结构——树(定义了父节点tnode、子节点child、兄弟节点sibling)。
② 针对输入设计的TINY语言给出yacc的 .y文件的设计代码,同时给出 .l文件的设计代码,如上代码所示。其中yacc的 tiny.y文件用于语法分析,lex的tiny.l文件用于词法分析,前者以LALR(1)构成语法分析器、后者构成词法分析扫描器。
③ 编写辅助C模块。定义全局变量和工具函数,存储在globals.h、util.h和util.c中;然后封装flex和bison生成的yylex和yyparse函数,得到getToken和parse函数,存储在parse.h和scan.h中;最后编写主函数,设计TINY源文读取函数,并调用parse函数进行语法分析,输出语法树,存储在main.c中。
④ 使用“bison -v tiny.y”命令生成parsing table分析表,内容保存在tiny.output中,其中有53个项目集状态(内容在上述分析表内容中已经展示)。
⑤ 使用“bison -d tiny.y”命令生成tiny.tab.c和tiny.tab.h文件,如下图所示。
⑥ 使用“flex tiny.l”命令生成lex.yy.c文件,如下图所示。
⑦ 修改tiny.tab.c文件中的代码,yacc生成的tiny.tab.c中使用yylex()函数来获取字符,需要替换成由以上lex生成的自定义的getToken()函数。即yychar = yylex()修改为yychar = getToken()(这里由于代码行过多,建议使用ctrl+F查找)。
⑧ 修改tiny.tab.c与tiny.tab.h文件名为y.tab.c与y.tab.h(与之后的mingw32-make命令对应,否则会出现编译连接失败的情况)。
⑨ 使用“mingw32-make”命令将main.c文件与上述三个文件y.tab.c、y.tab.h、lex.yy.c编译链接,即将语法分析程序和词法分析扫描器一同连接成为一个tiny.out编译器。
⑩ 使用上述创建的编译器进行最终测试样例(输入设计中的文本)的测试,即tiny.out test.txt,得出语法树。
六、实验结果
1. parsing table分析表部分截图(具体结果上文中已展示)
2、实验对于输入测试的输出结果截图
七、实验结论:
(1)实验结论(是否能够准确描述实验的结论)
Bison利用LALR(1)分析法生成语法分析器,其首先构造给定文法的LALR(1) DFA,再根据该DFA构造LALR(1)分析表,并利用该分析表进行语法分析。使用Bison的前提是待分析的文法是LALR(1)文法,也就是LALR(1)分析表中不存在移入归约冲突和归约归约冲突。
考虑到Bison仅仅是语法分析器的生成工具,而语法分析器的输入是词法分析器得到的token序列,因此需要将Bison和Flex配合使用。在Windows下使用flex和bison,lex进行词法分析扫描,而后产生的结果可以传递给yacc进行LALR(1)语法分析。二者在Windows下通过mingw32-make命令由main.c主函数分配变量和配置全局变量,产生tiny.out编译器可以进行语法分析。
应用Bison和Flex生成语法分析器具有编程效率高、不易出错的优点,能够完美取代传统的手工编写方法。并且经测试,本次实验中生成的TINY语法分析器能够正确地分析TINY程序的语法并生成语法树。
(2)分析和总结(对用工具和手工编写程序进行比较,给出结论)
使用工具编写程序:面对词法分析和语法分析时,若使用工具则可以通过lex和yacc规定下的格式进行编写构造对应的文件,由相对应的编译器直接给出结果,不用考虑其他复杂的流程,不仅大大提升了编程效率,还提高了准确性,相比手工编写程序具有巨大优势。
使用手写编写程序:我们需要先根据给定的文法构造LR(1) DFA,再将该DFA进行化简,得到LALR(1) DFA,然后将其转化为LALR(1)分析表,最后根据该分析表编写语法分析程序。上述过程虽然可以掌握整个流程的控制以及详细到底层代码设计,但是费时费力。对于较简单的文法而言尚可接受,但过程繁琐、容易出错;对于较为复杂的文法,通过人工方式很难正确计算出LALR(1)分析表,因此存在着较大的局限性。
(3)实验中出现的冲突及解决过程描述(理论,描述有哪些可能的冲突,操作中,描述具体遇到的冲突和解决方案)
① 冲突问题1:在使用bison命令“bison -d .y文件”编译 tiny.y文件时,报错:win_bison: D:\VSProgram\Project1\data/m4sugar/m4sugar.m4: cannot open: No such file or directory……
解决过程:尝试了各种方法,重建了很多次 .y文件,都一直不成功。后来通过查找资料,得知出现此报错情况有两种问题。第一,bison没有放在没有空格和汉字的目录下;第二,没有把下载的win_flex_bison-2.5.5.zip里面的data目录复制到win_bison.exe的目录下。通过检查,发现项目文件目录下缺少data目录,将data复制进来后,成功解决问题。
② 冲突问题2:在实验时,没有像步骤⑧那样将tiny.tab.c和tiny.tab.h的文件名修改为y.tab.c和y.tab.h导致使用mingw32-make命令时一直报错,该命令一直找不到对应的文件,结果如下图所示。
解决过程:同实验步骤⑧,修改文件名即可,原因如下:在mingw32-make定义内部它仅支持y.tab.c、y.tab.h(由.y文件生成的子程序),即文件名存在限定‘同理也不能修改lex.yy.c的文件名。