首页 > 其他分享 >HNU-编译原理lab2-2022级

HNU-编译原理lab2-2022级

时间:2025-01-12 19:29:04浏览次数:3  
标签:node end 2022 pos lab2 HNU --+ declaration expression

文章目录

实验要求

本次实验需要各位同学首先将自己的 lab1 的词法部分复制到 /src/parser 目录的 lexical_analyzer.l并合理修改相应部分,然后根据 cminus-f 的语法补全 syntax_analyer.y 文件,完成语法分析器,要求最终能够输出解析树。如:

输入:

int bar;
float foo(void) { return 1.0; }

parser 将输出如下解析树:

>--+ program
|  >--+ declaration-list
|  |  >--+ declaration-list
|  |  |  >--+ declaration
|  |  |  |  >--+ var-declaration
|  |  |  |  |  >--+ type-specifier
|  |  |  |  |  |  >--* int
|  |  |  |  |  >--* bar
|  |  |  |  |  >--* ;
|  |  >--+ declaration
|  |  |  >--+ fun-declaration
|  |  |  |  >--+ type-specifier
|  |  |  |  |  >--* float
|  |  |  |  >--* foo
|  |  |  |  >--* (
|  |  |  |  >--+ params
|  |  |  |  |  >--* void
|  |  |  |  >--* )
|  |  |  |  >--+ compound-stmt
|  |  |  |  |  >--* {
|  |  |  |  |  >--+ local-declarations
|  |  |  |  |  |  >--* epsilon
|  |  |  |  |  >--+ statement-list
|  |  |  |  |  |  >--+ statement-list
|  |  |  |  |  |  |  >--* epsilon
|  |  |  |  |  |  >--+ statement
|  |  |  |  |  |  |  >--+ return-stmt
|  |  |  |  |  |  |  |  >--* return
|  |  |  |  |  |  |  |  >--+ expression
|  |  |  |  |  |  |  |  |  >--+ simple-expression
|  |  |  |  |  |  |  |  |  |  >--+ additive-expression
|  |  |  |  |  |  |  |  |  |  |  >--+ term
|  |  |  |  |  |  |  |  |  |  |  |  >--+ factor
|  |  |  |  |  |  |  |  |  |  |  |  |  >--+ float
|  |  |  |  |  |  |  |  |  |  |  |  |  |  >--* 1.0
|  |  |  |  |  |  |  |  >--* ;
|  |  |  |  |  >--* }

请注意,上述解析树含有每个解析规则的所有子成分,包括诸如 ; { } 这样的符号,请在编写规则时务必不要忘了它们。

主要工作

  1. 了解 bison 基础知识和理解 Cminus-f 语法(重在了解如何将文法产生式转换为 bison 语句)
  2. 阅读 /src/common/SyntaxTree.c,对应头文件 /include/SyntaxTree.h(重在理解分析树如何生成)
  3. 了解 bisonflex 之间是如何协同工作,看懂pass_node函数并改写 Lab1 代码(提示:了解 yylval 是如何工作,在代码层面上如何将值传给$1$2等)
  4. 补全 src/parser/syntax_analyzer.y 文件和 lexical_analyzer.l 文件

Tips:在未编译的代码文件中是无法看到关于协同工作部分的代码,建议先编译 1.3 给出的计算器样例代码,再阅读 /build/src/parser/ 中的 syntax_analyzer.hsyntax_analyzer.c 文件

实验难点

1、Bison文件

Bison是一个语法分析器的生成工具, 可以将 LALR 文法转换成可编译的 C 代码,文件扩展名为 .y。在Bison文件中给出LALR文法以及一些分析动作,编译就可以产生一个语法分析器。

每个 Bison 文件由 %% 分成三部分,如实验给出的例子:

%{
#include <stdio.h>
/* 这里是序曲 */
/* 这部分代码会被原样拷贝到生成的 .c 文件的开头 */
int yylex(void);
void yyerror(const char *s);
%}

/* 这些地方可以输入一些 bison 指令 */
/* 比如用 %start 指令指定起始符号,用 %token 定义一个 token */
%start reimu
%token REIMU

%%
/* 从这里开始,下面是解析规则 */
reimu : marisa { /* 这里写与该规则对应的处理代码 */ puts("rule1"); }
      | REIMU  { /* 这里写与该规则对应的处理代码 */ puts("rule2"); }
      ; /* 规则最后不要忘了用分号结束哦~ */
      
/* 这种写法表示 ε —— 空输入 */
marisa : { puts("Hello!"); }

%%
/* 这里是尾声 */
/* 这部分代码会被原样拷贝到生成的 .c 文件的末尾 */

int yylex(void)
{
    int c = getchar(); // 从 stdin 获取下一个字符 
    switch (c) {
    case EOF: return YYEOF;
    case 'R': return REIMU;
    default:  return 0;     // 返回无效 token 值,迫使 bison 报错
    }
}

void yyerror(const char *s)
{
    fprintf(stderr, "%s\n", s);
}

int main(void)
{
    yyparse(); // 启动解析
    return 0;
}

