一、实验目的

任选一种有代表性的语法分析方法,如算符优先法、递归下降法、LL(1)、SLR(1)、LR(1)等,通过设计、编制、调试实现一个典型的语法分析程序,对实验一所得扫描器提供的单词序列进行语法检查和结构分析,实现并进一步掌握常用的语法分析方法。

二、基本实验内容与要求

选择对各种常见高级程序设计语言都较为通用的语法结构——算术表达式的一个简化子集——作为分析对象,根据如下描述其语法结构的 BNF 定义G2[<算术表达式>],任选一种学过的语法分析方法,针对运算对象为无符号常数和变量的四则运算,设计并实现一个语法分析程序。

G2[<算术表达式>]: <算术表达式> → <项> | <算术表达式>+<项> | <算术表达式>-<项> <项> → <因式> |<项><因式> | <项>/<因式> <因式> → <运算对象> | (<算术表达式>)
若将语法范畴<算术表达式>、<项>、<因式>和<运算对象>分别用 E、T、F 和 i 代表,则 G2 可写成:
G2[E]:E → T | E+T | E-T T → F | T
F | T/F F → i | (E)

输入:由实验一输出的单词串,例如:UCON,PL,UCON,MU,ID ······
输出:若输入源程序中的符号串是给定文法的句子,则输出“RIGHT”,并且给出每一步分析过程;若不是句子,即输入串有错误,则输出“ERROR”,并且显示分析至此所得的中间结果,如分析栈、符号栈中的信息等,以及必要的出错说明信息。
要求:
1、确定语法分析程序的流程图,同时考虑相应的数据结构,编写一个语法分析源程序。
2、将词法、语法分析合在一起构成一个完整的程序,并调试成功。3、供测试的例子应包括符合语法规则的语句,及分析程序能判别的若干错例。对于所输入的字符串,不论对错,都应有明确的信息输出。

采用SLR1分析方法,首先将给定文法的SLR1分析表手工画出,存入到程序中,然后进行语法分析程序的实现。

四、源程序

