编译原理课程作业-Cminus语言的词法及语法分析器实现

Cminus Compiler

编译原理课程作业-Cminus语言的词法及语法分析器实现

设计思想

词法分析

使用确定有限态自动机(DFA)实现与Lex自动分析两种方式实现。

语法分析

使用递归下降方式与Yacc自动分析两种方式实现。

程序部分代码使用STL简化设计,对于重复部分使用自定义结构体或者全局函数进行重构。

设计思路

手工方式

词法分析:

首先通过分析词法规则,使用手工的方式构造DFA,自定义结构体记录每个状态节点的状态,节点的分为接受与非接受多个状态,同时接受态又分为文件结束、保留字,ID等多个状态,使用的DFA如下:

在这里插入图片描述

DFA示意图

对于每一次Token的获取,从源文件的当前字符开始处理,从输入缓冲区中读出字符,在DFA中进行转移,直到转移失败,判断当前节点是否是可接受状态,若是,将行号,节点状态,符号串转入Token转换程序,得到具体的符号串类型(数字、保留字、ID等)后返回,否则转到错误处理。然后回退转移失败的字符到缓冲区,将DFA设置为初始状态,准备下次匹配新的符号串。

对于评论,分析结束后并不直接返回,重新将DFA设置为初始状态,匹配新的符号串。

重复上述过程直至文件结束,即可得到所有的符号。

语法分析:

语法分析采用递归下降的算法,转换后的EBNF文法规则如下:

program → declaration-list

declaration-list → declaration-list {declaration}

declaration → var-declaration|fun-declaration

var-declaration → type-specifier ID | type-specifier ID[NUM];

type-specifier → int|void

fun-declaration → type-specifier ID(params)|compound-stmt

params → params-list|void

param-list → param{,param}

param → type-specifier ID{[]}

compound-stmt → {local-declarations statement-list}

local-declarations → empty{var-declaration}

statement-list → {statement}

statement → expression-stmt|compound-stmt|selection-stmt|

iteration-stmt|return-stmt

expression-stmt → [expression];

selection-stmt → if(expression) statement [else statement]

iteration-stmt → while(expression) statement

return-stmt → return[expression];

expression → var=expression|simple-expression

var → ID|ID[expression]

simple-expression → additive-expression {relop additive-expression}

relop → <=|<|>|>=|==|!=

additive-expression → term{addop term}

addop → +|-

term → factor{mulop factor}

mulop → *|/

factor → (expression)|var|call|NUM

call → ID(args)

args → arg-list|empty

arg-list → expression{, expression}

递归下降构建语法树时,对于每条产生式设计函数,调用词法分析的函数,返回当前Token并准备下次读取。 分解产生式进行Match判断下一个产生式的入口,在Match的同时函数会判断当前Token 的合法性同时读取下一个Token。对于左因子与间接递归的情况,在本条产生式函数内进行多次Match,直至匹配到出现差异的地方进行递归函数的调用,这个过程中,根据产生式构建带有节点性质的语法树。由于手动消除了间接递归,部分情况需要将父节点的属性向下传递,帮助确定子节点的性质。节点性质与相对关系如下:

节点性质详细说明
IntkInt值
VoidK空值
IdK标识符
ConstK常量
Var_DeclK变量声明(数组变量,普通变量)
FuncK函数
ParamsK函数参数列表
ParamK函数参数
CompK复合语句
Selection_StmtKIf
Iteration_StmtKWhile
Return_StmtKReturn
AssignK赋值语句
OpK运算符
Arry_ElemK数组
CallK回调
ArgsK回调函数的参数列表

自动分析

词法分析

使用Lex自动分析工具进行词法的分析。使用Lex支持的正则表达实现保留字、数字、ID等Token的正则表达式。当 Lex 接收到文件或文本形式的输入时,它试图将文本与常规表达式进行匹配。 它一次读入一个输入字符,直到找到一个匹配的模式。 如果能够找到一个匹配的模式,Lex 就执行相关的动作(包括返回一个标记)。 另一方面,如果没有可以匹配的常规表达式,将会停止进一步的处理,Lex 将显示一个错误消息。

对于换行空格等返回的Token值直接设置为空跳过这些无效信息。

使用自定义函数匹配直至的所有字符以过滤评论。

在Lex文件的C语言区域编写代码,结合Lex自身的yylex()函数,将获取的Token存入全局变量中,并将Token值附加行号等信息返回,供打印词法分析情况及判断合法性使用。

语法分析

使用Yacc进行语法的自动分析。在语法分析过程中,首先将定义的Token类型传递至Lex进行合法Token的获取,Lex分析过后会形成相应的Token值存入全局变量,部分过程将调用这个全局变量逐步更新树的结构。

根据C-的规则在Yacc的规则区域声明所有的合法产生式,Yacc会帮助我们自动完成规约。为了完成语法树的建立,需要在每条产生式的规约发生时定义动作进行树的更新:当遇到终结符时,将Token记录于树节点中,当遇到非终结符时,利用Yacc在规约过程中保存的状态栈,将子指针指向栈中的非终结符($1,$2等代表此位置保存的状态),并附加一些节点性质(如参数列表,定义变量)的信息,直至完成整个分析。

在Yacc的c语言区域重载yyerror函数,使得语法分析出错时输出当前的Token与相应的位置。

为了使得Yacc定义的合法Token能被Lex接收,同时Lex分析得到的Token能被Yacc获取,并且需要保存数据用于词法的扫描与语法树的打印,在全局变量中定义树的结构,树的操作,节点的类型以及词法与语法的检查与转换,声明函数,在词法分析与语法分析中别实现获取Token、打印词法与建立语法树,打印语法树的实例。