Bison与lab1中Lex文件的编写规则类似,由%%区分的三部分构成,开头和结尾会被拷贝进.c文件,我们主要关注第二部分解析规则。

2、分析树的生成

查看/include/SyntaxTree.h文件:

#ifndef __SYNTAXTREE_H__
#define __SYNTAXTREE_H__

#include <stdio.h>

#define SYNTAX_TREE_NODE_NAME_MAX 30

struct _syntax_tree_node {
	struct _syntax_tree_node * parent;
	struct _syntax_tree_node * children[10];
	int children_num;

	char name[SYNTAX_TREE_NODE_NAME_MAX];
};
typedef struct _syntax_tree_node syntax_tree_node;

syntax_tree_node * new_anon_syntax_tree_node();
syntax_tree_node * new_syntax_tree_node(const char * name);
int syntax_tree_add_child(syntax_tree_node * parent, syntax_tree_node * child);
void del_syntax_tree_node(syntax_tree_node * node, int recursive);

struct _syntax_tree {
	syntax_tree_node * root;
};
typedef struct _syntax_tree syntax_tree;

syntax_tree* new_syntax_tree();
void del_syntax_tree(syntax_tree * tree);
void print_syntax_tree(FILE * fout, syntax_tree * tree);

#endif /* SyntaxTree.h */

这段代码定义了一个简单的语法树(Syntax Tree)及其操作接口,适用于编译器、解释器或其他需要树状结构的数据管理场景。语法树由多个节点组成,每个节点用 _syntax_tree_node 表示,包含指向父节点的 parent 指针、最多 10 个子节点的 children 数组、子节点数量的 children_num 和节点名称 name

通过函数 new_anon_syntax_tree_nodenew_syntax_tree_node,可以创建匿名或指定名称的节点;通过 syntax_tree_add_child 将子节点添加到父节点;通过 del_syntax_tree_node 删除节点,支持递归删除所有子节点。此外,代码提供了 _syntax_tree 结构,用于表示一棵语法树,其中 root 为根节点,并通过 new_syntax_treedel_syntax_tree 创建或销毁整棵树。函数 print_syntax_tree 支持将语法树的结构输出到文件流,用于调试或展示。

每个终结符都对应着一个叶子节点,词法分析时会完成叶子节点的产生。在自底向上的分析过程中,首先产生的是叶子节点,剩下构建语法分析树的过程由语法分析来负责。叶子节点的产生在./src/parser/lexical_analyzer.lpass_node()函数中实现,创建一个新的节点,并将其指针赋值给yylval,节点名为其成分(非终结符名或终结符名)。

3、理解Bison 和 Flex 的关系

参考文档给出了一个样例代码,我们可以先对其分析解释:

①YYSTYPE

YYSTYPE: 在 bison 解析过程中,每个 symbol 最终都对应到一个语义值上。或者说,在 parse tree 上,每个节点都对应一个语义值,这个值的类型是 YYSTYPEYYSTYPE 的具体内容是由 %union 构造指出的, 不同的节点对应着不同的终结符,可能为不同的类型,因此union中可以包含不同的数据类型。比如下面代码,我们可能希望ADDOP的值是 char 类型,而 NUMBER 应该是 double 类型的。

%union {
  char   op;
  double num;
}

会生成类似这样的代码:

typedef union YYSTYPE {
  char op;
  double num;
} YYSTYPE;
②归约

以加法为例:

term : term ADDOP factor
     {
        switch $2 {
        case '+': $$ = $1 + $3; break;
        case '-': $$ = $1 - $3; break;
        }
     }

当前节点使用 $$ 代表,而已解析的节点则是从左到右依次编号,称作 $1, $2, $3

③%type

term 应该使用 num 部分,那么我们就写成:

%type <num> term

而不是类似结构体定义的那样term.opterm.num

④%token

当我们用 %token 声明一个 token 时,这个 token 就会导出到 .h 中,可以在 C 代码中直接使用,供 flex 使用。%token <op> ADDOP 与之类似,但顺便也将 ADDOP 传递给 %type

yylval

这时候我们可以打开 .h 文件,看看里面有什么。除了 token 定义,最末尾还有一个 extern YYSTYPE yylval; 。这个变量我们上面已经使用了,通过这个变量,我们就可以在 lexer 里面设置某个 token 的值。

实验设计

1、补充lexical_analyzer.l

我们要将lab1中完成的lexical_analyzer.l放进./src/parser/lexical_analyzer.l文件中,但不是完全照搬。在词法分析的识别过程中,每次识别要生成一个词法单元叶子结点,所以要添加一个pass_node()以生成节点。

flex会将识别出来的结果交给Basin来构建语法树,注意到lab1中我们考虑了注释,空格等特殊符号,而语法树的构建是不用考虑这些的,所以对于这些token不用生成节点。

同时lab1中的void analyzer函数也不再需要显示地声明。

代码中已经为我们给出了一个样例:

\+   {pos_start = pos_end; pos_end++; pass_node(yytext); return ADD;}