#include <iostream>
#include <string>
#include <iomanip>
#include <cstring>
#include <fstream>
#include <stdio.h>
#include <cassert>
#include <vector>
//#define _CRT_SECURE_NO_WARNINGS
using namespace std;
/*
begin   BEGIN   1
end     END     2
if      IF      3
then    THEN    4
else    ELSE    5
while   WHILE   6
do      DO      7
标识符 ID      8
浮点常数UCON    9
<        LT      10
<=      LE      11
==        EQ      12
<>        NE      13
>        GT      14
>=      GE      15
=      IS      16
+      PL      17
-       MI      18
*       MU      19
/       DI      20
(       LP      21  新增加的左括号
)       RP      22  新增加的右括号
*/const char* table1[] = { " ","begin","end","if","then","else","while","do" };
const char* table2[] = { " ","BEGIN","END","IF","THEN","ELSE","WHILE","DO","ID","UCON","LT","LE","EQ","NE","GT","GE","IS","PL","MI","MU","DI","LP","RP" };
char TOKEN[20];//用来依次存放一个单词词文中的各个字符。
char str[50];//存放词法分析后的表达式,将输入的表达式转变成只含有i和运算符的表达式
int len = 0;//词法分析后表达式的长度//action表   char vt[8] = { '(',')','+','-','*','/','i','#' };
string action[16][8] = { {"s4","null","null" ,"null" ,"null","null","s5" , "null" },{"null","null","s6","s7","null","null","null","acc"},{"null","r3","r3","r3","s8","s9","null","r3"} ,{"null","r6","r6","r6","r6","r6","null","r6"},{"s4","null","null","null","null","null","s5","null"},{"null","r8","r8","r8","r8","r8","null","r8"},{"s4","null","null","null","null","null","s5","null"},{"s4","null","null","null","null","null","s5","null"},{"s4","null","null","null","null","null","s5","null"},{"s4","null","null","null","null","null","s5","null"},{"null","s15","s6","s7","null","null","null","null"} ,{"null","r1","r1","r1","s8","s9","null","r1"},{"null","r2","r2","r2","s8","s9","null","r2"} ,{"null","r4","r4","r4","r4","r4","null","r4"} ,{"null","r5","r5","r5","r5","r5","null","r5"} ,{"null","r7","r7","r7","r7","r7","null","r7"} };
//go表 char vn[3] = { 'E','T','F' };
int go[16][3] = { {1 , 2 , 3 },{ -1 , -1 , -1 },{ -1 , -1 , -1 } ,{ -1 , -1 , -1 },{ 10 , 2 , 3 },{ -1 , -1 , -1 },{ -1 , 11 , 3 },{ -1 , 12 , 3 },{ -1 , -1 , 13 },{ -1 , -1 , 14 },{ -1 , -1 , -1 } ,{ -1 , -1 , -1 },{ -1 , -1 , -1 } ,{ -1 , -1 , -1 } ,{ -1 , -1 , -1 } ,{ -1 , -1 , -1 } };
string grammar[9] = { "S->E","E->E+T","E->E-T","E->T","T->T*F","T->T/F","T->F","F->(E)","F->i" };//存放文法表达式
int grammarlength[9] = { 4,6,6,4,6,6,4,6,4 };//每个文法表达式的长度
char grammarleft[9] = { 'S','E','E','E','T','T','T','F','F' };//文法表达式的左部
string grammarright[9] = { "E","E+T","E-T","T","T*F","T/F","F","(E)","i" };//文法表达式的右部
int grammarrightlength[9] = { 1,3,3,1,3,3,1,3,1 };//文法表达式右部长度int lookup(char a[])/*每调用一次,就以 TOKEN 中的字符串查保留字表,若查到,就将相应关键字的类别码赋给整型变量 c;否则将 c 置为零。*/
{int i;for (i = 1; i <= 7; i++){if (strcmp(a, table1[i]) == 0)return i;}if (i == 8){return 0;}}
void out(int a, const char b[])
/*一般仅在进入终态时调用此函数,调用的形式为 OUT(c,VAL)。
其中,实参 c 为相应单词的类别码助记符;实参 VAL 为 TOKEN(即词文)或为空串。
函数 OUT 的功能是,在送出一个单词的内部表示之后,返回到调用该词法分析程序的那个程序。*/
{ofstream fout("C:\\Users\\Administrator\\Desktop\\编译原理\\实验代码\\result.txt", fstream::app);char temp = ' ';if (a == 8 || a == 9){fout << "(" << table2[a] << " ," << b << ")" << endl;}else{fout << "(" << table2[a] << " ," << temp << ")" << endl;}fout.close();
}void report_error(void)//返回错误
{ofstream fout("C:\\Users\\Administrator\\Desktop\\编译原理\\实验代码\\result.txt", fstream::app);fout << "输入不合规范!";fout.close();exit(0);
}
void scanner_example(FILE* fp)
{char ch;int i, c, flag = 0;;//ch = fgetc(fp);while ((ch = fgetc(fp)) != EOF){if (ch == ' ')ch = fgetc(fp);if (ch == '\n')ch = fgetc(fp);if (ch == '#')break;if (isalpha(ch)) //判断字符是否为字母,识别标识符和关键字{TOKEN[0] = ch; ch = fgetc(fp); i = 1;while (isalnum(ch))//检查所传的字符是否是字母和数字{TOKEN[i] = ch; i++;ch = fgetc(fp);}TOKEN[i] = '\0';//结束符fseek(fp, -1, 1); //从文件当前位置后退一个字符c = lookup(TOKEN);//查找在第二个表中的位置,如果是关键字返回下标,标识符返回0if (c == 0){out(8, TOKEN);//识别标识符str[len] = 'i';len++;}else out(c, TOKEN);//识别关键字}else if (isdigit(ch))//判断字符是否为数字字符{TOKEN[0] = ch; ch = fgetc(fp); i = 1;while (isdigit(ch) || ch == '.'){if (ch == '.'){if (flag == 0){flag = 1;TOKEN[i] = ch; i++;}else report_error();}else{TOKEN[i] = ch; i++;}ch = fgetc(fp);}TOKEN[i] = '\0';fseek(fp, -1, 1);out(9, TOKEN);//识别数字str[len] = 'i';len++;}elseswitch (ch){case '<': ch = fgetc(fp);if (ch == '=')out(11, TOKEN);//<=else if (ch == '>') out(13, TOKEN);//<>else{fseek(fp, -1, 1);out(10, TOKEN);//<}break;case '=': ch = fgetc(fp);if (ch == '=')out(12, TOKEN);//==else{fseek(fp, -1, 1);out(16, TOKEN);//=}break;case '>': ch = fgetc(fp);if (ch == '=')out(15, TOKEN);//>=else{fseek(fp, -1, 1);out(14, TOKEN);//>}break;case '+':{out(17, TOKEN);str[len] = '+';len++;break;}case '-':{out(18, TOKEN);str[len] = '-';len++;break;}case '*':{out(19, TOKEN);str[len] = '*';len++;break;}case '/':{out(20, TOKEN);str[len] = '/';len++;break;}case '('://新增识别左括号{out(21, TOKEN);str[len] = '(';len++;break;}case ')'://新增识别右括号{out(22, TOKEN);str[len] = ')';len++;break;}default: report_error(); break;}}return;
}int main()
{cout << "请输入要分析的表达式(以#结束):" << endl;char strr[50];int i = 0;//输入表达式的长度ofstream out1("C:\\Users\\Administrator\\Desktop\\编译原理\\实验代码\\test.txt");out1.close();ofstream out("C:\\Users\\Administrator\\Desktop\\编译原理\\实验代码\\test.txt", ios::out);while (true)//将表达式存入文件test{cin >> strr[i];out << strr[i];if (strr[i] == '#')break;i++;}out.close();FILE* fp1;FILE* fp2;fp2 = fopen("C:\\Users\\Administrator\\Desktop\\编译原理\\实验代码\\result.txt", "w");fclose(fp2);fp1 = fopen("C:\\Users\\Administrator\\Desktop\\编译原理\\实验代码\\test.txt", "r");if (!fp1){printf("打开文件失败!");}scanner_example(fp1);//对test文件中的内容进行词法分析,存入result文件string temp;cout << endl;cout << "词法分析结果为:" << endl;ifstream myfile2("C:\\Users\\Administrator\\Desktop\\编译原理\\实验代码\\result.txt", ios::in);if (!myfile2.is_open()){cout << "未成功打开文件" << endl;}while (getline(myfile2, temp))//输出rusult文件中的内容{cout << temp << endl;}myfile2.close();str[len] = '#';cout << endl;cout << "词法分析后表达式为:" << endl;for (int i = 0; i <= len; i++){cout << str[i];}cout << endl;cout << endl;cout << "语法分析结果为:" << endl;int count;//步骤int state[100], stateIndex;//状态串及下标state[0] = 0; stateIndex = 0;//初始化状态串char inSymbol[100], outSymbol[100];//栈中符号串  余留符号串inSymbol[0] = '#';//初始化int inSymbolIndex = 0, outSymbolIndex = 0;string analysis;//分析动作//string nextState;//下一状态for (int i = 0; i <= len; i++){outSymbol[i] = str[i];}cout << "字符串长度为:" << len << endl;cout << "步骤" << "\t" << "状态" << "\t\t";cout << "栈中符号串" << "\t\t" << "余留符号串" << "\t\t";cout << "分析动作" << "\t\n";for (count = 1;;){cout << count << fixed << setprecision(3) << "\t";for (int i = 0; i <= stateIndex; i++)//输出状态串{cout << state[i] << " " << fixed << setprecision(4);}cout << "\t\t";for (int i = 0; i <= inSymbolIndex; i++)//输出栈中符号串{cout << inSymbol[i] << " " << fixed << setprecision(4);}cout << "\t\t";for (int i = outSymbolIndex; i <= len; i++)//输出余留符号串{cout << outSymbol[i] << " " << fixed << setprecision(4);}cout << "\t\t";int row = state[stateIndex], column;//根据状态串和余留符号串查action表if (outSymbol[outSymbolIndex] == '(')column = 0;else if (outSymbol[outSymbolIndex] == ')')column = 1;else if (outSymbol[outSymbolIndex] == '+')column = 2;else if (outSymbol[outSymbolIndex] == '-')column = 3;else if (outSymbol[outSymbolIndex] == '*')column = 4;else if (outSymbol[outSymbolIndex] == '/')column = 5;else if (outSymbol[outSymbolIndex] == 'i')column = 6;else if (outSymbol[outSymbolIndex] == '#')column = 7;if (action[row][column] == "null"){cout << "error!" << endl;break;}else//查表不为空{analysis = action[row][column];cout << analysis << "\t";//输出分析动作if (analysis[0] == 's')//查到以s开头的分析动作{cout << endl;stateIndex++;if (analysis.length() == 3){state[stateIndex] = ((int)(analysis[1]) - 48) * 10 + ((int)(analysis[2]) - 48);}else state[stateIndex] = (int)analysis[1] - 48;inSymbolIndex++;inSymbol[inSymbolIndex] = outSymbol[outSymbolIndex];outSymbolIndex++;count++;}else if (analysis[0] == 'r')//查到以r开头的分析动作{int temp = (int)analysis[1] - 48;cout << grammar[temp] << endl;stateIndex = stateIndex - grammarrightlength[temp];inSymbolIndex = inSymbolIndex + 1 - grammarrightlength[temp];inSymbol[inSymbolIndex] = grammarleft[temp];cout << " " << fixed << setprecision(3) << "\t";for (int i = 0; i <= stateIndex; i++)//输出状态串{cout << state[i] << " " << fixed << setprecision(4);}cout << "\t\t";for (int i = 0; i <= inSymbolIndex; i++)//栈中符号串{cout << inSymbol[i] << " " << fixed << setprecision(4);}cout << "\t\t";for (int i = outSymbolIndex; i <= len; i++)//余留符号串{cout << outSymbol[i] << " " << fixed << setprecision(4);}cout << "\t\t";int row2 = state[stateIndex], column2;//根据状态串和栈中符号串查go表if (inSymbol[inSymbolIndex] == 'E')column2 = 0;else if (inSymbol[inSymbolIndex] == 'T')column2 = 1;else if (inSymbol[inSymbolIndex] == 'F')column2 = 2;if (go[row2][column2] == -1){cout << "error!" << endl;break;}else//不为空,输出{cout << "go[" << row2 << "," << column2 << "]=" << go[row2][column2] << endl;stateIndex++;state[stateIndex] = go[row2][column2];count++;}}else if (analysis == "acc"){cout << endl << "分析成功!";break;}}}fclose(fp1);fclose(fp2);return 0;
}

