【编译原理】基于算符优先分析法的语法分析器

目录

一、实验题目

二、分析与设计

三、源代码


一、实验题目

编写识别由下列文法所定义的表达式的预测分析程序。

 E+T | E-T | T

à T*F | T/F | F

à (E) | i

输入:从键盘输入表达式,或每行含有一个表达式的文本文件。其中,表达式中含有任意的十进制数或十六进制数,并以#结束,如:80-5H+(6+1)+4h/2#。

输出:分析成功或不成功信息。

二、分析与设计

(1)   分析

a)        判断文法是否为OPG文法

答:因为在该文法中,任意产生式都不包含两个相邻的非终结符,且任意两个算符之间只存在一种优先关系,因此该文法是OPG文法。

b)       求FIRSTVT与LASTVT集

                                         

 

c)        构造算符优先关系表

+

-

*

/

i

(

)

#

+

>

>

<

<

<

<

>

>

-

>

>

<

<

<

<

>

>

*

>

>

>

>

<

<

>

>

/

>

>

>

>

<

<

>

>

i

>

>

>

>

>

>

(

<

<

<

<

<

<

=

)

>

>

>

>

>

>

#

<

<

<

<

<

<

=

(2)   设计

a)        程序总体结构框图

 

b)       主要数据结构及实现

  进行算符优先分析程序的一个重要的数据结构是分析栈,主要实现存取的数据先进后出的功能,分析栈使用双向链表来实现,并定义了相应的出栈和入栈操作。利用二维数组table[8][8]来实现算符优先关系表,行和列分别对应于各个算符,表中数据表示在行和列上的两个元素的关系:大于关系为1,等于关系为0,小于关系为-1,出错为9。

c)        主要程序模块流程图

(3)   编码实现

(4)   上机调试

三、源代码

#include <iostream>
#include<cstring>
#include<string>
#include "conio.h"
#include <vector>
using namespace std;

/*词法分析
  输入:包含十进制和十六进制的表达式
  输出:标准形式的表达式
  */

enum type { digit, Hh, AF, letter, oparetors };

char* q;//指向输入符号串中当前的字符
char word[20];//存储当前识别的单词
int state = 0;//表示所处的状态,初始状态为0
int i;//单词的下标
int j = 0;//输出数组out数组的下标
int wordErro = 1;//词法分析是否正确的标志,1:正确;0:错误
char line[30];  //保存读入的一行表达式
char out[30];   //保存词法分析后带i的表达式

int isDigitOrChar(char ch) {
	if (ch >= 48 && ch <= 57)   //数字
		return digit;
	else if (ch == 72 || ch == 104)
		return Hh;              //Hh
	else if ((ch >= 65 && ch <= 70) || (ch >= 97 && ch <= 102))//ABCDEF或者abcdef
		return AF;
	else if ((ch >= 65 && ch <= 90) || (ch >= 97 && ch <= 122))//除A-F外的其他字母
		return letter;
	else if (ch == 40 || ch == 41 || ch == 42 || ch == 43 || ch == 45 || ch == 47 || ch == 35)//(、)、*、+、-、/ 、# 这六个运算符
		return oparetors;
}