我们可以仿照它来修改:

%%
  /* 运算 */
\+   {pos_start = pos_end; pos_end++; pass_node(yytext); return ADD;}
\-   {pos_start = pos_end; pos_end++; pass_node(yytext);return SUB;}
\*   {pos_start = pos_end; pos_end++; pass_node(yytext);return MUL;}
\/   {pos_start = pos_end; pos_end++; pass_node(yytext);return DIV;}
\<   {pos_start = pos_end; pos_end++; pass_node(yytext);return LT;}
"<=" {pos_start = pos_end; pos_end+=2; pass_node(yytext);return LTE;}
\>   {pos_start = pos_end; pos_end++; pass_node(yytext);return GT;}
">=" {pos_start = pos_end; pos_end+=2; pass_node(yytext);return GTE;}
"==" {pos_start = pos_end; pos_end+=2; pass_node(yytext);return EQ;}  
"!=" {pos_start = pos_end; pos_end+=2; pass_node(yytext);return NEQ;}
\=   {pos_start = pos_end; pos_end++; pass_node(yytext);return ASSIN;}

 /* 符号 */
\;   {pos_start = pos_end; pos_end++; pass_node(yytext);return SEMICOLON;}
\,   {pos_start = pos_end; pos_end++; pass_node(yytext);return COMMA;}
\(  {pos_start = pos_end; pos_end++; pass_node(yytext);return LPARENTHESE;}
\)  {pos_start = pos_end; pos_end++; pass_node(yytext);return RPARENTHESE;}
\[  {pos_start = pos_end; pos_end++; pass_node(yytext);return LBRACKET;}
\]  {pos_start = pos_end; pos_end++; pass_node(yytext);return RBRACKET;}
\{  {pos_start = pos_end; pos_end++; pass_node(yytext);return LBRACE;}
\}  {pos_start = pos_end; pos_end++; pass_node(yytext);return RBRACE;}

 /* 关键字 */
else {pos_start = pos_end; pos_end+=4; pass_node(yytext);return ELSE;}
if   {pos_start = pos_end; pos_end+=2; pass_node(yytext);return IF;}
int  {pos_start = pos_end; pos_end+=3; pass_node(yytext);return INT;}
float {pos_start = pos_end; pos_end+=5; pass_node(yytext);return FLOAT;}
return {pos_start = pos_end; pos_end+=6; pass_node(yytext);return RETURN;}
void   {pos_start = pos_end; pos_end+=4; pass_node(yytext);return VOID;}
while  {pos_start = pos_end; pos_end+=5; pass_node(yytext);return WHILE;}

 /* ID & NUM */
[a-zA-Z]+ {pos_start = pos_end; pos_end+=strlen(yytext); pass_node(yytext);return IDENTIFIER;}
[a-zA-Z]  {pos_start = pos_end; pos_end++; pass_node(yytext);return LETTER;}
[0-9]+    {pos_start = pos_end; pos_end+=strlen(yytext); pass_node(yytext);return INTEGER;}
[0-9]+\.|[0-9]*\.[0-9]+ {pos_start = pos_end; pos_end+=strlen(yytext); pass_node(yytext);return FLOATPOINT;}
\[\]  {pos_start = pos_end; pos_end+=2; pass_node(yytext);return ARRAY;}

 /* others */
\n  {lines++; pos_start = 1; pos_end = 1;}
\/\*([^\*]|(\*)*[^\*\/])*(\*)*\*\/ 
{
     for(int i=0;i<strlen(yytext);i++)
     {
		if(yytext[i]=='\n')
          {
			pos_start=1;
			pos_end=1;
			lines++;
		}
		else pos_end++;
     }
}
[ \f\n\r\t\v] {pos_start = pos_end;pos_end+=strlen(yytext);}
. {return ERROR;}
%%

2、补充syntax_analyzer.y

(1)union

我们在上面已经讨论过,在 bison 解析过程中,每个 symbol 最终都对应到一个语义值上。或者说,在 parse tree 上,每个节点都对应一个语义值,这个值的类型是 YYSTYPEYYSTYPE 的具体内容是由 %union 构造指出的, 不同的节点对应着不同的终结符,可能为不同的类型,因此union中可以包含不同的数据类型。

对于节点的声明,在union里加入已经定义好的syntax_tree_node *node即可:

%union {
    syntax_tree_node *node;
}
(2)%token与%type

在实验文档给出的代码中,我们可以看到token与type的具体用法,%token的符号是大写,代表终结符,%type的符号是小写,代表非终结符。终结符名要与我们刚刚补齐的代码中的名字相同,而非终结符名可以从实验文档中找到:

我们将 Cminus-f 的所有规则分为五类:

1.字面量、关键字、运算符与标识符
    id
    type-specifier
    relop
    addop
    mulop
2.声明
    declaration-list
    declaration
    var-declaration
    fun-declaration
    local-declarations
3.语句
    compound-stmt
    statement-list
    statement
    expression-stmt
    iteration-stmt
    selection-stmt
    return-stmt
