编译原理 语法分析程序

  • 第一部分、 学习经典的语法分析器
    • 一、实验目的
    • 二、实验任务
    • 三、实验内容
      • 1.选择一个编译器,掌握它的语法分析程序。
      • 2.阅读语法分析源程序。尤其要求对相关函数与重要变量的作用与功能进行稍微详细的描述。
      • 3.测试语法分析器。对TINY语言要求输出测试程序的字符形式的抽象语法树。(手工或编程)画出图形形式的抽象语法树。
  • 第二部分——实现一门语言的语法分析器(3学时)
    • 一、实验目的
    • 二、实验任务
    • 三、系统设计
      • 1.语言确定
      • 2.完成C-语言的BNF文法到EBNF文法的转换。
      • 3.为每一个将要写成递归下降函数的非终结符,定义其抽象语法子树的形式结构,然后定义C-语言的语法树的数据结构。
      • 4.仿照前面学习的语法分析器,编写选定语言的语法分析器。
      • 5.准备2~3个测试用例,测试并解释程序的运行结果。
    • 四、总结

工程链接:https://pan.baidu.com/s/1HS_b8xn34GFzh7g0CNfnsw
提取码:w0au(仅供参考!)
建议配合源码观看

第一部分、 学习经典的语法分析器

一、实验目的

学习已有编译器的经典语法分析源程序。

二、实验任务

阅读已有编译器的经典语法分析源程序,并测试语法分析器的输出。

三、实验内容

1.选择一个编译器,掌握它的语法分析程序。

编译器选择:TINY

2.阅读语法分析源程序。尤其要求对相关函数与重要变量的作用与功能进行稍微详细的描述。

TINY有两种基本的结构类型:
语句和表达式。语句共有5类:(if语句、repeat语句、assign语句、read语句和read语句),表达式共有3类(算符标的是、常量表达式和标识符表达式)。因此,语法树节点首先安装它是语句还是表达式来进行分类,接着根据语句或表达式的种类进行再次分类。

树节点最大可有3个孩子的结构(仅在带有else部分的if语句才用到)。语句通过同属域而不是子域来排序,即由父亲到他的孩子的唯一物理连接是到最左孩子的。孩子则在一个标准连接表中自左向右连接到一起,这种连接称作同属连接,用于区别父子连接。
五种语句对应的语法树:

树结构设计流程:
①创建一个树节点的结构体
②使用递归下降算法,将每一个文法产生式转变成递归函数中的一个子句
③用前看符号指导产生式规则的选择
④创建一个match函数,对前看符号进行匹配,如果不匹配,调用syntaxError函数对错误语法进行报错。
⑤创键一个printTree函数,将一个语法树打印出来。

3.测试语法分析器。对TINY语言要求输出测试程序的字符形式的抽象语法树。(手工或编程)画出图形形式的抽象语法树。

3.1.TINY语言测试用例:
sample.tny
3.2.TINY语言的输出测试程序的字符形式抽象语法树:

3.3.手绘抽象语法树的图形形式:

第二部分——实现一门语言的语法分析器(3学时)

一、实验目的

通过本次实验,加深对语法分析的理解,学会编制语法分析器。

二、实验任务

用C或C++语言编写一门语言的语法分析器。

三、系统设计

1.语言确定

C-语言,其定义在《编译原理及实践》附录A中。

2.完成C-语言的BNF文法到EBNF文法的转换。

通过这一转换,消除左递归,提取左公因子,将文法改写为LL(1)文法,以适用于自顶向下的语法分析。规划需要将哪些非终结符写成递归下降函数。
因为C-语言的BNF文法存在左递归,因此,这里首先手工消除左递归与公因子转化为EBNF;
语法分析采用递归下降的算法,转换后的EBNF文法规则如下:({ }表示可有可无)

program → declaration-list
declaration-list → declaration {  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 }