//词法分析
int toExpress(char* words, char* out) {
	wordErro = 1;
	state = 0;
	i = 0;
	j = 0;
	printf("词法分析的结果为:");
	q = words;
	while (*q) {
		switch (state) {
		case 0:   //当前为0状态
			switch (isDigitOrChar(*q)) {
			case digit://数字
				word[i++] = *q;
				state = 2;
				break;
			case Hh:           //Hh
			case AF:           //ABCDEF或者abcdef
			case letter:       //除A-F外的其他字母
				word[i++] = *q;
				state = 1;
				break;
			case oparetors:        //运算符
				word[i++] = *q;
				printf("%c", word[0]);
				out[j++] = word[0];
				state = 0;
				memset(word, 0, sizeof(word));
				i = 0;
				break;
			default:           //其他字符(非法)
				word[i++] = *q;
				state = 5;     //转到出错状态
				break;
			}
			break;
		case 1:   //当前为1状态
			switch (isDigitOrChar(*q)) {
			case digit://数字
				word[i++] = *q;
				state = 1;
				break;
			case Hh:           //Hh
			case AF:           //ABCDEF或者abcdef
			case letter:       //除A-F外的其他字母
				word[i++] = *q;
				state = 1;
				break;
			case oparetors:        //运算符,1状态可以为标识符的结束状态
				word[i++] = *q;
				state = 0;
				printf("i");
				out[j++] = 'i';
				for (int m = 0;m < sizeof(word);m++) {
					if (isDigitOrChar(word[m]) == oparetors) {
						printf("%c", word[m]);
						out[j++] = word[m];
					}
				}
				memset(word, 0, sizeof(word));
				i = 0;
				break;
			default:           //其他字符(非法)
				word[i++] = *q;
				state = 5;     //转到出错状态
				break;
			}
			break;
		case 2:   //当前为2状态
			switch (isDigitOrChar(*q)) {
			case digit://数字
				word[i++] = *q;
				state = 2;
				break;
			case Hh:           //Hh
				word[i++] = *q;
				state = 3;
				break;
			case AF:           //ABCDEF或者abcdef
				word[i++] = *q;
				state = 4;
				break;
			case oparetors:        //运算符,2状态可以为十进制整数的结束状态
				word[i++] = *q;
				state = 0;
				printf("i");
				out[j++] = 'i';
				for (int m = 0;m < sizeof(word);m++) {
					if (isDigitOrChar(word[m]) == oparetors) {
						printf("%c", word[m]);
						out[j++] = word[m];
					}
				}
				memset(word, 0, sizeof(word));
				i = 0;
				break;
			default:           //其他字符(非法)
				word[i++] = *q;
				state = 5;     //转到出错状态
				break;
			}
			break;
		case 3:   //当前为3状态
			switch (isDigitOrChar(*q)) {
			case oparetors:        //运算符,3状态可以为十六进制整数的结束状态
				word[i++] = *q;
				state = 0;
				printf("i");
				out[j++] = 'i';
				for (int m = 0;m < sizeof(word);m++) {
					if (isDigitOrChar(word[m]) == oparetors) {
						printf("%c", word[m]);
						out[j++] = word[m];
					}
				}
				memset(word, 0, sizeof(word));
				i = 0;
				break;
			default:           //其他字符(非法)
				word[i++] = *q;
				state = 5;
				break;
			}
			break;
		case 4:   //当前为4状态
			switch (isDigitOrChar(*q)) {
			case digit://数字
				word[i++] = *q;
				state = 4;
				break;
			case Hh:           //Hh
				word[i++] = *q;
				state = 3;
				break;
			case AF:           //ABCDEF或者abcdef
				word[i++] = *q;
				state = 4;
				break;
			default:           //其他字符(非法)
				word[i++] = *q;
				state = 5;     //转到出错状态
				break;
			}
			break;
		case 5:   //出错状态
			switch (isDigitOrChar(*q)) {
			case oparetors:        //运算符
				word[i++] = *q;
				state = 0;
				memset(word, 0, sizeof(word));
				i = 0;
				wordErro = 0;
				break;
			default:           //其他字符(非法)
				word[i++] = *q;
				state = 5;     //转到出错状态
				break;
			}
			break;
		}
		q++;     //指针下移(指向输入符号串的下一个字符)
	}
	if (wordErro == 1) {
		printf("\n》》》词法正确!《《《\n下面进行语法分析......\n");
	}
	else {
		printf("\n》》》词法错误!《《《\n");
	}

	return 0;
}


/*
  读入由i和运算符组成的式子,
  判断语法是否合法
*/

char sym;  //保存输入的字符
int cur;  //表达式字符串的当前下标
char cmpchar;//出栈的字符
char current;//输入串中当前分析的字符
int error;  //语法分析的错误标志  0:正确  -1:错误

//定义分析栈的数据结构,双向链表
typedef struct node {
	char data;
	struct node* before;
	struct node* next;
}*SNode;
SNode temp, top;

//定义入栈操作
void push(char c) {
	temp = (SNode)malloc(sizeof(node));//申请内存空间
	temp->data = c;
	temp->before = top;
	temp->next = NULL;
	top->next = temp;
	top = temp;
}

//定义出栈操作
char pop() {
	cmpchar = top->data;
	if (top->before != NULL) {
		temp = top;
        top = top->before;
		top->next = NULL;
		free(temp);
	}
	return cmpchar;
}

// 分析表结构如下:
// 存储算符优先关系表
// 大于关系为1,等于关系为0,小于关系为-1,出错为9
					      //+, -, *,  /,  (,  ),  i, #
int table[8][8] ={ /* + */{ 1, 1, -1, -1, -1, 1, -1, 1},
                   /* - */{ 1, 1, -1, -1, -1, 1, -1, 1},
                   /* * */{ 1, 1,  1,  1, -1, 1, -1, 1},
	               /* / */{ 1, 1,  1,  1, -1, 1, -1, 1},
	               /* ( */{-1,-1, -1, -1, -1, 0, -1, 9},
                   /* ) */{ 1, 1,  1,  1,  9, 1,  9, 1},
                   /* i */{ 1, 1,  1,  1,  9, 1,  9, 1},
                   /* # */{-1,-1, -1, -1, -1, 9, -1, 0} 
                 };

//将字符数字化
int Vt2d(char current) {
	switch (current) {
	case '+':j = 0;break;
	case '-':j = 1;break;
	case '*':j = 2;break;
	case '/':j = 3;break;
	case '(':j = 4;break;
	case ')':j = 5;break;
	case 'i':j = 6;break;
	case '#':j = 7;
	}
	return j;
}