4.表达式
    expression
    var
    additive-expression
    term
    factor
    integer
    float
    call
5.其他
    params
    param-list
    param
    args
    arg-list
起始符号是 program。

声明结果如下:

%start program
%token <node> ADD SUB MUL DIV
%token <node> LT LTE GT GTE EQ NEQ ASSIN
%token <node> SEMICOLON COMMA LPARENTHESE RPARENTHESE LBRACKET RBRACKET LBRACE RBRACE
%token <node> ELSE IF INT FLOAT RETURN VOID WHILE IDENTIFIER LETTER INTEGER FLOATPOINT ARRAY
%type <node> type-specifier relop addop mulop
%type <node> declaration-list declaration var-declaration fun-declaration local-declarations
%type <node> compound-stmt statement-list statement expression-stmt iteration-stmt selection-stmt return-stmt
%type <node> simple-expression expression var additive-expression term factor integer float call
%type <node> params param-list param args arg-list
%type <node> program
(4)%type的解析规则

源代码中同样给出了一个实例:

program : declaration-list { $$ = node("program", 1, $1); gt->root = $$; }

这句表示 program 规则的语法动作部分。前面写出规则名称,在规则后面的 { } 内部分是语法动作部分,也就是 Bison 在匹配到该规则时执行的 C 代码。

$$ = node("program", 1, $1);

  • $$ 是当前规则(program)的语法值,表示整个程序节点的值。
  • node("program", 1, $1) 是创建一个新的语法树节点的函数调用,表示在语法树中创建一个名为 "program" 的节点,并且该节点有一个子节点($1),它是 declaration-list 的值。1 表示这个节点的子节点个数。

gt->root = $$;

  • rootgt 结构中的一个字段,表示语法树的根节点。
  • $$ 是刚刚创建的 program 节点,它作为程序的根节点被赋值给 gt->root,将整个程序的语法树根节点存储在 gt 中。

对于其他规则的编写,可以参考实验文档给出的Cminus-f语法规则:

‘program→declaration-list‘
	//表示一个程序由 declaration-list(声明列表)组成。
‘declaration-list→declaration-list declaration | declaration‘
	//declaration-list 是一个声明的序列,可以是多个声明组合,也可以是单独一个声明。
‘declaration→var-declaration | fun-declaration‘
	//声明可以是变量声明(var-declaration)或函数声明(fun-declaration)
‘var-declaration →type-specifier ID ; | type-specifier ID [ INTEGER ] ;‘
	//变量声明可以是以下两种形式:
	//type-specifier ID ;:声明一个简单变量,例如 int x;。
	//type-specifier ID [ INTEGER ] ;:声明一个数组变量,例如 int arr[10];。
‘type-specifier→int | float | void‘
 	//类型说明符可以是 int、float 或 void。
‘fun-declaration→type-specifier ID ( params ) compound-stmt‘
    //函数声明的结构如下:
	//返回类型(type-specifier)。
	//函数名(ID)。
	//参数列表(params)。
	//函数体(compound-stmt)。
‘params→param-list | void‘
    //数参数可以是参数列表(param-list)或 void(表示无参数)。
‘param-list→param-list , param | param‘
    //参数列表可以是多个参数(用逗号分隔)或单个参数。
‘param→type-specifier ID | type-specifier ID []‘
    //每个参数可以是简单变量(int x)或数组(int arr[])。
‘compound-stmt→{ local-declarations statement-list}‘
    //复合语句由局部声明(local-declarations)和语句列表(statement-list)组成,包含在花括号 {} 中。
‘local-declarations→local-declarations var-declaration | empty‘
    //局部声明可以包含多个变量声明,也可以为空。
‘statement-list→statement-list statement | empty‘
    //语句列表可以包含多个语句,也可以为空。
‘statement→ expression-stmt| compound-stmt| selection-stmt| iteration-stmt| return-stmt‘
    //语句包括表达式语句、复合语句、选择语句、循环语句和返回语句。
‘expression-stmt→expression ; | ;‘
    //表达式语句可以是一个表达式加分号,或者仅是一个分号(空语句)。
‘selection-stmt→ if ( expression ) statement| if ( expression ) statement else statement‘
    //选择语句是 if 语句或 if-else 语句。
‘iteration-stmt→while ( expression ) statement‘
    //循环语句是 while 循环。
‘return-stmt→return ; | return expression ;‘
    //返回语句可以是不带值的 return 或带值的 return expression。
‘expression→var = expression | simple-expression‘
    //表达式可以是赋值操作,也可以是简单表达式。
‘var→ID | ID [ expression]‘
    //变量可以是简单变量或数组元素。
‘simple-expression→additive-expression relop additive-expression | additive-expression‘
    //简单表达式可以是加法表达式之间用关系运算符连接,或者是单独一个加法表达式。
‘relop →<= | < | > | >= | == | !=‘
    //关系运算符包括:小于等于、小于、大于、大于等于、等于、不等于。
‘additive-expression→additive-expression addop term | term‘
    //加法表达式由加法运算符连接的项组成,或者是单独一个项