3.为每一个将要写成递归下降函数的非终结符,定义其抽象语法子树的形式结构,然后定义C-语言的语法树的数据结构。

3.1.因为非终结符较多,所以这里简单的画出几个语句、表达式、声明的语法子树的形式结构:

3.2.c-语言的语法树数据结构:
树结点定义:

包含子结点、兄弟节点、行号、序列类型(声明、定义、表达式中的一种)、属性、形式类型信息;
序列类型:定义decl、声明def、表达式exp三种;

树结点的构建:
以声明结点创建为例:

开辟空间、给子节点和兄弟节点赋初值NULL、给结点结点类型赋初值Declk表示声明、行号赋初值;

4.仿照前面学习的语法分析器,编写选定语言的语法分析器。

4.1.选择使用递归下降的任意方法实现;
注:本实验需要用到实验1当中的词法分析器,因此,采用增量编程的方法,直接在原来的实验1的工程基础上,加上语法分析的相关文件myparse.h、myparse.c、util.c、util.h;
文件结构如下:

语法分析添加文件:
parser.h:与语法分析相关的声明
parser.cpp:语法分析阶段的实现
util.h:语法树的结点生成与打印相关函数的声明
util.c:语法树的结点生成与打印相关函数的实现

词法分析部分与实验一完全相同,语法分析程序需要用到词法分析的getToken函数来进行Token词素的获取。

语法分析的过程主要是:在语法分析之的过程中通过词法分析程序获得Token,然后通过递归向下分析法根据C-语言的文法进行语法分析,并生成语法树,最后打印语法树。程序构建树大致流程:

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

4.2.节点类型:
在程序处理过程中,主要对语法树定义了19种节点,节点性质与相对关系如下:

节点类型 描述 子节点组成
IntK 变量或返回值类型(int)
VoidK 变量或返回值类型(void)
IdK 标示符(id)
ConstK 数值
Var_DeclK 变量声明 变量类型+变量名
Var_DeclK 数组声明 数组名+数组大小
FunK 函数声明 返回类型+函数名+参数列表+函数体
ParamsK FunK的参数列表 参数(如果有多个参数,则之间为兄弟节点)
ParamK FunK的参数 参数类型+参数名
CompK 复合语句体 变量声明列表+语句列表
Selection_StmtK if 条件表达式+IF体+[ELSE体]
Iteration_StmtK while 条件表达式+循环体
Return_StmtK return [表达式]
AssignK 赋值 被赋值变量+赋值变量
OpK 运算 运算符左值+运算符右值
Arry_ElemK 数组元素 数组名+下标
CallK 函数调用 函数名+参数列表
ArgsK CallK的参数列表 [表达式](如果有多个表达式,则之间为兄弟节点)
UnkownK 未知节点

4.3.成员函数和变量的作用和含义:

Token getToken():获取保存在scanner中TokenList数组中的Token,每次获取完之后数组下标指向下一个 void
printSpace(int n):打印n个空格 void syntaxError(string
s):报错的函数,报告出错位置(行号)、出错位置附近的Token void match(TokenType
ex):与目标Token类型ex匹配,如果匹配成功则获取下一个Token(为currentToken赋值),否则报错 void
printTree(TreeNode * t):打印生成的语法树 TreeNode * newNode(Nodekind
k):根据节点类型新建节点

以下为递归向下分析文法过程中各阶段的分析函数:

    TreeNode * declaration_list(void);TreeNode * declaration(void);TreeNode * params(void);TreeNode * param_list(TreeNode * k);TreeNode * param(TreeNode * k);TreeNode * compound_stmt(void);TreeNode * local_declaration(void);TreeNode * statement_list(void);TreeNode * statement(void);  TreeNode * expression_stmt(void);TreeNode * selection_stmt(void);TreeNode * iteration_stmt(void);TreeNode * return_stmt(void);TreeNode * expression(void);TreeNode * var(void);TreeNode * simple_expression(TreeNode * k);TreeNode * additive_expression(TreeNode * k);TreeNode * term(TreeNode * k);TreeNode * factor(TreeNode * k);   TreeNode * call(TreeNode * k);TreeNode * args(void);