//判断是否是终结符
bool isVt(char ch) {
	if (isDigitOrChar(ch) == oparetors || ch == 'i') return true;
	else return false;
}


//读入当前字符
char read(string line, int k) {
	return line[k];
}

void showMess(int l, string line) {

	node* t = top;
	vector<char> v;
	while (t) {
		v.push_back(t->data);
		t = t->before;
	}

	string str;
	for (int i = v.size() - 1; i >= 0; i--) {
		str += v[i];
	}
	cout << str;

	cout << "\t\t";
	for (int i = l; i < line.size(); i++) {
		cout << line[i];
	}
	cout << "\t\t";

}


//语法分析
//输入:符号串line,以‘#’结束
//输出:error=0:语法正确  error=-1:语法错误
void gramAnalysis(char line[]) {
	int i, j;
	push('#');
	int k = 1;
	error = 0;//初始化为正确
	for (int l = 0; ; l++) {
		showMess(l, line);

		// 退出条件
		if (top->data == 'N' && top->before->data == '#' && line[l] == '#') break;

		node* sktemp = top;
		char sk = top->data;
		char a = line[l];
		// 任何两终结符之间最多只有一非终结符,若非终结符往前寻找一位即可
		if (!isVt(sk)) {
			sk = top->before->data;
			sktemp = top->before;
		}
		i = Vt2d(sk); // 获取栈顶终结符
		j = Vt2d(a); // 获取当前输入符号
		switch (table[i][j]) {
		case 9:  // 语法错误
			error = -1;
			break;
		case 0:
			cout << "=" << "\t\t" << "移进" << endl;
			push(a);
			break;
		case -1:  // 小于,移进
			cout << "<" << "\t\t" << "移进" << endl;
			push(a);
			break;
		case 1:   // 大于,归约
			cout << ">" << "\t\t" << "归约" << endl;
			node* Q = sktemp;
			node* sj = sktemp;
			do {
				sj = Q;
				if (isVt(Q->before->data)) Q = Q->before;
				else Q = Q->before->before;
			} while (table[Vt2d(Q->data)][Vt2d(sj->data)] == 0);
			int idx = 0;
			while (top != Q) {
				char c = pop();
				idx++;
			}
			if (idx % 2 == 0) {
				error = -1;
				break;
			}
			push('N');
			l--;
			break;
		}
		if (error == -1) break;
	}
}

/*
   主分析程序,先词法分析,
   若正确则进行语法分析
*/

int main() {
	FILE* fp;
	//打开文本文件
	fopen_s(&fp, "C:/Users/wzy/Desktop/编译原理实验4 -测试用例-test.txt", "r");
	if (fp == NULL) {
		printf("open file error!\n");
		getchar();
		return -1;
	}
	//逐行读入表达式,并分析是否符合语法规则
	while (!feof(fp)) {
		printf("------------------------------------------\n");
		strcpy_s(line, "\0");
		fscanf_s(fp, "%s", &line, 30); //逐行读
		cur = 0;
		error = 0;
		printf("输入字符串为:%s \n", line);
		char inputString[30] = { 0 };//中间变量,存储输入的字符串
		for (int i = 0;i < sizeof(line);i++) {
			inputString[i] = line[i];
		}
		//进行词法分析
		toExpress(line, out);
		memset(line, 0, sizeof(line));
		for (int i = 0;i < sizeof(line);i++) {
			line[i] = out[i];
		}
		if (wordErro == 1) {//若词法正确,则进行语法分析
			sym = read(line, cur);//读入当前字符
			//初始化指针
			top = NULL;
			top = (node*)malloc(sizeof(node));
			top->before = NULL;
			top->next = NULL;
			top->data = ' ';
			gramAnalysis(line);//开始语法分析
			//如果表达式语法分析出错
			if (error == -1) {
				printf("\n》》》语法错误!《《《\n分析结果:%s为非法字符串\n", inputString);
				printf("------------------------------------------\n");
			}
			//如果表达式合法
			else if (error == 0) {
				printf("\n》》》语法正确!《《《\n分析结果:%s为合法字符串\n", inputString);
				printf("------------------------------------------\n");
			}
			memset(line, 0, sizeof(line));
			memset(out, 0, sizeof(out));
			wordErro = 1;
		}
		else {
			printf("》》》不进行语法分析!!《《《\n分析结果:%s为非法字符串\n", inputString);
			printf("------------------------------------------\n");
			continue;
		}
	}
	fclose(fp);
	getchar();
	return 0;
}

测试用例

80+5eH+(6+1h)*2+4h#
6+(5+2))*5+80bh#
95eah+3*(5+10)+35h#
9*6+(5+2)*5+80bh#
59h+((3+9ah)*3+4#
7+9*2#

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值