‘addop→+ | -‘
    //加法运算符是 + 或 -。
‘term→term mulop factor | factor‘
    //项是由乘法运算符连接的因子组成,或者是单独一个因子。
‘mulop→* | /‘
    //乘法运算符是 * 或 /。
‘factor→( expression ) | var | call | integer | float‘
    //因子可以是括号中的表达式、变量、函数调用、整数或浮点数。
‘integer→INTEGER‘
‘float→FLOATPOINT‘
‘call→ID ( args)‘
    //函数调用由函数名(ID)和参数(args)组成
‘args→arg-list | empty‘
    //函数参数可以是参数列表或为空。
‘arg-list→arg-list , expression | expression‘
    //参数列表是多个用逗号分隔的表达式。

补充完的解析规则如下:

program : declaration-list { $$ = node("program", 1, $1); gt->root = $$; }
declaration-list : declaration-list declaration { $$ = node("declaration-list", 2, $1, $2);}
                 | declaration { $$ = node("declaration-list", 1, $1); }
declaration : var-declaration { $$ = node("declaration", 1, $1); }
            | fun-declaration { $$ = node("declaration", 1, $1); }
var-declaration : type-specifier IDENTIFIER SEMICOLON { $$ = node("var-declaration", 3, $1, $2, $3); }
                | type-specifier IDENTIFIER LBRACKET INTEGER RBRACKET SEMICOLON { $$ = node("var-declaration", 6, $1, $2, $3, $4, $5, $6); }
type-specifier : INT { $$ = node("type-specifier", 1, $1); }
               | FLOAT { $$ = node("type-specifier", 1, $1); }
               | VOID { $$ = node("type-specifier", 1, $1); }
fun-declaration : type-specifier IDENTIFIER LPARENTHESE params RPARENTHESE compound-stmt { $$ = node("fun-declaration", 6, $1, $2, $3, $4, $5, $6); }
params : param-list { $$ = node("params", 1, $1); }
       | VOID { $$ = node("params", 1, $1); }
param-list : param-list COMMA param { $$ = node("param-list", 3, $1, $2, $3); }
           | param { $$ = node("param-list", 1, $1); }
param : type-specifier IDENTIFIER { $$ = node("param", 2, $1, $2); }
      | type-specifier IDENTIFIER ARRAY { $$ = node("param", 3, $1, $2, $3); }
compound-stmt : LBRACE local-declarations statement-list RBRACE { $$ = node("compound-stmt", 4, $1, $2, $3, $4); }
local-declarations : { $$ = node("local-declarations", 0); }
                   | local-declarations var-declaration { $$ = node("local-declarations", 2, $1, $2); }
statement-list : { $$ = node("statement-list", 0); }
               | statement-list statement { $$ = node("statement-list", 2, $1, $2); }
statement : expression-stmt { $$ = node("statement", 1, $1); }
          | compound-stmt { $$ = node("statement", 1, $1); }
          | selection-stmt { $$ = node("statement", 1, $1); }
          | iteration-stmt { $$ = node("statement", 1, $1); }
          | return-stmt { $$ = node("statement", 1, $1); }
expression-stmt : expression SEMICOLON { $$ = node("expression-stmt", 2, $1, $2); }
                | SEMICOLON { $$ = node("expression-stmt", 1, $1); }
selection-stmt : IF LPARENTHESE expression RPARENTHESE statement { $$ = node("selection-stmt", 5, $1, $2, $3, $4, $5); }
               | IF LPARENTHESE expression RPARENTHESE statement ELSE statement { $$ = node("selection-stmt", 7, $1, $2, $3, $4, $5, $6, $7); }
iteration-stmt : WHILE LPARENTHESE expression RPARENTHESE statement { $$ = node("iteration-stmt", 5, $1, $2, $3, $4, $5); }

return-stmt : RETURN SEMICOLON { $$ = node("return-stmt", 2, $1, $2); }
            | RETURN expression SEMICOLON { $$ = node("return-stmt", 3, $1, $2, $3); }
expression : var ASSIN expression { $$ = node("expression", 3, $1, $2, $3); }
           | simple-expression { $$ = node("expression", 1, $1); }
var : IDENTIFIER { $$ = node("var", 1, $1); }
    | IDENTIFIER LBRACKET expression RBRACKET { $$ = node("var", 4, $1, $2, $3, $4); }
simple-expression : additive-expression relop additive-expression { $$ = node("simple-expression", 3, $1, $2, $3); }
                  | additive-expression { $$ = node("simple-expression", 1, $1); }
relop : LTE { $$ = node("relop", 1, $1); }
      | LT { $$ = node("relop", 1, $1); }
      | GT { $$ = node("relop", 1, $1); }
      | GTE { $$ = node("relop", 1, $1); }
      | EQ { $$ = node("relop", 1, $1); }
      | NEQ { $$ = node("relop", 1, $1); }
additive-expression : additive-expression addop term { $$ = node("additive-expression", 3, $1, $2, $3); }
                    | term { $$ = node("additive-expression", 1, $1); }