以下表格列出了根据上文中的C-文法使用递归向下分析方法分析程序的过程,待分析文法中列出的是将要分析的若干文法,分析函数是程序针对该文法编写的分析函数,用以分析该文法,分析是对该文法的分析过程以及分析函数的编写思想,代码是分析函数用C语言的实现;
4.4.各文法分析过程如下(具体代码实现见源码):

待分析文法 program→declaration-list
分析函数 TreeNode * parse(void)
分析 说明C-语言编写的程序由一组声名序列组成。parse(void)函数使用递归向下分析方法直接调用declaration_list()函数,并返回树节点
待分析文法 declaration_list → declaration{ declaration }
分析函数 TreeNode * declaration_list(void)
分析 说明C-语言编写的程序由一组声名序列组成。declaration_list(void)函数使用递归向下分析方法直接调用declaration()函数,并返回树节点

待分析文法 param_list→param{, param}
分析函数 TreeNode * param_list(TreeNode * k)
分析 说明参数列表由param序列组成,并由逗号隔开。param_list(TreeNode * k)函数使用递归向下分析方法直接调用param(TreeNode * k)函数,并返回树节点

待分析文法 param→ type-specifier ID{[ ]}
分析函数 TreeNode * param(TreeNode * k)
分析 说明参数由int或void、标示符组成,最后可能有中括号表示数组。param(TreeNode * k)函数根据探测先行Token是int还是void而新建IntK或VoidK节点作为其第一个子节点,然后新建IdK节点作为其第二个子节点,最后探测Follow集合,是否是中括号,而确定是否再新建第三个子节点表示数组类型

待分析文法 compound-stmt→{ local-declaration statement-list}
分析函数 TreeNode * compound_stmt(void)
分析 说明复合语句由用花括号围起来的一组声明和语句组成。compound_stmt(void) 函数使用递归向下分析方法直接调用local_declaration()函数和statement_list()函数,并根据返回的树节点作为其第一个子节点和第二个子节点
待分析文法 local-declarations → empty {var- declaration}
分析函数 TreeNode * local_declaration(void)
分析 说明局部变量声明要么是空,要么由许多变量声明序列组成。local_declaration(void)函数通过判断先行的Token是否是int或void便可知道局部变量声明是否为空,如果不为空,则新建一个变量定义节点(Var_DeclK)

待分析文法 statement-list→{statement}
分析函数 TreeNode * statement_list(void)
分析 说明由语句列表由0到多个statement组成。statement_list(void)函数通过判断先行Token类型是否为statement的First集合确定后面是否是一个statement,如果是,则使用递归向下分析方法直接调用statement()函数

待分析文法 expression-stmt→ [expression];
分析函数 TreeNode * expression_stmt(void)
分析 说明表达式语句有一个可选的且后面跟着分号的表达式。expression_stmt(void)函数通过判断先行Token类型是否为分号,如果不是则直接调用函数expression()

待分析文法 iteration-stmt→while (expression)statement
分析函数 TreeNode * iteration_stmt(void)
分析 iteration_stmt(void)函数直接调用expression()函数和statement()函数分别得到其第一个和第二个子节点
待分析文法 return-stmt→return [expression];
分析函数 TreeNode * return_stmt(void)
分析 return_stmt(void)函数通过判断先行Token类型是否为分号,如果不是则直接调用函数expression()得到其子节点


5.准备2~3个测试用例,测试并解释程序的运行结果。

5.1.测试样例1(正例):
代码:

测试结果:

手工画出语法树为(因为篇幅原因只画出了第一个函数,其实还有一个函数的树未画):

5.2.测试样例2(反例):
代码:

测试结果:

可以看到这里遇到!会报出错误;

