编译原理-算符优先
2016-05-17 11:29
387 查看
一、实验目的
根据文法求出FirstVT和LastVT,构造算法优先表,根据输入二元组判断正确与否二、实验内容
2.1任务一:构造FIRSTVT,LASTVT
FIRSTVT计算过程:(1)若有产生式P->a…或者P->Qa…,则a∈FIRSTVT(P);
(2)若a∈FIRSTVT(Q),且有产生式P->Q…,则a∈FIRSTVT(P);
LASTVT计算过程:
(1)若有产生式P->…a或者P->…aQ,则a∈LASTVT(P);
(2)若a∈LASTVT(Q),且有产生式P->…Q,则a∈LASTVT(P);
2.2任务二:构造算符优先表
(1)对形如 P→…ab…和形如P→…aQb…,有a=b;(2)对形如 P→…aR…,而b∈FIRSTVT(R),有a
2.3任务三:根据输入判断结果
(1)读入当前输入符号(2)若输入符号为数字,则输入符号进OPND,输入指针后移,转第(1)步
(3)若当前输入符号为终结符,则比较当前字符和符号栈栈顶字符的优先级
(4)若输入符号和栈顶的优先级相同,则输入指针后移,符号栈出栈
(5)若输入符号优先级小于栈顶的优先级,则输入符号入符号栈,输入指针后移
(6)若输入符号优先级大于栈顶的优先级,则从数据栈中弹出两个数,符号栈中弹出符号进行计算,结果压入数据栈。继续判断符号栈栈顶和输入符号的优先级
(7)若输入符号为“#”且符号栈只剩一个“#”时,表示判断成功。
三、实验过程
3.1 开发环境
使用Visual Studio 2015开发win32控制台程序3.2 数据结构设计
3.2.1 使用的宏定义#define MAXSIZE 10
#define NUM_VT 7
#define NUM_VN 5
3.2.2 定义的结构体
//栈操作
typedef struct Stack
{
char data[MAXSIZE];
int top;
};
3.3 测试数据设计
3.3.1 文法文件“nick.txt”
S->E
E->E+T
E->T
T->T*F
T->F
F->P^F
F->P
P->(E)
P->i
3.3.2 输入文件“Input.txt”
(i,1)
(+,)
(i,2)
(*,)
(i,3)
(#,)
/* *功能:算符优先 *作者:王文堃 *创建时间:2016/5/15 */ #include <iostream> #include <malloc.h> #include <stack> using namespace std; /* *任务一:构造FIRSTVT,LASTVT *任务开始时间:2016/5/15 *任务结束时间:2016/5/16 */ /* *任务二:构造算符优先表 *任务开始时间:2016/5/17 *任务完成时间:2016/5/17 */ /* *任务三:根据输入判断结果 *任务开始时间:2016/5/17 *任务完成时间:2016/5/17 */ #define MAXSIZE 10 #define NUM_VT 7 #define NUM_VN 5 char WenFa[MAXSIZE][MAXSIZE]; //存放从文件中读取的文法 int g_num_wenfa = 0; //存放文法的个数,初值为0 char vt[NUM_VT] = {'+','*','^','i','(',')','#'}; //所有终结符 char vn[NUM_VN] = {'E','T','F','P','S'}; //存放所有非终结符 int firstvt[NUM_VN][NUM_VT]; //存放每一个非终结符的firstvt int lastvt[NUM_VN][NUM_VT]; //存放每一个非终结符的lastvt int labelofch[MAXSIZE]; //存放已ch打头的所有文法的标号 char OPT[NUM_VT][NUM_VT]; //算符优先表 char Input[MAXSIZE][MAXSIZE]; //保存输入 int g_num_input = 0; //存放输入的个数 stack <char> OPTR; //符号栈 stack <char> OPND; //数据栈 /*-----------------------------------------计算FIRSTVT----------------------------------------*/ //读取文法 bool readfromfile(char* str) { FILE* fp = fopen(str, "r"); if (fp) //succeed { while (EOF != fscanf(fp, "%s", WenFa[g_num_wenfa])) { g_num_wenfa++; } fclose(fp); return true; } else { printf("error to open the file\n"); return false; } } //判断文法中字符是否为终结符 int isVt(char ch) { for (int i = 0; i < NUM_VT; i++) { if (ch == vt[i]) { return true; } } return false; } //判断文法中字符是否为非终结符 int isVn(char ch) { for (int i = 0; i < NUM_VN; i++) { if (ch == vn[i]) { return true; } } return false; } //找到以ch开头的文法的下标 int findit(char ch) { int num = 0; for (int i = 0; i < g_num_wenfa; i++) { if (WenFa[i][0] == ch) labelofch[num++] = i; } return num; //返回以ch开头文法的个数 } //将非终结符转换成下标 int Vn2num(char ch) { switch (ch) { case 'E': return 0; break; case 'T': return 1; break; case 'F': return 2; break; case 'P': return 3; break; case 'S': return 4; break; default: return -1; } } //将终结符转换成下标 int Vt2num(char ch) { switch (ch) { case '+': return 0; break; case '*': return 1; break; case '^': return 2; break; case 'i': return 3; break; case '(': return 4; break; case ')': return 5; break; case '#': return 6; break; default: return -1; } } //求FIRSTVT(ch) void get_firstvt(char ch) { if (isVn(ch)) //对终非结符ch { int firstnum = Vn2num(ch); //ch所对应的编号 int num = findit(ch); //找到以该非终结符开头文法的下标 for (int i = 0; i < num; i++) { char rightfirst = WenFa[labelofch[i]][3]; //取出文法推到的第一个字符 if (isVt(rightfirst)) //该文法推出的第一个字符是终结符 { firstvt[firstnum][Vt2num(rightfirst)] = 1; } else //该文法推出的第一个字符是非终结符 { char rightsc = WenFa[labelofch[i]][4]; if (isVt(rightsc)) //判断该非终结符之后的符号是不是终结符 { firstvt[firstnum][Vt2num(rightsc)] = 1; } if (ch != rightfirst) { get_firstvt(rightfirst); //计算这个非终结符的firstvt集 int newvn = Vn2num(rightfirst); for (int j = 0; j < NUM_VT; j++) { if (firstvt[newvn][j] == 1) { firstvt[firstnum][j] = 1; } } } } } } } //计算所有firstvt void do_firstvt() { for (int i = 0; i < NUM_VN; i++) { get_firstvt(vn[i]); } } //输出firstvt表 void output_firstvt() { int i = 0; printf("---------------firstvt----------------\n"); printf(" "); for (i = 0; i < NUM_VT; i++) { printf("%5c", vt[i]); } printf("\n"); for (i = 0; i < NUM_VN; i++) { printf("%c", vn[i]); for (int j = 0; j < NUM_VT; j++) { printf("%5d", firstvt[i][j]); } printf("\n"); } printf("--------------------------------------\n"); printf("\n"); } /*-----------------------------------------计算LASTVT----------------------------------------*/ //找到下标为num的文法的最后一个字符的下标 int get_last(int num) { for (int i = 0; i < MAXSIZE; i++) { if (WenFa[num][i] == '\0') { return (i-1); } } } //求LASTVT(ch) void get_lastvt(char ch) { if (isVn(ch)) //对终非结符ch { int lastnum = Vn2num(ch); //ch所对应的编号 int num = findit(ch); //找到以该非终结符开头文法的下标 for (int i = 0; i < num; i++) { int ilast = get_last(labelofch[i]); char rightlast = WenFa[labelofch[i]][ilast]; //获得文法的最后一个字符 if (isVt(rightlast)) //该文法推出的最后一个字符是终结符 { lastvt[lastnum][Vt2num(rightlast)] = 1; } else //该文法推出的最后一个字符是非终结符 { char rightlastsc = WenFa[labelofch[i]][ilast -1]; //文法倒数第二个字符 if (isVt(rightlastsc)) //判断倒数第二个字符是不是终结符 { lastvt[lastnum][Vt2num(rightlastsc)] = 1; } if (ch != rightlast) { get_lastvt(rightlast); //计算这个非终结符的lastvt集 int newvn = Vn2num(rightlast); for (int j = 0; j < NUM_VT; j++) { if (lastvt[newvn][j] == 1) { lastvt[lastnum][j] = 1; } } } } } } } //计算所有lastvt void do_lastvt() { for (int i = 0; i < NUM_VN; i++) { get_lastvt(vn[i]); } } //输出lastvt表 void output_lastvt() { int i = 0; printf("----------------lastvt----------------\n"); printf(" "); for (i = 0; i < NUM_VT; i++) { printf("%5c", vt[i]); } printf("\n"); for (i = 0; i < NUM_VN; i++) { printf("%c", vn[i]); for (int j = 0; j < NUM_VT; j++) { printf("%5d", lastvt[i][j]); } printf("\n"); } printf("--------------------------------------\n"); printf("\n"); } /*----------------------------------------构造算符优先表---------------------------------------*/ //处理小于的情况,a<FIRSTVT(R) void do_less(char vtch1, char vnch2) { int x = Vt2num(vtch1); //a int i = Vn2num(vnch2); //firstvt表中的行 for (int j = 0; j < NUM_VT; j++) //遍历firstvt表 { if (firstvt[i][j] == 1) OPT[x][j] = '<'; } } //处理大于的情况,LASTVT(R)>a void do_more(char vnch1, char vtch2) { int y = Vt2num(vtch2); int i = Vn2num(vnch1); for (int j = 0; j < NUM_VT; j++) //遍历LASTVT表 { if (lastvt[i][j] == 1) { OPT[j][y] = '>'; } } } //处理#号 void dosharp() { //添加“#=#” int temp = Vt2num('#'); OPT[temp][temp] = '='; //添加'#'<FIRSTVT(S)、LASTVT(S)>'#' for (int i = 0; i < NUM_VT; i++) { int x = Vn2num('S'); int j = Vt2num('#'); if (firstvt[x][i] == 1) { OPT[j][i] = '<'; } if (lastvt[x][i] == 1) { OPT[i][j] = '>'; } } } //构造算法优先表 void OperatorPrecedence() { int i = 0; //循环变量 for (i = 0; i < g_num_wenfa; i++) //对所有的文法 { int j = 3; //内部循环变量,从推出来的第一个字符开始 while (WenFa[i][j] != '\0') //该文法没有完 { char ch = WenFa[i][j]; if (isVt(ch)) //判断当前字符是不是终结符 { //判断下一个字符是不是终结符 if (isVt(WenFa[i][j + 1])) { //若是,则这两个终结符的优先级相等 OPT[Vt2num(ch)][Vt2num(WenFa[i][j + 1])] = '='; } else { //做小于 do_less(ch, WenFa[i][j + 1]); //继续判断下一个字符是不是终结符 if (isVt(WenFa[i][j + 2])) { //若是,则这两个终结符的优先级依然相等 OPT[Vt2num(ch)][Vt2num(WenFa[i][j + 2])] = '='; } } } else //当前是非终结符 { if (isVt(WenFa[i][j + 1])) //判断下一个字符 { //大于 do_more(ch, WenFa[i][j + 1]); } } j++; } } dosharp(); //处理# } //输出算符优先表 void output_OPT() { int i = 0; printf("------------------OPT-----------------\n"); printf(" "); for (i = 0; i < NUM_VT; i++) { printf("%5c", vt[i]); } printf("\n"); for (i = 0; i < NUM_VT; i++) { printf("%c", vt[i]); for (int j = 0; j < NUM_VT; j++) { printf("%5c", OPT[i][j]); } printf("\n"); } printf("--------------------------------------\n"); printf("\n"); } /*---------------------------------------处理输入判断结果---------------------------------------*/ //从文件中读取输入 bool inputformfile(char* str) { FILE* fp = fopen(str, "r"); if (fp) //succeed { while (EOF != fscanf(fp, "%s", Input[g_num_input])) { g_num_input++; } fclose(fp); return true; } else { printf("error to open the file\n"); return false; } } //判断输入字符的类型 int judge(char ch) { switch (ch) { case 'i': return 0; //表示数字 break; case '+': return 1; //表示'+'号 break; case '*': return 2; //表示'*'号 break; default: return -1; } } //将int行数字传化成char型 int char2int(char ch) { if (ch >= 48 && ch <= 57) //数字 { return ch - 48; } } //处理输入 bool doinput() { int iInput = 0; //指向输入下标 //#入符号栈 OPTR.push('#'); while (iInput != (g_num_input-1) || OPTR.size() != 0) //将输入遍历完 { char* str = Input[iInput]; //取出第一个输入 int result = judge(str[1]); if (result == 0) //数字 { OPND.push(str[3]); } else //是符号 { //判断符号栈顶的符号和当前符号的优先级 int topofstack = Vt2num(OPTR.top()); int topofinput = Vt2num(str[1]); if (OPT[topofstack][topofinput] == '<') //栈顶小于输入就入栈 { OPTR.push(str[1]); } else if (OPT[topofstack][topofinput] == '>')//否则运算 { int i = char2int(OPND.top()); OPND.pop(); int j = char2int(OPND.top()); OPND.pop(); int vtnum = Vt2num(OPTR.top()); if (vtnum == 0) //+ { OPND.push(i + j); OPTR.pop(); } else if (vtnum == 1) //* { OPND.push(i*j); OPTR.pop(); } } else //如果相等就出栈 { OPTR.pop(); } } if (iInput != (g_num_input-1)) { iInput++; } } //判断 if (OPTR.size() == 0) { return true; } else { return false; } } int main() { if (readfromfile("nick.txt")) { do_firstvt(); //构造firstvt output_firstvt(); //输出firstvt do_lastvt(); //构造lastvt output_lastvt(); //输出lastvt OperatorPrecedence(); //构造算符优先表 output_OPT(); //输出算符优先表 //--- inputformfile("input.txt"); bool result = doinput(); if (result != 0) { printf("最终结果正确\n"); } else { printf("最终结果错误\n"); } } getchar(); return 0; }
最终的运算结果是:
相关文章推荐
- 实现一个简单的计算器
- NOP(N) NOP_##N
- 编译器是如何工作的?(转)
- 写给想学java的朋友!
- 学java的必看!
- java中的加密!
- java中的使用类!
- LEX/FLEX词法分析器
- 如何模仿人的学习模式来教计算机程序解数学题?
- 上下文无关文法1
- 上下文无关文法2
- 编译原理——词法分析器
- 编译原理预习笔记------名词理解
- Simple scanner of c
- 编译原理:短语、直接短语、句柄、素短语
- 编译原理中FIRST集合与FOLLOW集合的算法
- 上下文无关文法解析
- 识别浮点常量问题-编译原理程序实现
- 计算的哲学(philosophy in coding)
- 编译原理 Chapter1 Introduction 第一章 简介