addop : ADD { $$ = node("addop", 1, $1); }
      | SUB { $$ = node("addop", 1, $1); }
term : term mulop factor { $$ = node("term", 3, $1, $2, $3); }
     | factor { $$ = node("term", 1, $1); }
mulop : MUL { $$ = node("mulop", 1, $1); }
      | DIV { $$ = node("mulop", 1, $1); }
factor : LPARENTHESE expression RPARENTHESE { $$ = node("factor", 3, $1, $2, $3); }
       | var { $$ = node("factor", 1, $1); }
       | call { $$ = node("factor", 1, $1); }
       | integer { $$ = node("factor", 1, $1); }
       | float { $$ = node("factor", 1, $1); }
integer : INTEGER { $$ = node("integer", 1, $1); }
float : FLOATPOINT { $$ = node("float", 1, $1); }
call : IDENTIFIER LPARENTHESE args RPARENTHESE { $$ = node("call", 4, $1, $2, $3, $4); }
args : { $$ = node("args", 0); }
     | arg-list { $$ = node("args", 1, $1); }
arg-list : arg-list COMMA expression { $$ = node("arg-list", 3, $1, $2, $3); }
         | expression { $$ = node("arg-list", 1, $1); }

可以选择其中一个解释一番:

var-declaration : type-specifier IDENTIFIER SEMICOLON
                { $$ = node("var-declaration", 3, $1, $2, $3); }
                | type-specifier IDENTIFIER LBRACKET INTEGER RBRACKET SEMICOLON
                { $$ = node("var-declaration", 6, $1, $2, $3, $4, $5, $6); }
var-declaration : type-specifier IDENTIFIER SEMICOLON

表示一个简单变量声明,

type-specifier 是变量的类型(如 intfloat)。

IDENTIFIER 是变量的标识符(如 xy)。

SEMICOLON 是分号,表示声明结束。

var-declaration : type-specifier IDENTIFIER LBRACKET INTEGER RBRACKET SEMICOLON

表示一个数组变量声明,

type-specifier 是数组的类型(如 intfloat)。

IDENTIFIER 是数组的标识符(如 arrnums)。

LBRACKETRBRACKET 分别是左方括号和右方括号。

INTEGER 是数组的大小(如 1020)。

SEMICOLON 表示声明结束。

实验结果验证

1、执行命令make parser进行编译

image-20241124180542252

根据显示的错误,在ast.hpp文件中添加#include<string>.

再次编译:

image-20241124180603476

报错说明 yyin变量未声明。在syntax_analyzer.y 头部加上对 yyin 添加声明extern FILE *yyin;。再次编译:

image-20241124180622749

2、test验证

执行命令./tests/lab2/test_syntax.sh easy./tests/lab2/test_syntax.sh normal生成语法解析树

image-20241124180644208

出现 Permission denied 的错误是因为 test_syntax.sh 脚本文件缺乏可执行权限

运行ls -l ./tests/lab2/test_syntax.sh,检查 test_syntax.sh 的当前权限,发现无执行权限,将x权限赋予它:

image-20241124180709624

再次测试:

image-20241124180727386

image-20241124180739519

可以看到,能识别出源程序中错误的语法部分

3、用diff 命令进行验证

分别用diff ./tests/lab2/syntree_easy ./tests/lab2/syntree_easy_std

diff ./tests/lab2/syntree_normal ./tests/lab2/syntree_normal_std

将自己的生成结果和助教提供的syntree_easy_stdsyntree_normal_std进行比较。没有任何输出结果,说明结果正确。

image-20241124202604146

4、自测样例

  1. mytest1.cminus
int a;
int cmp(int a, int b)
{
	int c;
    c=3.1415926+2*3;
	a=(3.141526+2)*3;
	c = a/*
this is a comment */ * /**/ b;
	return c;
}

image-20241124205733000

生成的语法树为:

