河北工业大学编译原理实验二——语法分析程序设计与实现

前言:本文是河北工业大学(Hebut)计算机科学与技术专业编译原理的实验报告。公开实验报告是为了传承当时做实验前辈们的帮助,也是希望大家可以共同学习。慎抄,或者不建议抄袭!


实验二 语法分析程序设计与实现

一、实验目的

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

二、基本实验内容与要求

选择对各种常见高级程序设计语言都较为通用的语法结构——算术表达式的一个简化子集——作为分析对象,根据如下描述其语法结构的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)
输入:由实验一输出的单词类别,例如:REAL,PL,INT,MU,ID ······
输出:若输入源程序中的符号串是给定文法的句子,则输出“RIGHT”,并且给出每一步分析过程;若不是句子,即输入串有错误,则输出“ERROR”,并且显示分析至此所得的中间结果,以及必要的出错说明信息(错误的位置及性质)。
要求:
1、上机前确定好语法分析程序的流程图,同时考虑相应的数据结构。
2、将词法、语法分析合在一起构成一个完整的程序,并调试成功。
3、供测试的例子应包括符合语法规则的语句,及分析程序能判别的若干错例。对于所输入的字符串,不论对错,都应有明确的信息输出。

三、实验内容

(1)实验设计

选用SLR(1)分析法进行本次实验。通过构造SLR(1)分析表,对单词进行移进规约从而完成语法分析。
首先改写G2[2]文法为拓广文法:
0. E’→E

  1. E→E+T
  2. E→E-T
  3. E→T
  4. T→T*F
  5. T→T/F
  6. T→F
  7. F→(E)
  8. F→i
    画出识别所有活前缀的DFA如下图:
    根据课上学习画的图,上课好好听,总会画的
    再求出所有非终结符的FOLLOW集:
    FOLLOW (E)={#,),+,-}
    FOLLOW (T)={#,),+,-,,/}
    FOLLOW (F)={#,),+,-,
    ,/}
    综上可以画出SLR(1)分析表:
    上课知识,做实验的目的就是为了巩固学习
    把该表作为程序的静态变量初始化,然后利用分析法中的步骤编写代码进行分析,如果最终到达acc状态说明归约成功,如果到达空状态说明规约失败,输出错误信息。

(2)程序代码

/*****************************************************************//**
 * \file   语法分析程序.cpp
 * \brief  编译原理实验二	语法分析程序设计与实现
 * 
 * \author admin
 * \date   November 2022
 *********************************************************************/
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<stack>
#include<iomanip>
#include<string>
#include <cstring>
#include"MyLex.h"
using namespace std;
string sGoto;
int step = 1;
stack<int> status;/*状态栈*/
string word;
string value;
/*构造SLR(1)表,共有15个状态,需要构造ACTION表与GOTO表,然后用不同的函数处理*/
string Action[16][8] =
{
	"s4","0","0","0","0","0","s5","0",		//0
	"0","0","s6","s7","0","0","0","acc",	//1
	"0","r3","r3","r3","s8","s9","0","r3",	//2
	"0","r6","r6","r6","r6","r6","0","r6",	//3
	"s4","0","0","0","0","0","s5","0",		//4
	"0","r8","r8","r8","r8","r8","0","r8",	//5
	"s4","0","0","0","0","0","s5","0",		//6
	"s4","0","0","0","0","0","s5","0",		//7
	"s4","0","0","0","0","0","s5","0",		//8
	"s4","0","0","0","0","0","s5","0",		//9
	"0","s15","s6","s7","0","0","0","0",	//10
	"0","r1","r1","r1","s8","s9","0","r1",	//11
	"0","r2","r2","r2","s8","s9","0","r2",	//12
	"0","r4","r4","r4","r4","r4","0","r4",	//13
	"0","r5","r5","r5","r5","r5","0","r5",	//14
	"0","r7","r7","r7","r7","r7","0","r7"	//15
};
int Goto[16][3] =
{
	1,2,3,
	0,0,0,
	0,0,0,
	0,0,0,
	10,2,3,
	0,0,0,
	0,11,3,
	0,12,3,
	0,0,13,
	0,0,14,
	0
};

void reduction(int num)
{
	switch (num)
	{
	case 1:// E→E+T
		status.pop(); status.pop(); status.pop();
		sGoto = "goto" + to_string(Goto[status.top()][0]);
		status.push(Goto[status.top()][0]);
		break;
	case 2:// E→E-T
		status.pop(); status.pop(); status.pop();
		sGoto = "goto" + to_string(Goto[status.top()][0]);
		status.push(Goto[status.top()][0]);
		break;
	case 3:// E→T
		status.pop(); 
		sGoto = "goto" + to_string(Goto[status.top()][0]);
		status.push(Goto[status.top()][0]);
		break;
	case 4:// T→T*F
		status.pop(); status.pop(); status.pop();
		sGoto = "goto" + to_string(Goto[status.top()][1]);
		status.push(Goto[status.top()][1]);
		break;
	case 5:// T→T/F
		status.pop(); status.pop(); status.pop();
		sGoto = "goto" + to_string(Goto[status.top()][1]);
		status.push(Goto[status.top()][1]);
		break;
	case 6:// T→F
		status.pop();
		sGoto = "goto" + to_string(Goto[status.top()][1]);
		status.push(Goto[status.top()][1]);
		break;
	case 7:// F→(E)
		status.pop(); status.pop(); status.pop();
		sGoto = "goto" + to_string(Goto[status.top()][2]);
		status.push(Goto[status.top()][2]);
		break;
	case 8:// F→i
		status.pop();
				sGoto = "goto" + to_string(Goto[status.top()][2]);
		status.push(Goto[status.top()][2]);
		break;
	default:
		break;
	}
}

string outStack()
{
	string s = "";
	string c;
	int val;
	stack <int> temp;
	while (!status.empty())
	{
		val = status.top();
		temp.push(val);
		c = to_string(val) + " ";
		s += c;
		status.pop();
	}
	//复原
	while (!temp.empty())
	{
		val = temp.top();
		temp.pop();
		status.push(val);
	}
	return s;
}

int getChar(string word)
{
	if (word == "LP")return 0;
	if (word == "RP")return 1;
	if (word == "PL")return 2;
	if (word == "MI")return 3;
	if (word == "MU")return 4;
	if (word == "DI")return 5;
	if (word == "ID" || word == "INT" || word == "REAL")return 6;
	if (word == "#")return 7;
}
void out()
{
	cout << std::left << setw(8) << step << std::right << setw(30) << outStack();
	cout << std::left << setw(12) << word << setw(10) << value <<sGoto << endl;
	step++;	sGoto = "";
}
void scanner(FILE* fp)
{
	int num;
	word = myLex(fp);
	while (value != "acc")
	{
		value = Action[status.top()][getChar(word)];
		out();
		if (value.substr(0, 1) == "r")
		{
			num = stoi(value.substr(1));
			reduction(num);
		}
		else if (value.substr(0, 1) == "s")
		{
			num = stoi(value.substr(1));
			status.push(num);
			word = myLex(fp);
		}
		else if (value == "0")
		{
			cout << "语法错误:error: in (" << lines << ")lines " << word<<" 处出现错误!" << endl;
			exit(0);
		}
	}
	cout << "RIGHT" << endl;
}
int main()
{
	char fileName[20];/*待查询的文件名*/
	cout << "请输入要进行词法分析的文件名:";
	cin >> fileName;
	FILE* fpInput = fopen(fileName, "r");
	status.push(0);
	cout << std::left <<setw(8) << "步骤数"<<  setw(30) << "状态栈"<< setw(12) << "待分析符号"<< setw(10) << "当前操作" << endl;
	scanner(fpInput);
	fclose(fpInput);
}


/*****************************************************************//*
 * \file   源.cpp
 * \brief  编译原理实验一	词法分析程序设计与实现
 *
 * \author admin
 * \date   October 2022
 *********************************************************************/
#define _CRT_SECURE_NO_WARNINGS
#include<fstream>
#include<string>
#include"number.h"
using namespace std;
const int MAX_KEYWORD_NUMBER = 40;	/*关键字的数量*/
const char* KEY_WORD_END = "Waiting for extending"; /*关键字结束标记*/
const char* KEY_WORD_START = "Waiting for identifying"; /*关键字结束标记*/
const char* Keyword[MAX_KEYWORD_NUMBER] = { KEY_WORD_START,"if","else","for","do","begin","end",KEY_WORD_END };/*关键词数组*/
const char* Value[MAX_KEYWORD_NUMBER] = { KEY_WORD_START,"IF","ELSE","FOR","DO","BEGIN","END","ID","INT","REAL","LT","LE","EQ","NE","GT","GE","IS","PL","MI","MU","DI","AND","OR","NOT","OCT","HEX","CHAR","STR","LP","RP","#",KEY_WORD_END};/*助记符数组*/
char TOKEN[30];/*存放单词词文*/
int lines = 1;/*记录当前运行到的行数*/

/**
 * \brief 显示错误信息
 * \param s 错误信息
 */
void report_error(string s)
{
	cout << "词法错误:error: in (" << lines << ")lines, " << s << endl;
}

/**
 * \brief 以token查询关键字表
 * \param token 待查字符串
 * \return 如果为关键字,返回所处位置,没有找到返回0
 */
int lookUp(char* token)
{
	int i = 1;
	while (strcmp(Keyword[i], KEY_WORD_END) != 0)
	{
		if (strcmp(token, Keyword[i]) == 0)return i;
		i++;
	}
	return 0;
}

/**
 * \brief 输出二元式
 * \param a 单词的value值
 * \param token 有值单词的值
 */
string out(int a, const char* token)
{
	return Value[a];
}
/**
 * \brief 扫描文件字符并拼接token进行查询
 * \param fp 文件指针
 */
string scanner_example(FILE* fp)
{
	char ch; int i, c;
	while ((ch = fgetc(fp)) != EOF)
	{
		if (ch == ' ' || ch == '\t')//不识别空格和tab
		{
			ch = fgetc(fp);
			fseek(fp, -1, SEEK_CUR);
		}
		else if (ch == '\n')//不识别换行
		{
			lines++;
		}
		else if (isalpha(ch))  /*it must be a identifer!*/
		{
			TOKEN[0] = ch; ch = fgetc(fp); i = 1;
			while (isalnum(ch))
			{
				TOKEN[i] = ch; i++;
				ch = fgetc(fp);
			}
			TOKEN[i] = '\0';
			if (ch != EOF)
			{
				fseek(fp, -1, SEEK_CUR);  /* retract*/
			}
			c = lookUp(TOKEN);
			if (c == 0) return out(7, TOKEN);
			else if (c == 6)return out(6, " ");
			else return out(c, " ");
		}
		else if (isdigit(ch) || ch == '.')
		{
			if (ch == '0')
			{
				ch = fgetc(fp);
				if (ch >= '0' && ch < '8')
				{
					TOKEN[0] = '0'; i = 1;
					while (isdigit(ch))
					{
						TOKEN[i] = ch; i++;
						ch = fgetc(fp);
					}
					TOKEN[i] = '\0';
					return out(24, TOKEN);
				}
				else if (ch == 'x')
				{
					TOKEN[0] = '0'; TOKEN[1] = 'x';
					ch = fgetc(fp); i = 2;
					while (ch >= '.' && ch <= '9' || ch >= 'a' && ch <= 'f' || ch >= 'A' && ch <= 'F')
					{
						TOKEN[i] = ch; i++;
						ch = fgetc(fp);
					}
					TOKEN[i] = '\0';
					return out(25, TOKEN);
				}
				else if (ch != '.' && !isdigit(ch))
				{
					return out(8, "0\0");
				}
				else
				{
					fseek(fp, -1, SEEK_CUR);  /* retract*/
				}
			}
			else if (isdigit(ch) || ch == '.')
			{
				int flag = getFCON(fp, TOKEN, ch);
				if (!flag)report_error("数值错误!");
				return out(flag, TOKEN);
			}
		}
		else
		{
			switch (ch)
			{
			case ':':ch = fgetc(fp);
				if (ch == '=')out(16, " ");
				break;
			case '<': ch = fgetc(fp);
				if (ch == '=')out(11, " ");
				else if (ch == '>') out(13, " ");
				else
				{
					fseek(fp, -1, SEEK_CUR);
					out(10, " ");
				}
				break;
			case '=': out(12, " "); break;
			case '>': ch = fgetc(fp);
				if (ch == '=')out(15, " ");
				else
				{
					fseek(fp, -1, SEEK_CUR);
					out(14, " ");
				}
				break;
			case'+':return out(17, " "); break;
			case'-':return out(18, " "); break;
			case'*':return out(19, " "); break;
			case'/':
				//不识别注释
				ch = fgetc(fp);
				if (ch == '/')//单行注释
				{
					while (ch != '\n')ch = fgetc(fp);
					lines++;
					ch = fgetc(fp);
					fseek(fp, -1, SEEK_CUR);
				}
				else if (ch == '*')//多行注释
				{
					while (true)
					{
						ch = fgetc(fp);
						if (ch == '\n')
						{
							lines++; ch = fgetc(fp);
						}
						if (ch == '*')
						{
							ch = fgetc(fp);
							if (ch == '/')
							{
								ch = fgetc(fp);
								if (ch == '\n')
								{
									lines++; ch = fgetc(fp);
								}
								break;
							}
							else fseek(fp, -1, SEEK_CUR);
						}
					}
				}
				else
				{
					fseek(fp, -1, SEEK_CUR);
					return out(20, " ");
				}
				break;
			case'&':ch = fgetc(fp);
				if (ch == '&')out(21, " ");
				else report_error("逻辑运算出错!"); break;
			case'|':ch = fgetc(fp);
				if (ch == '|')out(22, " ");
				else report_error("逻辑运算出错!"); break;
			case'!':out(23, " "); break;
			case'(':return out(28, " "); break;
			case')':return out(29, " "); break;
			case '\'':
			{
				ch = fgetc(fp);
				TOKEN[0] = ch;
				if ((ch = fgetc(fp) != '\''))
				{
					report_error("字符常量出错!");
				}
				else
				{
					TOKEN[1] = '\0';
				}
				out(26, TOKEN);
				break;
			}
			case '\"':
			{
				ch = fgetc(fp); i = 0;
				while (ch != '\"')
				{
					TOKEN[i] = ch; i++;
					ch = fgetc(fp);
				}
				TOKEN[i] = '\0';
				out(27, TOKEN);
				break;
			}
			default: report_error("运算符号出错!"); break;
			}
		}
	}
	if (ch == EOF)
	{
		return out(30, " ");
	}
}

string myLex(FILE* fp)
{
	return scanner_example(fp);
}

/*****************************************************************//**
 * \file   MyLex.h
 * \brief  词法输入程序
 * 
 * \author admin
 * \date   November 2022
 *********************************************************************/
#define _CRT_SECURE_NO_WARNINGS
#include<fstream>
#include<string>
#include"number.h"
using namespace std;
const int MAX_KEYWORD_NUMBER = 40;	/*关键字的数量*/
const char* KEY_WORD_END = "Waiting for extending"; /*关键字结束标记*/
const char* KEY_WORD_START = "Waiting for identifying"; /*关键字结束标记*/
const char* Keyword[MAX_KEYWORD_NUMBER] = { KEY_WORD_START,"if","else","for","do","begin","end",KEY_WORD_END };/*关键词数组*/
const char* Value[MAX_KEYWORD_NUMBER] = { KEY_WORD_START,"IF","ELSE","FOR","DO","BEGIN","END","ID","INT","REAL","LT","LE","EQ","NE","GT","GE","IS","PL","MI","MU","DI","AND","OR","NOT","OCT","HEX","CHAR","STR","LP","RP","#",KEY_WORD_END};/*助记符数组*/
char TOKEN[30];/*存放单词词文*/
int lines = 1;/*记录当前运行到的行数*/

/**
 * \brief 显示错误信息
 * \param s 错误信息
 */
void report_error(string s)
{
	cout << "词法错误:error: in (" << lines << ")lines, " << s << endl;
}

/**
 * \brief 以token查询关键字表
 * \param token 待查字符串
 * \return 如果为关键字,返回所处位置,没有找到返回0
 */
int lookUp(char* token)
{
	int i = 1;
	while (strcmp(Keyword[i], KEY_WORD_END) != 0)
	{
		if (strcmp(token, Keyword[i]) == 0)return i;
		i++;
	}
	return 0;
}

/**
 * \brief 输出二元式
 * \param a 单词的value值
 * \param token 有值单词的值
 */
string out(int a, const char* token)
{
	return Value[a];
}
/**
 * \brief 扫描文件字符并拼接token进行查询
 * \param fp 文件指针
 */
string scanner_example(FILE* fp)
{
	char ch; int i, c;
	while ((ch = fgetc(fp)) != EOF)
	{
		if (ch == ' ' || ch == '\t')//不识别空格和tab
		{
			ch = fgetc(fp);
			fseek(fp, -1, SEEK_CUR);
		}
		else if (ch == '\n')//不识别换行
		{
			lines++;
		}
		else if (isalpha(ch))  /*it must be a identifer!*/
		{
			TOKEN[0] = ch; ch = fgetc(fp); i = 1;
			while (isalnum(ch))
			{
				TOKEN[i] = ch; i++;
				ch = fgetc(fp);
			}
			TOKEN[i] = '\0';
			if (ch != EOF)
			{
				fseek(fp, -1, SEEK_CUR);  /* retract*/
			}
			c = lookUp(TOKEN);
			if (c == 0) return out(7, TOKEN);
			else if (c == 6)return out(6, " ");
			else return out(c, " ");
		}
		else if (isdigit(ch) || ch == '.')
		{
			if (ch == '0')
			{
				ch = fgetc(fp);
				if (ch >= '0' && ch < '8')
				{
					TOKEN[0] = '0'; i = 1;
					while (isdigit(ch))
					{
						TOKEN[i] = ch; i++;
						ch = fgetc(fp);
					}
					TOKEN[i] = '\0';
					return out(24, TOKEN);
				}
				else if (ch == 'x')
				{
					TOKEN[0] = '0'; TOKEN[1] = 'x';
					ch = fgetc(fp); i = 2;
					while (ch >= '.' && ch <= '9' || ch >= 'a' && ch <= 'f' || ch >= 'A' && ch <= 'F')
					{
						TOKEN[i] = ch; i++;
						ch = fgetc(fp);
					}
					TOKEN[i] = '\0';
					return out(25, TOKEN);
				}
				else if (ch != '.' && !isdigit(ch))
				{
					return out(8, "0\0");
				}
				else
				{
					fseek(fp, -1, SEEK_CUR);  /* retract*/
				}
			}
			else if (isdigit(ch) || ch == '.')
			{
				int flag = getFCON(fp, TOKEN, ch);
				if (!flag)report_error("数值错误!");
				return out(flag, TOKEN);
			}
		}
		else
		{
			switch (ch)
			{
			case ':':ch = fgetc(fp);
				if (ch == '=')out(16, " ");
				break;
			case '<': ch = fgetc(fp);
				if (ch == '=')out(11, " ");
				else if (ch == '>') out(13, " ");
				else
				{
					fseek(fp, -1, SEEK_CUR);
					out(10, " ");
				}
				break;
			case '=': out(12, " "); break;
			case '>': ch = fgetc(fp);
				if (ch == '=')out(15, " ");
				else
				{
					fseek(fp, -1, SEEK_CUR);
					out(14, " ");
				}
				break;
			case'+':return out(17, " "); break;
			case'-':return out(18, " "); break;
			case'*':return out(19, " "); break;
			case'/':
				//不识别注释
				ch = fgetc(fp);
				if (ch == '/')//单行注释
				{
					while (ch != '\n')ch = fgetc(fp);
					lines++;
					ch = fgetc(fp);
					fseek(fp, -1, SEEK_CUR);
				}
				else if (ch == '*')//多行注释
				{
					while (true)
					{
						ch = fgetc(fp);
						if (ch == '\n')
						{
							lines++; ch = fgetc(fp);
						}
						if (ch == '*')
						{
							ch = fgetc(fp);
							if (ch == '/')
							{
								ch = fgetc(fp);
								if (ch == '\n')
								{
									lines++; ch = fgetc(fp);
								}
								break;
							}
							else fseek(fp, -1, SEEK_CUR);
						}
					}
				}
				else
				{
					fseek(fp, -1, SEEK_CUR);
					return out(20, " ");
				}
				break;
			case'&':ch = fgetc(fp);
				if (ch == '&')out(21, " ");
				else report_error("逻辑运算出错!"); break;
			case'|':ch = fgetc(fp);
				if (ch == '|')out(22, " ");
				else report_error("逻辑运算出错!"); break;
			case'!':out(23, " "); break;
			case'(':return out(28, " "); break;
			case')':return out(29, " "); break;
			case '\'':
			{
				ch = fgetc(fp);
				TOKEN[0] = ch;
				if ((ch = fgetc(fp) != '\''))
				{
					report_error("字符常量出错!");
				}
				else
				{
					TOKEN[1] = '\0';
				}
				out(26, TOKEN);
				break;
			}
			case '\"':
			{
				ch = fgetc(fp); i = 0;
				while (ch != '\"')
				{
					TOKEN[i] = ch; i++;
					ch = fgetc(fp);
				}
				TOKEN[i] = '\0';
				out(27, TOKEN);
				break;
			}
			default: report_error("运算符号出错!"); break;
			}
		}
	}
	if (ch == EOF)
	{
		return out(30, " ");
	}
}

string myLex(FILE* fp)
{
	return scanner_example(fp);
}
/*****************************************************************//**
 * \file   number.h
 * \brief  词法输入程序的数字部分
 * 
 * \author admin
 * \date   November 2022
 *********************************************************************/
#include<iostream>
#include<math.h>
#include<string>
using namespace std;
#define ClassNo 100		//Suppose the class of number is 100
#define EndState -1
#define ErrorState -2
enum
{
	DIGIT = 1, POINT, OTHER, POWER, PLUS, MINUS
};
int w, n, p, e, d;
int Class;
int ICON;
long double FCON;
static int CurrentState;

int GetChar(void);
int EXCUTE(int, int);
int GetChar(char c)
{

	if (isdigit(c))
	{
		d = c - '0';
		return DIGIT;
	}
	if (c == '.')return POINT;
	if (c == 'E' || c == 'e')return POWER;
	if (c == '+')return PLUS;
	if (c == '-')return MINUS;
	return OTHER;
}
int EXCUTE(int state, int symbol)
{
	switch (state)
	{
	case 0:
		switch (symbol)
		{
		case DIGIT:
			n = 0; p = 0; e = 1; w = d; CurrentState = 1; Class = ClassNo; break;
		case POINT:
			n = 0; p = 0; e = 1; w = 0; CurrentState = 3; Class = ClassNo; break;
		default:
			CurrentState = EndState;
		}
		break;
	case 1:
		switch (symbol)
		{
		case DIGIT:
			w = w * 10 + d; break; //CurrentState = 1
		case POINT:
			CurrentState = 2; break;
		case POWER:
			CurrentState = 4; break;
		default:
			ICON = w;
			CurrentState = EndState;
		}
		break;
	case 2:
		switch (symbol)
		{
		case DIGIT:
			n++; w = w * 10 + d; break;
		case POWER:
			CurrentState = 4; break;
		default:
			FCON = w * pow(10, e * p - n);
			CurrentState = EndState;
		}
		break;
	case 3:
		switch (symbol)
		{
		case DIGIT:
			n++; w = w * 10 + d; CurrentState = 2; break;
		default:
			CurrentState = ErrorState;
		}
		break;
	case 4:
		switch (symbol)
		{
		case DIGIT:
			p = p * 10 + d; CurrentState = 6; break;
		case MINUS:
			e = -1; CurrentState = 5; break;
		case PLUS:
			CurrentState = 5; break;
		default:
			CurrentState = ErrorState;
		}
		break;
	case 5:
		switch (symbol)
		{
		case DIGIT:
			p = p * 10 + d; CurrentState = 6; break;
		default:
			CurrentState = ErrorState;
		}
		break;
	case 6:
		switch (symbol)
		{
		case DIGIT:
			p = p * 10 + d; break;
		default:
			FCON = w * pow(10, e * p - n);
			CurrentState = EndState;
		}
		break;
	}
	return CurrentState;
}
int getFCON(FILE* fp, char* token, char& ch)
{
	char temp[30];
	int i = 0, j = 0, flag = 8;
	CurrentState = 0;
	while (CurrentState != EndState)
	{
		token[i] = ch; i++;
		if (ch == 'e' || ch == 'E' || ch == '.')flag = 9;
		EXCUTE(CurrentState, GetChar(ch));
		if (CurrentState == ErrorState)
		{
			return 0;
		}
		if (CurrentState != EndState)
		{
			ch = fgetc(fp);
		}
	}
	if (ch != EOF)
	{
		fseek(fp, -1, SEEK_CUR);
	}
	if (flag == 9)sprintf(temp, "%g", FCON);
	else sprintf(temp, "%d", ICON);
	strcpy(token, temp);
	return flag;
}

(3)实验结果分析

普遍示例:
在这里插入图片描述

在这里插入图片描述

错误示例:
在这里插入图片描述
在这里插入图片描述

四、扩展实验

1、 对所给算术表达式的文法G2,完成两种以上不同的语法分析程序的设计与实现。

(1)递归下降法实现

这部分参考了实验指导书的提示,按照实验指导书提供的框图进行代码的编写。如下图是递归下降法分析算术表达式的框图,其中(a)表示 ZC过程、(b)表示 E过程 、©表示 T过程、(d)表示F过程、(e)表示 ADVANCE过程。其中ZC过程和ADCANCE过程在实验一中已经实现,同样是利用MyLex中的代码实现。
在这里插入图片描述
在这里插入图片描述

程序代码如下:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<iomanip>
#include<string>
#include <cstring>
#include"MyLex.h"
using namespace std;
const int MAXSTACK = 100;
const int MAXINPUT = 300;
const int MAXS = 100;
int stack[MAXSTACK];
int sentence[MAXS];
int i = 0, k = 0, j = 0, atom = 0, newVN, step = 1;

char cmp[8][9] = 
{
//+ - * / i ( ) #
">><<<<>>",
">><<<<>>",
">>>><<>>",
">>>><<>>",
">>>>??>>",
"<<<<<<=?",
">>>>??>>",
"<<<<<<?="
};

int getChar(string word)
{
	if (word == "PL")return 0;
	if (word == "MI")return 1;
	if (word == "MU")return 2;
	if (word == "DI")return 3;
	if (word == "ID" || word == "INT" || word == "REAL")return 4;
	if (word == "LP")return 5;
	if (word == "RP")return 6;
	if (word == "#")return 7;
	//F = 8,T = 9,E = 10
}
void out()
{
	cout << "第" << step << "步" << endl;
	cout << "规约子串:";
	for (int i = j+1; i <= k; i++)
	{
		int t = stack[i];
		switch (t)
		{
		case 0 :
			cout << "+"; break;
		case 1:
			cout << "-"; break;
		case 2:
			cout << "*"; break;
		case 3:
			cout << "/"; break;
		case 4:
			cout << "i"; break;
		case 5:
			cout << "("; break;
		case 6:
			cout << ")"; break;
		case 7:
			cout << "#"; break;
		case 8:
			cout << "N"; break;
		}
	}
	cout << endl;
	step++;
}

void nowStack()
{
	cout << "输入串情况:";
	int cnt = 0;
	while (stack[cnt] != -1)
	{
		int t = stack[cnt];
		switch (t)
		{
		case 0:
			cout << "+"; break;
		case 1:
			cout << "-"; break;
		case 2:
			cout << "*"; break;
		case 3:
			cout << "/"; break;
		case 4:
			cout << "i"; break;
		case 5:
			cout << "("; break;
		case 6:
			cout << ")"; break;
		case 7:
			cout << "#"; break;
		case 8:
			cout << "N"; break;
		}
		cnt++;
	}
	cout << endl;
}

int reducion(int begin, int end, int len)
{
	int cnt = j + 2;
	out();
	while (stack[cnt] != -1)
	{
		stack[cnt] = stack[cnt + len - 1];
		stack[cnt + len - 2] = -1;
		cnt++;
	}
	if (begin == 4 && end == 4 && len == 1)
	{
		return 8;
	}
	else if (begin == 5 && end == 6 && len == 3)
	{
		return 8;
	}
	else if (begin == 8 && end == 8 && len == 3)
	{
		return 8;
	}
}

void moveIn(int t)
{
	cout << "第" << step << "步" << endl;
	cout << "移进:";
	switch (t)
	{
	case 0:
		cout << "+"; break;
	case 1:
		cout << "-"; break;
	case 2:
		cout << "*"; break;
	case 3:
		cout << "/"; break;
	case 4:
		cout << "i"; break;
	case 5:
		cout << "("; break;
	case 6:
		cout << ")"; break;
	case 7:
		cout << "#"; break;
	case 8:
		cout << "N"; break;
	}
	cout << endl;
	step++;
}

void operatorFirst(FILE *fp)
{
	memset(stack, -1, sizeof(int) * MAXSTACK);
	stack[0] = 7;
	string token;
	while (token != "#")
	{
		token = myLex(fp);
		sentence[i] = getChar(token);
		i++;
	}
	i = 0;
	do
	{
		atom = sentence[i++];
		if (atom < 8)j = k;
		else j = k - 1;
		while (cmp[stack[j]][atom] == '>')
		{
			int q;
			do
			{
				q = stack[j];
				if (stack[j - 1] < 8)j--;
				else j -= 2;
			} while (cmp[stack[j]][q] != '<');
			newVN = reducion(stack[j + 1], stack[k], k - j);
			k = j + 1;
			stack[k] = newVN;
			nowStack();
		}
		if (cmp[stack[j]][atom] == '<' || cmp[stack[j]][atom] == '=') {
			stack[++k] = atom;
			moveIn(atom);
			nowStack();
		}
		else
		{
			cout << "语法错误:error: in (" << lines << ")lines "  << " 处出现错误!" << endl;
			exit(0);
		}
	} while (atom != 7);
	cout << "RIGHT" << endl;
}

int main()
{
	char fileName[20];/*待查询的文件名*/
	cout << "请输入要进行词法分析的文件名:";
	cin >> fileName;
	FILE* fp = fopen(fileName, "r");
	operatorFirst(fp);
	fclose(fp);
}

正确示例:
 
 
错误示例:
 
 
(2)算符优先法实现
对于G2[E]文法进行拆分:
S→#E#
E→E+T
E→E-T
E→T
T→T*F
T→T/F
T→F
F→(E)
F→i
非终结符有:S、E、T、F
构造FIRSTVT集合
FIRSTVT(S) = {“#”}
FIRSTVT(E) = {+” “-” “*” “/” “(” “i”}
FIRSTVT(T) = {*” “/” “(” “i”}
FIRSTVT(F) = {(” “i”}
构造LASTVT集合
LASTVE(S) = {“#”}
LASTVE(E) = {+” “-” “*” “/” “i” “)}
LASTVE(T) = {*” “/” “i” “)}
LASTVE(F) = { “i” “)}
构造算法优先分析表。
	+	-	*	/	i	(	)	#
+	>	>	<	<	<	<	>	>
-	>	>	<	<	<	<	>	>
*	>	>	>	>	<	<	>	>
/	>	>	>	>	<	<	>	>
i	>	>	>	>			>	>
(	<	<	<	<	<	<	=	
)	>	>	>	>			>	>
#	<	<	<	<	<	<		=
mylex与上面程序相同,下面中非终结符全部由N代替进行归约。
程序代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<iomanip>
#include<string>
#include <cstring>
#include"MyLex.h"
using namespace std;
const int MAXSTACK = 100;
const int MAXINPUT = 300;
const int MAXS = 100;
int stack[MAXSTACK];
int sentence[MAXS];
int i = 0, k = 0, j = 0, atom = 0, newVN, step = 1;

char cmp[8][9] = 
{
//+ - * / i ( ) #
">><<<<>>",
">><<<<>>",
">>>><<>>",
">>>><<>>",
">>>>??>>",
"<<<<<<=?",
">>>>??>>",
"<<<<<<?="
};

int getChar(string word)
{
	if (word == "PL")return 0;
	if (word == "MI")return 1;
	if (word == "MU")return 2;
	if (word == "DI")return 3;
	if (word == "ID" || word == "INT" || word == "REAL")return 4;
	if (word == "LP")return 5;
	if (word == "RP")return 6;
	if (word == "#")return 7;
	//F = 8,T = 9,E = 10
}
void out()
{
	cout << "第" << step << "步" << endl;
	cout << "规约子串:";
	for (int i = j+1; i <= k; i++)
	{
		int t = stack[i];
		switch (t)
		{
		case 0 :
			cout << "+"; break;
		case 1:
			cout << "-"; break;
		case 2:
			cout << "*"; break;
		case 3:
			cout << "/"; break;
		case 4:
			cout << "i"; break;
		case 5:
			cout << "("; break;
		case 6:
			cout << ")"; break;
		case 7:
			cout << "#"; break;
		case 8:
			cout << "N"; break;
		}
	}
	cout << endl;
	step++;
}

void nowStack()
{
	cout << "输入串情况:";
	int cnt = 0;
	while (stack[cnt] != -1)
	{
		int t = stack[cnt];
		switch (t)
		{
		case 0:
			cout << "+"; break;
		case 1:
			cout << "-"; break;
		case 2:
			cout << "*"; break;
		case 3:
			cout << "/"; break;
		case 4:
			cout << "i"; break;
		case 5:
			cout << "("; break;
		case 6:
			cout << ")"; break;
		case 7:
			cout << "#"; break;
		case 8:
			cout << "N"; break;
		}
		cnt++;
	}
	cout << endl;
}

int reducion(int begin, int end, int len)
{
	int cnt = j + 2;
	out();
	while (stack[cnt] != -1)
	{
		stack[cnt] = stack[cnt + len - 1];
		stack[cnt + len - 2] = -1;
		cnt++;
	}
	if (begin == 4 && end == 4 && len == 1)
	{
		return 8;
	}
	else if (begin == 5 && end == 6 && len == 3)
	{
		return 8;
	}
	else if (begin == 8 && end == 8 && len == 3)
	{
		return 8;
	}
}

void moveIn(int t)
{
	cout << "第" << step << "步" << endl;
	cout << "移进:";
	switch (t)
	{
	case 0:
		cout << "+"; break;
	case 1:
		cout << "-"; break;
	case 2:
		cout << "*"; break;
	case 3:
		cout << "/"; break;
	case 4:
		cout << "i"; break;
	case 5:
		cout << "("; break;
	case 6:
		cout << ")"; break;
	case 7:
		cout << "#"; break;
	case 8:
		cout << "N"; break;
	}
	cout << endl;
	step++;
}

void operatorFirst(FILE *fp)
{
	memset(stack, -1, sizeof(int) * MAXSTACK);
	stack[0] = 7;
	string token;
	while (token != "#")
	{
		token = myLex(fp);
		sentence[i] = getChar(token);
		i++;
	}
	i = 0;
	do
	{
		atom = sentence[i++];
		if (atom < 8)j = k;
		else j = k - 1;
		while (cmp[stack[j]][atom] == '>')
		{
			int q;
			do
			{
				q = stack[j];
				if (stack[j - 1] < 8)j--;
				else j -= 2;
			} while (cmp[stack[j]][q] != '<');
			newVN = reducion(stack[j + 1], stack[k], k - j);
			k = j + 1;
			stack[k] = newVN;
			nowStack();
		}
		if (cmp[stack[j]][atom] == '<' || cmp[stack[j]][atom] == '=') {
			stack[++k] = atom;
			moveIn(atom);
			nowStack();
		}
		else
		{
			cout << "语法错误:error: in (" << lines << ")lines "  << " 处出现错误!" << endl;
			exit(0);
		}
	} while (atom != 7);
	cout << "RIGHT" << endl;
}

int main()
{
	char fileName[20];/*待查询的文件名*/
	cout << "请输入要进行词法分析的文件名:";
	cin >> fileName;
	FILE* fp = fopen(fileName, "r");
	operatorFirst(fp);
	fclose(fp);
}

正确示例:
在这里插入图片描述在这里插入图片描述在这里插入图片描述

错误示例:
在这里插入图片描述

在这里插入图片描述

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值