程序流程图

手动分析

**[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y64DTcsw-1602664522113)(README.assets/clip_image004.jpg)]**

词法分析过程

自动分析

**[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cVGbX7Us-1602664522116)(README.assets/clip_image006.jpg)]**

自动分析流程图

测试结果

正例测试

输入
int gcd (int u, int v)

{

if (v == 0)

 return u ;

else

 return gcd(v,u-u/v*v);

/* u-u/v*v == u mod v */

}

void main(void)

{ 

int x;

int y;

x = input();

y = input();

 output(gcd(x,y));

}
输出
  • 词法分析手动

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GcYYR6nL-1602664522128)(README.assets/clip_image010.jpg)]

  • 词法分析自动

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tGnZzoGw-1602664522131)(README.assets/clip_image012.jpg)]

  • 语法分析手动

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Eo4R8dGX-1602664522135)(README.assets/clip_image014.jpg)]

  • 语法分析自动

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1KQCJIS1-1602664522139)(README.assets/clip_image016.jpg)]

反例测试

输入
int gcd (int u,)

{

if (v #= 0)

 return u ;

else

 return gcd(v,u-u/v*v);

/* u-u/v*v == u mod v */

}

void main(void)

{ 

int x;

int y;

x = input();

y = input();

 output(gcd(x,y));

}
输出
  • 词法分析手动

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JQhfrPkX-1602664522141)(README.assets/clip_image018.jpg)]

  • 词法分析自动

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ujETzUvO-1602664522143)(README.assets/clip_image020.jpg)]

  • 语法分析手动

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gKGkw174-1602664522148)(README.assets/clip_image022.jpg)]

  • 语法分析自动

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lYmiqedc-1602664522150)(README.assets/clip_image024.jpg)]

代码地址

  • 6
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
C-Minus 的词法规则 (1)关键字: if else int return void while (2)专用符号: + - * / < >= == ~= = ; , ( ) [ ] { } /* */ (3)其他标记为 ID 和 NUM ,通过下列正则表达式定义: ID = letter letter* NUM = digit digit* letter = a|..|z|A|..|Z digit = 0|..|9 (4)空格由空白、换行符、制表符组成。 (5)注释由 /*...*/ 围起来,不能嵌套。 C-Minus 的语法规则 C-Minus 的 BNF 语法如下: 1. program -> declaration_list 2. declaration_list -> declaration_list declaration | declaration 3. declaration -> var_declaration | fun_declaration 4. var_declaration -> type_specifier ID; | type_specifier ID [ NUM ]; 5. type_specifier -> int | void 6. fun_declaration -> type_specifier ID ( params ) compound_stmt 7. params -> param_list | void 8. param_list -> param_list , param | param 9. param -> type_specifier ID | type_specifier ID [ ] 10. compound_stmt -> { local_declarations statement_list } 11. local_declarations -> local_declarations var_declaration | empty 12. statement_list -> statement_list statement | empty 13. statement -> expression_stmt | compound_stmt | selection_stmt | iteration_stmt | return_stmt 14. expression_stmt -> expression ; | ; 15. selection_stmt -> if ( expression ) statement | if ( expression ) statement else statement 16. iteration_stmt -> while ( expression ) statement 17. return_stmt -> return | return expression 18. expression -> var = expression | simple_expression 19. var -> ID | ID [ expression ] 20. simple_expression -> additive_expression relop additive_expression | additive_expression 21. relop -> <= | | >= | == | ~= 22. additive_expression -> additive_expression addop term | term 23. addop -> + | - 24. term -> term mulop factor | factor 25. mulop -> * | / 26. factor -> ( expression ) | var | call | NUM 27. call -> ID ( args ) 28. args -> arg_list | empty 29. arg8list -> arg_list , expression | expression 对以上每条文法规则,给出了相关语义的简短解释。 1. program -> declaration_list 2. declaration_list -> declaration_list declaration | declaration 3. dec
1. 课程设计目标 实验建立C-编译器。只含有扫描程序(scanner)和语法分析(parser)部分。 2. 分析与设计 C-编译器设计的整体框架,本实验实现扫描处理和语法分析程序(图中粗黑部分)。 2.1 、扫描程序scanner部分 2.1.1系统设计思想 设计思想:根据DFA图用switch-case结构实现状态转换。 惯用词法: ① 语言的关键字:else if int return void while ② 专用符号:+ - * / < >= == != = ; , ( ) [ ] { } /* */ ③ 其他标记是ID和NUM,通过下列正则表达式定义: ID = letter letter* NUM = digit digit* letter = a|..|z|A|..|Z digit = 0|..|9 大写和小写字母是有区别的 ④ 空格由空白、换行符和制表符组成。空格通常被忽略,除了它必须分开ID、NUM关键字。 ⑤ 注释用通常的C语言符号/ * . . . * /围起来。注释可以放在任何空白出现的位置(即注释不能放在标记内)上,且可以超过一行。注释不能嵌套 说明:当输入的字符使DFA到达接受状态的时候,则可以确定一个单词了。初始状态设置为START,当需要得到下一个token时,取得次token的第一个字符,并且按照DFA与对此字符的类型分析,转换状态。重复此步骤,直到DONE为止,输出token类型。当字符为“/”时,状态转换为SLAH再判断下一个字符,如果为“*”则继续转到INCOMMENT,最后以“*”时转到ENDCOMMENT状态,表明是注释,如果其他的则是字符停滞于当前字符,并且输出“/”。 2.1.2程序流程图
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值