四、总结

对于编译原理的这次课程设计,自己经历了从刚开始的不懂明白任务的要求和内容理论知识的了解开始着手写代码完成基本功能根据DFA及自顶向下等理论修改完善代码等这些过程。
在写语法分析的时候,已经对编译器的语法分析的内容有了一定的了解,所以直接进行了理论的学习。首先自己对递归向下分析法进行了学习,将书上的几个递归向下分析的伪代码看过之后,自己对递归向下的分析方法的原理有了初步的认识,大概知道了根据文法怎么分析,但是对于如何编写代码却还是难以下手,于是就对照TINY语言的文法看了几遍书后面的TINY语言的递归向下分析的语法分析程序,这样就基本知道了C-语言的语法分析程序怎么写。由于C-语言给出的文法有左递归存在,于是自己将存在左递归的文法改写成EBNF的形式,并据此进行代码编写。由于在编写代码的过程中需要确定分析是否正确或选择多个文法中的某一个文法进行分析,有时必须探测需要的或下一个Token的类型,在这种情况下需要求First集合,在推导中若存在empty,又需要求Follow集合,所以这样又需要我了解First集合和Follow集合,自己在程序中也根据求出的First集合和Follow集合进行判断,以确定程序的走向。在编写过程中,还有一类问题,就是存在公共左因子,如文法expression→ var = expression | simple-expression,左因子为ID,在分析过程中,由于已经取出了一个ID的Token,且生成了一个IdK的节点,但是在当前状态无法确定是哪一个推导,然而IdK节点已经生成,又无法回退,并且是使用自顶向下的分析方法,已经生成的IdK在程序上方无法使用,自己通过查阅资料等途径的学习确定了在这种情形下的处理方式:将已经生成的IdK节点传到下方的处理程序,所以TreeNode * simple_expression(TreeNode * k)、TreeNode * additive_expression(TreeNode * k)等函数都被设计成有节点类型参数的函数,目的就是将已经生成的节点传到下面的分析函数中去。
通过这次的编译原理课程的学习和实践,自己获益良多。首先最基本的成果是完成了课程设计的任务,实现了编译器的词法分析和语法分析阶段的功能,词法分析主要能过滤注释、分析出语法分析阶段需要的Token并满足语法阶段的所有要求,能够判别词法分析阶段是否出错和出错类型和位置。语法分析主要能根据递归向下的分析思想和C-文法对词法分析获取的Token进行语法分析,能够构造出语法树,能够判别语法分析过程中是否出错以及出错位置和错误类型。
总之,通过这次课程实验,自己获益匪浅,达到了自己预期的效果。

