实验名:PL/0编译器的实现 一、实验目的 理解编译器的工作机制,掌握编译器的构造方法 掌握词法分析器的生成工具LEX的用法 掌握语法分析器的生成工具YACC的用法
二、实验内容简要概括 1.实验分为三个部分: 分别是词法分析、语法分析、语义分析和代码生成部分 三个部分对应PL/0编译程序的编译过程,使得在学习原理的过程中可以更好的体会到分析过程。
2.词法分析 编写PL/0语言识别程序,并借助flex工具生成一个PL/0语言的词法分析程序,对输入的PL/0语言的源程序进行扫描,识别出单词符号的类别,输出各种符号的信息。
参考LEX源程序的格式,自定义声明,辅助定义,识别规则和子程序。
3.语法分析 编写一个PL/0语言的语法分析程序,借助bison工具生成,使得程序可以对输入的PL/0源程序进行语法分析,并按语法归约过程输出归约时所用的语法规则。
4.语义分析 使用C语言编写PL/0的语义分析程序,要求将其转换成一个类pcode语言
三、实验环境: OS:Windows10 1607 Compiler:Cygwin64 Terminal,yacc,lex,bison, code:blocks:13.12
辅助:sublime text 3126, notepad++ 7.41 使用语言:C
四、实验设计说明 1.第一部分:词法分析
实验具体输入输出要求如下
输入:PL0源程序
输出:将单词符号分为下面五类,然后统计PL0源程序中各单词符号出现的次数
K类(关键字)I类(标识符)C类(常量)P类(算符及界符)O类(其他)
根据要求,再参考lex的语法格式,设计思路大致如下:
1)对五类关键词分别建立结构体,类似于Hash的链表法一样,一个关键词中对应一个数组,将每一个读入的,相同类型但是不同内容的符号插在对应类别的结构体后面。结构体设计如图。
2)辅助定义环节添加关键词信息与对应的语义动作insert
对读进来的PL/0文件进行分析,将结果输出到另外一个文件中
该部分的最大难度在于语义动作insert
本次实验对输入的字符串有长度限制,需要在分析语言的过程中给予警告,但同时也不能终止编译动作,因为如果每次编译报错,只能报一行错误,会使得编译器的分析效率十分低下。本次实验中insert动作对应的函数如下:
2.第二部分:语法分析 实验具体输入输出要求如下
输入:PL0源程序
输出:关于该程序的语法结构分析说明
参考bison的说明,大致明白这次实验是在词法分析的基础之上多加了对于语法规则的定义,其中定义的语句要符合C语言文法。
语法格式:左部(非终结符):右部(文法符号串);
非终结符名称通常用小写字母,终结符名称通常用大写字母。
右部为空表示左部的非终结符可以匹配空串。
左部相同的语法规则应尽量合并。
语义动作出现在规则的尾部时,bison在归约前执行它;语义动作出现在规则的中间时,bison在识别出它前面的若干文法符号后执行它。
实现思路参考词法分析实验,只是这次在对应的语义动作环节直接输出,而不需要insert到结构体中。
实验输出如下:
3.第三部分:语义分析和目标语言的生成
实验具体输入输出要求如下
输入:PL0源程序
输出:类Pcode代码
首先需要对类Pcode进行了解
目标代码类pcode是一种栈式机的汇编语言。
栈式机系统结构:没有累加器和寄存器,只有存储栈指针所有运算都在栈顶
指令格式fla
f 功能码
l 层次差 (标识符引用层减去定义层)
a 根据不同的指令有所区别
参考类Pcode的功能表和解释器结构,将程序分成三个部分:code.l,code.y和define.h。其中define.h定义了类Pcode的功能码代号,符号表和指令结构,code.l属于词法分析部分,code.y属于语法与语义分析部分。其中在语义动作说明部分,根据输入情况不同用$伪变量进行压栈出栈操作。
本次实验中已经提供了interpret函数,所以省略了C语言中对于功能码的具体情况说明,接下来的思路就是根据输入的程序,先检查词法有无错误—>构建符号表-->执行interpret()的内容à输出类Pcode结果
实验结果图在第五部分
五、以一个*PL0*的源程序作具体说明
原PL0程序如下:
var a, b, c;procedure gcd;procedure rec;beginif a <> 0 thenbegina := a - 1;end;end;beginif c < b thenbeginc := b;call rec;end;end;beginread(a);read(b);read(c);write(a);call gcd;write(a);end.复制代码
这个程序里面存在着过程嵌套,其实这一次实验中还实现了并列,但是在此就不赘述了。 实验输出结果如图
(注:在写报告的过程中才发现之前看错ppt说明的指令功能表,误将INT看成INI,但为了阅读的方便,所以此处不再做修改)