>--+ program
|  >--+ declaration-list
|  |  >--+ declaration-list
|  |  |  >--+ declaration
|  |  |  |  >--+ var-declaration
|  |  |  |  |  >--+ type-specifier
|  |  |  |  |  |  >--* int
|  |  |  |  |  >--* a
|  |  |  |  |  >--* ;
|  |  >--+ declaration
|  |  |  >--+ fun-declaration
|  |  |  |  >--+ type-specifier
|  |  |  |  |  >--* int
|  |  |  |  >--* cmp
|  |  |  |  >--* (
|  |  |  |  >--+ params
|  |  |  |  |  >--+ param-list
|  |  |  |  |  |  >--+ param-list
|  |  |  |  |  |  |  >--+ param
|  |  |  |  |  |  |  |  >--+ type-specifier
|  |  |  |  |  |  |  |  |  >--* int
|  |  |  |  |  |  |  |  >--* a
|  |  |  |  |  |  >--* ,
|  |  |  |  |  |  >--+ param
|  |  |  |  |  |  |  >--+ type-specifier
|  |  |  |  |  |  |  |  >--* int
|  |  |  |  |  |  |  >--* b
|  |  |  |  >--* )
|  |  |  |  >--+ compound-stmt
|  |  |  |  |  >--* {
|  |  |  |  |  >--+ local-declarations
|  |  |  |  |  |  >--+ local-declarations
|  |  |  |  |  |  |  >--* epsilon
|  |  |  |  |  |  >--+ var-declaration
|  |  |  |  |  |  |  >--+ type-specifier
|  |  |  |  |  |  |  |  >--* int
|  |  |  |  |  |  |  >--* c
|  |  |  |  |  |  |  >--* ;
|  |  |  |  |  >--+ statement-list
|  |  |  |  |  |  >--+ statement-list
|  |  |  |  |  |  |  >--+ statement-list
|  |  |  |  |  |  |  |  >--+ statement-list
|  |  |  |  |  |  |  |  |  >--+ statement-list
|  |  |  |  |  |  |  |  |  |  >--* epsilon
|  |  |  |  |  |  |  |  |  >--+ statement
|  |  |  |  |  |  |  |  |  |  >--+ expression-stmt
|  |  |  |  |  |  |  |  |  |  |  >--+ expression
|  |  |  |  |  |  |  |  |  |  |  |  >--+ var
|  |  |  |  |  |  |  |  |  |  |  |  |  >--* c
|  |  |  |  |  |  |  |  |  |  |  |  >--* =
|  |  |  |  |  |  |  |  |  |  |  |  >--+ expression
|  |  |  |  |  |  |  |  |  |  |  |  |  >--+ simple-expression
|  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ additive-expression
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ additive-expression
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ term
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ factor
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ float
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--* 3.1415926
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ addop
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--* +
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ term
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ term
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ factor
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ integer
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--* 2
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ mulop
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--* *
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ factor
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ integer
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--* 3
|  |  |  |  |  |  |  |  |  |  |  >--* ;
|  |  |  |  |  |  |  |  >--+ statement
|  |  |  |  |  |  |  |  |  >--+ expression-stmt
|  |  |  |  |  |  |  |  |  |  >--+ expression
|  |  |  |  |  |  |  |  |  |  |  >--+ var
|  |  |  |  |  |  |  |  |  |  |  |  >--* a
|  |  |  |  |  |  |  |  |  |  |  >--* =
|  |  |  |  |  |  |  |  |  |  |  >--+ expression
|  |  |  |  |  |  |  |  |  |  |  |  >--+ simple-expression
|  |  |  |  |  |  |  |  |  |  |  |  |  >--+ additive-expression
|  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ term
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ term
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ factor
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--* (
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ expression
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ simple-expression
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ additive-expression
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ additive-expression
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ term
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ factor
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ float
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--* 3.141526
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ addop
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--* +
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ term
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ factor
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ integer
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--* 2
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--* )
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ mulop
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--* *
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ factor
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ integer
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--* 3
|  |  |  |  |  |  |  |  |  |  >--* ;
|  |  |  |  |  |  |  >--+ statement
|  |  |  |  |  |  |  |  >--+ expression-stmt
|  |  |  |  |  |  |  |  |  >--+ expression
|  |  |  |  |  |  |  |  |  |  >--+ var
|  |  |  |  |  |  |  |  |  |  |  >--* c
|  |  |  |  |  |  |  |  |  |  >--* =
|  |  |  |  |  |  |  |  |  |  >--+ expression
|  |  |  |  |  |  |  |  |  |  |  >--+ simple-expression
|  |  |  |  |  |  |  |  |  |  |  |  >--+ additive-expression
|  |  |  |  |  |  |  |  |  |  |  |  |  >--+ term
|  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ term
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ factor
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ var
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--* a
|  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ mulop
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--* *
|  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ factor
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--+ var
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  >--* b
|  |  |  |  |  |  |  |  |  >--* ;
|  |  |  |  |  |  >--+ statement
|  |  |  |  |  |  |  >--+ return-stmt
|  |  |  |  |  |  |  |  >--* return
|  |  |  |  |  |  |  |  >--+ expression
|  |  |  |  |  |  |  |  |  >--+ simple-expression
|  |  |  |  |  |  |  |  |  |  >--+ additive-expression
|  |  |  |  |  |  |  |  |  |  |  >--+ term
|  |  |  |  |  |  |  |  |  |  |  |  >--+ factor
|  |  |  |  |  |  |  |  |  |  |  |  |  >--+ var
|  |  |  |  |  |  |  |  |  |  |  |  |  |  >--* c
|  |  |  |  |  |  |  |  >--* ;
|  |  |  |  |  >--* }
  1. mytest2.cminus

这组样例测试的是语法不支持的识别语句:

① 变量不支持初始化

int main(int a)
{
    int a=0;
    int b=1;
    return  function(a,b);
}

image-20241124212009208

②不支持的操作+=

int main(int a,int b)
{
    a+=b;
    return a;
}

image-20241124213259511

③不支持的变量类型

int main(int a)
{
    integer a;
    int b;
    return  function(a,b);
}

image-20241124212316427