五、实验结果及分析

1、输入正确的表达式


2、输入错误表达式

编译原理实验二 语法分析程序设计与实现相关推荐

  1. 编译原理 实验二 词法分析程序设计

    1. 实验内容 ● TINY语言的词法由TINY Syntax.ppt描述: ● TINY语言的词法分析器由TINY Scanner.rar的C语言代码实现: ● TINY+语言的词法由TINY+ S ...

  2. 编译原理实验二:赋值语句的语法分析程序设计

    编译原理实验二:赋值语句的语法分析程序设计 1.1实验内容 目的: 在前面实验的基础上,通过设计.编制.调试一个典型的赋值语句的语法分析程序,实现对词法分析程序所提供的单词序列进行语法检查,进一步掌握 ...

  3. 编译原理--实验2 语法分析

    文章目录 前言 1.1实验目的 1.2 实验任务 1.3 实验内容 1.3.1 实验要求 1.3.2 输入格式 1.3.3 输出格式 1.3.4 样例 1.4 程序 1.4.1 程序流程图 1.4.2 ...

  4. 编译原理实验二:Bison

    编译原理实验二:Bison 实验要求 1.了解Bision基础知识,如何将文法产生式转换为Bison语句 2.阅读/src/common/SyntaxTree.c,对应头文件 /include/Syn ...

  5. 编译原理实验二(全部存储到数组再逐行验证语法版.....这种思路被老师否了,应该是验证一行扔掉一行才对)

    编译原理实验二(可能还有BUG,不确定继续找) 要大改一次23333,老师的意思是不能用数组存储,而是一边识别单词,然后识别完一行就判断一次语法 写实验二的时候找到的实验一的一个逻辑错误 改动了实验一 ...

  6. 编译原理实验二 macos系统 itoa方法报错解决方法

    编译原理实验二 生成符号表的前期准备中使用itoa函数报错问题 执行 gcc -o parser lex.yy.c parser.tab.c ast.c 显示: itoa 函数是一个广泛应用的,从非标 ...

  7. 编译原理实验三 语义分析程序设计与实现

    一.实验目的 在实现词法.语法分析程序的基础上,编写相应的语义子程序,进行语义处理,加深对语法制导翻译原理的理解,进一步掌握将语法分析所识别的语法范畴变换为某种中间代码(四元式)的语义分析方法,并完成 ...

  8. 编译原理实验一 词法分析程序设计与实现

    一.实验目的 通过编写和调试一个词法分析程序,掌握在对程序设计语言的源程序进行扫描的过程中,将字符流形式的源程序转化为一个由各类单词构成的序列的词法分析方法. 二.基本实验内容与要求 假定一种高级程序 ...

  9. 编译原理实验二:标识符的识别

    实验要求 [任务介绍]根据给定源语言的构词规则,从任意字符串中识别出所有的合法标识符. [输入]字符串. [输出]单词符号流,一行一个单词. [题目]设计一个程序,从任意字符串中识别出所有可视为C语言 ...

  10. 编译原理—实验二LL(1)语法分析(一)

    一.实验目的 1.熟悉LL(1)语法分析的基本原理,语法分析的过程,以及语法分析中要注意的一些问题. 2. 复习高级语言及线性表.栈.图等典型数据结构,进一步加强用高级语言来解决实际问题的能力. 二. ...

