编译原理——实验3:基于YACC的TINY语法分析器的构建

实验3:基于YACC的TINY语法分析器的构建

目录

一、实验要求

二、实验方案

1、输入设计

2、输出设计

3、lex与yacc数据传递方法(Windows下为flex与bison):

四、分析表parsing table问题

五、内容和步骤

1、针对TINY语言给出 yacc的.y文件的代码

2、给出.l文件的代码

3、实验具体步骤

六、实验结果

1. parsing table分析表部分截图(具体结果上文中已展示)

2、实验对于输入测试的输出结果截图

七、实验结论:

(1)实验结论(是否能够准确描述实验的结论)

(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.hutil.hutil.c中;然后封装flex和bison生成的yylex和yyparse函数,得到getToken和parse函数,存储在parse.hscan.h中;最后编写主函数,设计TINY源文读取函数,并调用parse函数进行语法分析,输出语法树,存储在main.c中。

 ④ 使用“bison -v tiny.y”命令生成parsing table分析表,内容保存在tiny.output中,其中有53个项目集状态(内容在上述分析表内容中已经展示)。

⑤ 使用“bison -d tiny.y”命令生成tiny.tab.ctiny.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.cy.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的文件名。 

  • 16
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 基于yacctiny语法分析器构建是一种常见的方法,它可以帮助程序员快速地构建一个符合tiny语法规则的分析器。yacc是一种自动化工具,它可以根据用户提供的文法规则生成一个语法分析器。在使用yacc构建tiny语法分析器时,程序员需要先定义tiny语法规则,然后使用yacc生成相应的分析器代码。生成的代码可以通过编译器进行编译,最终得到一个可执行的分析器程序。通过使用基于yacctiny语法分析器,程序员可以更加方便地进行代码分析和调试,提高代码的可读性和可维护性。 ### 回答2: Yacc是Unix中的一种工具程序,用于生成编译器中的语法分析器。它是自动机器的助手,用于编写和维护语言处理程序,它接受一个输入文件,由该文件生成对应的C语言程序。Tiny语言是一种简易的编程语言,不支持函数、数组和嵌套循环等高级特性,仅支持基本的赋值、运算和分支语句等基础操作。 Tiny语法分析器构建要从以下几个方面入手: 1. 定义语言的文法规则。Tiny语言的文法规则包括程序(Program)、语句(Statement)、赋值语句(Assignment)、表达式(Expression)和条件语句(Conditional statement)等,可以通过BNF范式来描述文法规则。例如,赋值语句的文法规则可表示为<assignment>:=<id>:=<expression>,其中<id>表示标识符,<expression>表示表达式。 2. 生成语法分析器。通过在Yacc输入文件中定义文法规则,Yacc自动构建语法分析器Yacc输入文件通常由三个部分组成:声明部分(%{和%})、文法规则部分和C代码部分。声明部分包括需要引入的头文件和全局变量等,文法规则部分包括文法规则和对应的语义动作,而C代码部分则包括对语法树的操作等语义动作实现。 3. 基于语法分析树实现语义动作。语法分析器根据定义的文法规则将输入的程序解析成语法分析树,接着对语法树进行遍历,实现相应的语义动作。例如,在赋值语句中,需要将表达式的计算结果赋给对应的变量;在条件语句中,需要判断分支条件并选择对应的执行路径等。 4. 测试和调试。在完成语法分析器构建后,需要进行测试和调试,以保证语法分析器的正确性。对于Tiny语言,可以编写一些简单的程序,并将其输入到语法分析器中,观察输出结果是否符合预期。 通过以上步骤,可以基于Yacc构建高效、可靠的Tiny语法分析器,并对其进行测试和验证,为后续的编译工作打下坚实的基础。 ### 回答3: Yacc是一款强大的语法分析器生成器,其可以方便地生成C或C++程序,用于处理指定的语法规则。在本文中,我们将介绍如何使用Yacc构建一个Tiny语法分析器Tiny语言是一个极简的编程语言,其主要结构包括变量声明、赋值语句、算术运算符和条件语句等。因此,我们将使用Yacc构建Tiny语言的语法分析器,以便能够分析和解析Tiny代码。 首先,我们需要定义Tiny语言的语法规则。Tiny语言的语法规则可以用BNF(巴科斯范式)来表示,例如: program -> stmt_list stmt_list -> stmt | stmt stmt_list stmt -> id := expr | if expr then stmt_list end expr -> id | int | expr op expr op -> + | - | * | / 在这里,我们定义了Tiny程序的语法规则,和各种语句、表达式,以及运算符的语法规则。 接下来,我们需要使用Yacc来定义这些语法规则并生成相应的语法分析器。在这里,我们需要创建一个文件,命名为"tiny.y",并在其中定义Tin语言的语法规则。例如: %{ #include <stdio.h> %} %token ID INT IF THEN END ASSIGN PLUS MINUS TIMES DIV LPAREN RPAREN SEMICOLON %% program: stmt_list stmt_list: stmt | stmt stmt_list stmt: ID ASSIGN expr SEMICOLON | IF expr THEN stmt_list END expr: ID | INT | expr PLUS expr | expr MINUS expr | expr TIMES expr | expr DIV expr %% int main() { yyparse(); return 0; } 在这个例子中,我们首先使用"%{"和"%}"包围的代码段定义了所需要的头文件。然后我们使用"%token"定义了Tin语言中的词法符号,例如ID、INT、IF、THEN、END、ASSIGN、PLUS、MINUS、TIMES、DIV、LPAREN、RPAREN和SEMICOLON。 在接下来的代码中,我们使用"%%"分隔符来分隔不同的部分。在"program"部分,我们定义了Tiny程序的语法规则;在"stmt_list"部分,我们定义了语句列表的语法规则;在"stmt"部分,我们定义了语句的语法规则;在"expr"部分,我们定义了表达式的语法规则。 最后,在代码的底部,我们定义了一个小程序"main"。在这个程序中,我们调用了"yyparse()"函数来启动Yacc语法分析器,从而解析Tiny代码。 在本文中,我们介绍了如何基于Yacc构建一个Tiny语法分析器。通过这种语法分析器,我们可以方便地解析Tiny代码,识别程序的语法结构并生成相应的分析结果。当然,这只是一个简单的例子,我们可以根据具体的需要,对语法分析器进行细致的优化和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阮阮的阮阮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值