还有很多可以测试的样例,这里就不一一列举了。

实验反馈

  • 在补充lexical_analyzer.l文件时,其实只要根据实例给出的example就能很好的编写代码,唯一需要注意的是lab1的other的token,对于空格,注释等,我们不需要为其建立语法树中的节点,程序遇到这些内容的操作是跳过,但也不能直接把这些删去,因为程序要先识别出这些内容才能选择跳过,所以只要把return删去就行。
  • 对于syntax_analyzer.y的补充,首先要做的是理解实验文档中给出的语法规则,代码的编写都是依赖于这些规则的。在处理解析规则时,最麻烦的是声明部分,把这一部分写好了,后面的节点就很简单了,节点的个数就是{ }前声明类型的个数,直接数就行,节点的编号按顺序就OK的。

标签:node,end,2022,pos,lab2,HNU,--+,declaration,expression
From: https://blog.csdn.net/2301_76658831/article/details/145081689

相关文章

  • Hnu-体能测试
    【问题描述】小张老师负责信息学院m(0<m<=100)个学生的k(1<k<=10)项体能测试,体能测试顺序按照编号来,学生测试完一项就在小张老师这里登记一次,现在小张老师手上有n(n<=m*k)个数据,需要根据学生登记的情况,判定出哪些学生还缺体能项目测试,缺的项目是哪一项,然后提醒这些学生尽快完成......
  • [NOISG2022 Qualification] Dragonfly Solution in O(d log d)
    [NOISG2022Qualification]DragonflySolutioninO(dlogd)提供一个使用线段树合并、栈、树状数组的严格单\(\log\)离线做法。题目大意:给你一棵树,每个点有权值和颜色,每次问你一个从\(1\)开始的路径,求权值不为\(0\)的节点的颜色种类数,并且把所有权值不为\(0\)的节点权......
  • 2022-2023 集训队互测 Round 6 - >.<
    不能包含某一条路径,这个东西看起来很像字符串啊!我们把这些路径插入到trie中,建立AC自动机,然后再把\(n\)个单点插进去。在建出来的AC自动机上跑最短路,钦定某些点不能被进入即可。但是因为字符集是\(\mathcalO(n)\)的,所以直接暴力连边复杂度无法接受。考虑连边的过程,是继......
  • (即插即用模块-Attention部分) 三十四、(2022) FACMA 频率感知跨通道注意力
    文章目录1、Frequency-AwareCross-ModalityAttention2、WeightedCross-ModalityFusionmodule3、代码实现paper:FCMNet:Frequency-awarecross-modalityattentionnetworksforRGB-DsalientobjectdetectionCode:https://github.com/XiaoJinNK/FCMNet1、......
  • Adobe Premiere Pro 2022 下载安装教程,亲测有效
    简介嗨咯,大家好,今天为大家带来的事AdobePremierePro2022下载安装教程,亲测有效。AdobePremierePro是一款领先的视频编辑软件,适用于电影、电视和网络内容创作。该软件结合强大的创意工具、Adobe应用程序和服务的深度集成以及AdobeSensei的AI技术,可帮助用户轻......
  • Matlab2019a安装C2000 Processors超详细过程
    ⭐1.环境搭建⭐链接1EmbeddedCoderSupportPackageforTexasInstrumentsC2000Processors-FileExchange-MATLABCentral......
  • vs2022遇到“停止生成”的问题
    关闭vs2012,提示必须停止生成,第一次遇见不知道怎么办,查了下,第一种解决了。在使用VisualStudio2022时,如果遇到“停止生成”的问题,可以尝试以下几种解决方案:取消生成:在VisualStudio的主界面,点击“生成”菜单,然后选择“取消”。使用快捷键 Ctrl+Break 来取消当前正......
  • Visual Studio 2022 上架腾讯云 AI 代码助手了
    近期在VisualStudio市场上上架了腾讯云AI代码助手。该插件可以在VisualStudio2022版本(含社区版,版本不低于17.6即可)使用智能辅助编码能力,助力VisualStudio的开发者提高效率。我们在该平台上支持技术对话、代码补全、单元测试生成、解释代码、修复代码等场景。如何安装......
  • 用 2025 年的工具,秒杀了 2022 年的题目。
    你好呀,我是歪歪。前几天打开知乎的时候,在付费咨询模块,我看到了一个差不多两年半前没有回答的技术问题。其实这个问题问的很清晰了,但是当时我拒绝了:虽然过去快两年半的时间,但是我记得还是比较清楚,当时拒绝的理由是如果让我来回答这个问题,我肯定是首选基于Redis来做。大家想......
  • E94 Tarjan边双缩点+树形DP P8867 [NOIP2022] 建造军营
    视频链接:E94Tarjan边双缩点+树形DPP8867[NOIP2022]建造军营_哔哩哔哩_bilibili  P8867[NOIP2022]建造军营-洛谷|计算机科学教育新生态//Tarjan边双缩点+树形DPO(n)#include<bits/stdc++.h>usingnamespacestd;intread(){intx=0,f=1;charc=getchar......