最新文章

  1. Altium Designer PCB快速布局
  2. [转]Delphi 2010 3513正式版破解
  3. linux系统深度清理上网记录,linux日志清理,云主机磁盘清理经验
  4. 6的变换_电源学报 | 2020年第3期:quot;高性能双向直流变换技术及其应用quot;专辑征稿启事...
  5. JSON学习资料整理
  6. catia知识工程_【开团】CATIA超全直播课程&实例讲解!
  7. 未经授权的ADB Android设备
  8. vim php psr,PHP编码规范(PSR-1)-基本代码规范
  9. python并发编程 协程_Python并发编程协程之Gevent
  10. 2016年数学建模-A 系泊系统设计
  11. CentOS8.4搭建PXE启动Ubuntu-server20.04(UEFI)
  12. wh计算公式_阀门扭矩计算公式
  13. 中国杰出前十大程序员_10个值得关注的杰出青少年应用程序开发人员
  14. Ubuntu的VirtualBox虚拟机怎么识别物理机的U盘?我教你。
  15. imx6 通过移植XRM117x(SPI转串口)对Linux中的SPI驱动框架进行分析
  16. html选择按键点击后锁死输入框_js Dom为页面中的元素绑定键盘或鼠标事件
  17. Java#21(抽象与接口)
  18. 数据库 —— mycat 代理
  19. Web前端课程大酬宾啦.....
  20. 嵌入式IDE原理 OpenOCD介绍 以及stlink如何连接stm32板子

热门文章

  1. ai python视频教程_2017AI人工智能时代基础实战python机器学习深度学习算法全套视频教程...
  2. 阿里ICON图标,使用教程
  3. 游戏开发者,游戏开发教程
  4. 简单三步,教你搭建一个私有云盘
  5. power BI电商案例分析
  6. 谷歌浏览器 cococut version 视频下载插件配合倍速插件快速下载视频
  7. 电脑上win10的mysql软件老闪退,技术编辑应对win10系统Mysql闪退的修复办法
  8. Python爬虫【一】爬取移动版“微博辟谣”账号内容(API接口)
  9. SPI通信协议_02
  10. 四川行无疆电商讲解拼多多电商产品销量如何清零