大三上学期学的《编译原理》,当时学的也是迷迷糊糊的,主要是概念太多太抽象了,很多原理都听不懂。看了慕课上哈工大陈鄞老师的《编译原理》后受益匪浅,从中学到了很多。
下面是我们学校的编译原理的实验,只做了前三个:词法分析、LL1分析、LR1分析。LR1分析因为那段时间考试周临近,忙不过来,就只写了个总控程序,没有写自动生成分析表。(其实后两个实验的总控程序只要根据老师给的流程图就能写出来,难点在于如何自动生成分析表)。
PS:程序只是针对老师布置的实验来做的,实验用的文法例子运行结果正常,但是用其他的文法测试已经看出来有错误了,所以我就放在百度云上免费下载,仅供参考。
另外,大二下初学C#,用的还是以前C++面向过程的思想,所以会有点乱。。。
一、实验目的和要求
通过本实验的编程实践,使学生了解词法分析的任务,掌握词法分析程序设计的原理和构造方法,使学生对编译的基本概念、原理和方法有完整的和清楚的理解,并能正确地、熟练地运用。
1、编程时注意编程风格:空行的使用、注释的使用、缩进的使用等。
2、将标识符填写的相应符号表须提供给编译程序的以后各阶段使用。
3、根据测试数据进行测试。测试实例应包括以下三个部分:
全部合法的输入。
各种组合的非法输入。
由记号组成的句子。
4、词法分析程序设计要求输出形式:
例:输入VC++语言的实例程序:
If i=0 then n++;
a﹤= 3b %);
输出形式为:
单词 | 二元序列 (单词种别,单词属性) |
类 型 | 位置(行,列) |
for | (1,for) | 关键字 | (1,1) |
i | (6,i) | 标识符 | (1,2) |
= | (4,=) | 关系运算符 | (1,3) |
0 | (5,0) | 常数 | (1,4) |
then | (1,then) | 关键字 | (1,5) |
n | (6,n) | 标识符 | (1,6) |
++ | Error | Error | (1,7) |
; | (2,;) | 分界符 | (1,8) |
a | (6,a) | 标识符 | (2,1) |
<= | (4,<=) | 关系运算符 | (2,2) |
3b | Error | Error | (2,3) |
% | Error | Error | (2,4) |
) | (2,)) | 分界符 | (2,5) |
; | (2,;) | 分界符 | (2,6) |
二、实验原理
(1)实验数据结构说明
///
/// 枚举各种符号的类型
///
public enum Type
{
Error,//错误类型
KeyWord,//关键字
Delimiter,//分界符
ArithmeticOperator,//算术运算符
RelationalOperator,//关系运算符
Constant,//常数
Identifier,//标识符
}
//存储单词符号的位置
public struct Position
{
public int x { get; set; }
public int y { get; set; }
}
//识别出的单词符号
public class Tokens
{
public string word { get; set; }//单词
public Position position;//符号所在的位置
public Type type { get; set; }//识别出的符号的类型
//public int pointer;//指针
}
/**********************************全局字段********************************/
ArrayList tokenList = new ArrayList();//该列表存放已识别出的符号
string[] keyWordsList ={"bool","break","case","char","continue","do",
"double","else","float","for","goto","if","int","long","return",
"short","signed","sizeof","switch","typedef","unsigned",
"void","while","end","printf","scanf","then","default"};
string[] delimiterList = { "#","(", ")", "[", "]", "\"", ";", ":", "{", "}", "\\", ",","." ,"'"};
string[] relationalOperatorList ={"=","<",">","<>","<=",">=","==","!="};
string[] arithmeticOperatorList = { "+", "-", "*", "/","%"};
char[] inStream;//存放待分析的文本
char ch;//每次读取一个字符存到全局变量
int index = 0;//输入字串的下标指示器
static int r = 1, c = 1;//记录当前读取的字符的行列信息
/***************************************************************************/
(2)实验算法描述
主程序每次读入一个字符,判断该字符的类型,然后调用对应的子程序继续读入字符进行处理。
如果是空白字符,调用GetBC()继续读取直到不为空白字符。
如果是字母或下划线,调用letter()子程序,拼写出该单词后,查找关键字表,判断其类型是关键字还是标识符,最后将其插入到符号表中。
如果是数字,调用digit()子程序,拼写出该符号串,若串中包含字母或一个以上的小数点,则出错,否则识别出一个常数。
运算符和分界符的识别同理。先识别出第一个字符,然后读取下一个字符,接着查表,判断是否为双字符运算符(>=,<>等等),若是,把它插入到符号表,若不是,输入指针回退一个字符,把它插入到符号表。
(3)算法流程图
(4) 运行结果
一、实验目的和要求
通过完成预测分析法的语法分析程序,了解预测分析法和递归子程序法的区 别和联系。使学生了解语法分析的功能,掌握语法分析程序设计的原理和构造方 法,训练学生掌握开发应用程序的基本方法。有利于提高学生的专业素质,为培养适应社会多方面需要的能力。
1、编程时注意编程风格:空行的使用、注释的使用、缩进的使用等。
2、如果遇到错误的表达式,应输出错误提示信息。
3、对下列文法,用 LL(1)分析法对任意输入的符号串进行分析:
输出的格式如下:
二、实验原理
(1)实验数据结构说明
List Vt = new List();//终结符集合
List Vn = new List ();//非终结符集合
List production = new List();//产生式集合
string[][] table;//预测分析表
Stack symbolStack = new Stack();//分析栈
char[] inString;//输入符号串
int index;//当前输入符号的指针
public class Log
{
public string strStack; //分析栈中的符号串
public string strRemain; //剩余输入串
public string strProduction; //所用产生式
public string strAction; //动作
}
List log = new List();//记录分析过程
//利用HashSet集合中元素的互异性可以保证first集元素不会重复
//这是一个数组,每个数组单元里是一个哈希集合,哈希集合里存的是char型元素
List> firstSets = new List>();//first集
List> followSets =new List>();//follow集
(2)实验算法描述
表驱动的预测分析法:
最初,语法分析器的格局如下:
输入缓冲区中是w#,G的开始符号位于栈顶,其下面是#。下面的程序使用预测分析表M生成了处理这个输入的预测分析过程。
设置ip使它指向w的第一个符号,其中ip 是输入指针;
令X=栈顶符号;
while ( X≠#){ /* 栈非空 */
if ( X 等于ip所指向的符号a) 执行栈的弹出操作,将ip向前移动一个位置;
else if (M是一个终结符号) error ( ) ;
else if (M[X,a]是一个报错条目) error ( ) ;
else if (M[X,a] = X→Y1Y2 … Yk){
输出产生式 X→Y1Y2 … Yk ;
弹出栈顶符号;
将Yk,Yk-1…,Yi 压入栈中,其中
Y1位于栈顶。 }
令X=栈顶符号
}
(3)算法流程图
(4) 运行结果
一、实验目的和要求
构造 LR(1)分析程序,利用它进行语法分析,判断给出的符号串是否为该文 法识别的句子,了解 LR(K)分析方法是严格的从左向右扫描,和自底向上的 语法分析方法。
对下列文法,用 LR(1)分析法对任意输入的符号串进行分析:
1、编程时注意编程风格:空行的使用、注释的使用、缩进的使用等。
2、如果遇到错误的表达式,应输出错误提示信息。
3、程序输入/输出实例:
输入一以#结束的符号串(包括+*()i#):在此位置输入符号串
输出过程如下:
二、实验原理
(1)实验数据结构说明
/*****************************字段*****************************/
private string _inputString;//输入串
private Stack _status=new Stack();//状态栈
private Stack _symbol = new Stack();//符号栈
private List Vt = new List { 'i', '+', '*', '(', ')', '#' };//终结符集合
private List Vn = new List { 'E', 'T', 'F' };//非终结符集合
private List _production = new List {
"","E->E+T","E->T","T->T*F","T->F","F->(E)","F->i"};//为保证序号和下标一致,第一个产生式为空,不使用
private List> _goto = new List> {//GOTO表
new List {1,2,3 },
new List {-1,-1,-1 },
new List {-1,-1,-1 },
new List {-1,-1,-1 },
new List {8,2,3 },
new List {-1,-1,-1 },
new List {-1,9,3 },
new List {-1,-1,10 },
new List {-1,-1,-1 },
new List {-1,-1,-1 },
new List {-1,-1,-1 },
new List {-1,-1,-1 },
};
private List> _action = new List> {//ACTION表
new List {"S5",null,null,"S4",null,null },
new List {null,"S6",null,null,null,"acc" },
new List {null,"r2","S7",null,"r2","r2" },
new List {null,"r4","r4",null,"r4","r4" },
new List {"S5",null,null,"S4",null,null },
new List {null,"r6", "r6", null, "r6", "r6" },
new List {"S5",null,null,"S4",null,null },
new List {"S5",null,null,"S4",null,null },
new List {null,"S6",null,null, "S11",null },
new List {null,"r1","S7",null,"r1","r1" },
new List {null,"r3","r3",null, "r3", "r3" },
new List {null,"r5","r5",null, "r5", "r5" },
};
private int ip=0;//输入指针
private class Log
{
public string strStatus;
public string strSymbol;
public string strString;
public string strAction;
}
private List log = new List();//记录分析过程
(2)实验算法描述
输入:串w和LR语法分析表,该表描述了文法G的ACTION函数和GOTO函数。
输出:如果w在 L(G)中,则输出w的自底向上语法分析过程中的归约步骤;否则给出一个错误指示。
方法:初始时,语法分析器栈中的内容为初始状态s0,输入缓冲区中的内容为w#。然后,语法分析器执行下面的程序:
令a为w#的第一个符号;
while (1)
{ /* 永远重复*/
令s是栈顶的状态;
if (ACTION[s,a] = st)
{
将t压入栈中;
令a为下一个输入符号;
}
else if (ACTION[s,a] = 归约A → β ) {
从栈中弹出│ β │个符号;
将GOTO[t,A]压入栈中;
输出产生式 A → β ;
}
else if (ACTION[s,a] = 接受) break; /* 语法分析完成*/
else 调用错误恢复例程;
}
(3)算法流程图
(4) 运行结果:
完整代码:https://download.csdn.net/download/qq_40582463/10645775
百度云盘:https://pan.baidu.com/s/1Drleu2nKP6Eslj4Uc6i7Fg
开发环境:
VS2015、Windows10(文本缩放125%,可以解决控件显示不全的问题)