编译原理 语法分析程序相关推荐

  1. 编译原理——语法分析程序的设计

    实验目的 通过设计.编制.调试一个典型的语法分析程序,实现对词法分析程序所提供的单词序列进行语法检查和结构分析,进一步掌握常用的语法分析中预测分析方法. 实验内容 设计一个文法的预测分析程序,判断特定 ...

  2. 编译原理语法分析之LR分析

    要求: (1)根据给定文法,先对文法进行解析,构造识别活前缀的DFA并输出: (2)根据DFA构造LR分析表并输出: (3)分析给定表达式是否是该文法识别的正确的算术表达式(要求输出归约过程) (4) ...

  3. 编译原理:语法分析实验(LR分析法)

    语法分析实验 一.实验目的 根据LR分析法的原理,对指定文法构造识别活前缀的DFA,做出相应的LR分析表,并编程实现相应的语法分析程序.或根据预测分析法的原理,对指定文法构造预测分析表,并编程实现相应 ...

  4. 编译原理 --- 语法分析概念,自上而下分析面临的问题以及如何消除左递归问题

    第一部分 --- 语法分析基本概念 1.上面这个箭头 --> 符号表示的意思是P被 α 定义 A是一个非终止符,γ是一个和α,β属于同一个集合的元素 1.一个双箭头符号表示的是直接推出,而一个双 ...

  5. 编译原理——语法分析(递归下降)

    1 实验目的 理解和掌握算术表达式的文法构造,熟悉常用的语法分析方法. 2 实验内容 根据Sample语言的语法规则判定输入的一串单词能否构成一个正确的算术表达式. 3 实验方案 3.1方案描述   ...

  6. 编译原理扫描程序,判断当前输入的程序中程序调用的函数是那个函数原型

    l任务描述 –分析C语言源程序的片段,判断程序末尾是否为一个函数调用语句.如果是函数调用,则输出被调用函数的原型,并判断程序的末尾正在输出第几个参数. l输入 –int foo(int a, unsi ...

  7. 编译原理语法分析_斯坦福大学《编译原理》学习记录 - 第二章:语法分析器

    05-01: Introduction to Parsing 语法分析 parser的作用: 05-02: Context Free Grammars 上下文无关文法 CFG(上下文无关文法)回答了一 ...

  8. 编译原理----词法分析程序----python语言版

    对python的应用还是不熟练,很多实用的方法没掌握,下面的程序本来是用C写的,为了练习一下python,又用python改写的,很粗糙,有bug,不过能运行出结果,嘿嘿,以后学好了python再来优 ...

  9. 编译原理 语法分析树和二义性文法

    语法分析树是用来描述语法中句子结构的一种树,它能够动态表示一个句子推导的过程. 我们来看一个例子:由左边的文法规则可知,这是一个实现加法减法和乘法的算术表达式.从开始符号E开始,逐步推导,E => ...

  10. 编译原理——语法分析(1)文法的形式定义与四种文法类型的区分

    文法的形式定义 1.处理文法的语法分析器大体上可以分为三种类型:通用的,自顶向下的和自顶向上的. 2.文法:一种用于描述程序设计语言语法的表示方法--"上下文无关文法",简称&qu ...

最新文章

  1. try-catch-finally中的4个巨坑,老程序员也搞不定!
  2. 【新手教程】如何向App Store提交应用
  3. js一些平时会用到的
  4. web.xml:url-pattern
  5. 在树莓派2上安装 Windows 10
  6. 【软件】WPS VBA 7.0.1590(百度云免费下载链接)
  7. PS打不开webp格式图片的解决方法
  8. iOS——directory not found for option 链接错误
  9. HTML初学者--列表的简单制作
  10. PPP中常用定位和误差模型
  11. 抖音小程序实践四:实现小程序分享
  12. 关于判断力 - 正视事实是基础
  13. 用NavicatPremium导出数据
  14. 西门子数据采集西门子840DSL数据采集西门子840D监控siemens828D监控
  15. Java训练01“ 根据身高体重计算BMI指数”
  16. 【龙芯1B】:DS18B20测温例程
  17. 【分享】邮政校招社招笔试题经验分享和试题练习
  18. 苹果系统安装python环境_mac下python相关环境搭建(python开发必经之路之mac快速上手)...
  19. 旧笔记本电脑自制nas_将旧笔记本电脑变成Windows 8平板电脑
  20. Unity-TA 成长之路(三)URP-Universal Render Pipeline

热门文章

  1. Windows新建文本文档的快捷键设置
  2. Pspice for TI取消默认打开方式
  3. 常用quartz表达式
  4. HTML中怎么从图片里取色,图片取色配色法——从电影画面中取色
  5. 全国计算机二级ps考什么,计算机二级ps考试内容有哪些
  6. 遗传算法matlab_当结构设计遇到遗传算法应用ANSYS和MATLAB联合优化设计探索(二)...
  7. 计算机网络设计——企业网络规划与搭建
  8. ThinkPHP内核全行业小程序运营管理系统源码 自由DIY布局 一键生成小程序
  9. WPE详细教程四 独立外挂制作(1)
  10. 软件工程案例-仓库管理系统简单版