【Compile】算符优先文法分析(C++)

题目描述

掌握预测分析程序的分析、设计与实现的基本技术与一般方法。

编写识别由下列文法所定义的表达式的预测分析程序。
E → E + T ∣ E − T ∣ T E \rightarrow E+T | E-T | T EE+TETT
T → T ∗ F ∣ T / F ∣ F T \rightarrow T*F | T/F | F TTFT/FF
F → ( E ) ∣ i F \rightarrow (E) | i F(E)i
输入:从键盘输入表达式,或每行含有一个表达式的文本文件。其中,表达式中含有任意的十进制数或十六进制数,并以#结束。
如:80-5H+(6+1)+4h/2#。

题目分析

本次实验为运用编程实现算符优先文法分析(自底向上语法分析,非规范归约)。
判断给定的文法是OPG文法,随后构造出FIRSTVT和LASTVT集合,以此构造出终结符之间的优先关系,产生算法优先分析表。
为了便于编程实现,采用了双向链栈。细节问题已在代码注释给出

C++

#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <conio.h>
#include <vector>
#define digit 1 // 1数字
#define op 2 // +-*/()#
#define Hh 3 // 3Hh
#define AF 4 // 4A-F
#define letter 5 // 5其它字母
using namespace std;
const int N = 8;

typedef struct node {
	char data;
	struct node* before;
	struct node* next;
};

node* temp, * top;
string line;

char q; // 指向输入符号串中当前的字符
char word[20]; // 存储当前识别的单词
int state; // 表示所处的状态
int i; // 单词的下标

// 存储算法优先关系表
// 大于关于为1,等于关系为0,小于关系为-1,出错为9
// 顺序:+, -, *, /, i, (, ), #
int table[N][N] = {
	{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, 9, 9, 1, 1},
	{-1, -1, -1, -1, -1, -1, 0, 9},
	{1, 1, 1, 1, 9, 9, 1, 1},
	{-1, -1, -1, -1, -1, -1, 9, 0}
};

void push(char c); // 入栈
char pop(); // 出栈
int t2j(char current); // 将字符数字化
void showMess(int l, string line);
void parser(string line);
bool check_terminal(char ch); // 判断是否是终结符
int isDigitOrChar(char ch);
string change_i(string words); // 将含有十进制或十六进制数的表达式转换为用i代替的表达式


int main() {
	ifstream fin("D:/Compile_exp/exp2-test.txt");
	if (!fin.is_open()) {
		cout << "open file error." << endl;
		_getch();
		return -1;
	}

	while (getline(fin, line)) {
		string temp = line;
		line = change_i(line);
		if (line == "-1") {
			cout << temp << " is not a valid express." << endl;
			puts("--------------------------------");
			continue;
		}
		cout << "Output string is: " << line << endl;
		
		top = NULL;
		top = (node*)malloc(sizeof(node));
		top->before = NULL;
		top->next = NULL;
		top->data = ' ';

		cout << "Start parse string " << line << ":" << endl << endl;
		parser(line);
		puts("--------------------------------");
	}

	return 0;
}

void push(char c) {  // 入栈函数
	temp = (node*)malloc(sizeof(node));

	temp->data = c;
	temp->before = top;
	temp->next = NULL;

	top->next = temp;  // 双向链表
	top = temp;
}

char pop() {
	char ch = top->data;

	if (top->before != NULL) {
		temp = top;
		top = temp->before;
		temp->before = NULL;
		top->next = NULL;
		free(temp);
	}

	return ch;
}

int t2j(char current) {
	// 顺序:+, -, *, /, i, (, ), #
	int j = 0;

	switch (current) {
	case '+': j = 0; break;
	case '-': j = 1; break;
	case '*': j = 2; break;
	case '/': j = 3; break;
	case 'i': j = 4; break;
	case '(': j = 5; break;
	case ')': j = 6; break;
	case '#': j = 7;
	}

	return j;
}

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";
}

void parser(string line) {
	int i, j;
	push('#');
	bool flag = true;

	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 (!check_terminal(sk)) {
			sk = top->before->data;
			sktemp = top->before;
		}
		i = t2j(sk); // 获取栈顶终结符
		j = t2j(a); // 获取当前输入符号
		switch (table[i][j]) {
		case 9:  // 语法错误
			flag = false;
			break;
		case 0:
			cout << "=" << "\t\t" << "push in" << endl;
			push(a);
			break;
		case -1:  // 小于,移进
			cout << "<" << "\t\t" << "push in" << endl;
			push(a);
			break;
		case 1:   // 大于,归约
			cout << ">" << "\t\t" << "reduce" << endl;
			node* Q = sktemp;
			node* sj = sktemp;
			do {
				sj = Q;
				if (check_terminal(Q->before->data)) Q = Q->before;
				else Q = Q->before->before;
			} while (table[t2j(Q->data)][t2j(sj->data)] == 0);
			int idx = 0;
			while (top != Q) {
				char c = pop();
				idx++;
			}
			if (idx % 2 == 0) {  // 判非法
				flag = false;
				break;
			}
				
			push('N');
			l--;
			break;
		}
		if (!flag) break;
	}
	if (!flag) 
		cout << endl << line << " is not valid." << endl;
	else 
		cout << endl << line << " is valid." << endl;
}

int isDigitOrChar(char ch) {
	if (ch >= 48 && ch <= 57) // 数字
		return digit;
	else if (ch == 72 || ch == 104) // H or h
		return Hh;
	else if ((ch >= 65 && ch <= 70) || (ch >= 97 && ch <= 102)) // 字母A,B,C,D,E,F
		return AF;
	else if ((ch >= 65 && ch <= 90) || (ch >= 97 && ch <= 122)) // 除A~F外的其它字母
		return letter;
	else if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(' || ch == ')' || ch == '#')
		return op;
}

// 将含有十进制或十六进制数的表达式转换为用i代替的表达式
string change_i(string words) {
	memset(word, 0, sizeof word);
	state = 0;
	i = 0;
	cout << "Input string is: " << words << endl;

	string result = "";
	int cnt = 0;
	q = words[cnt++];

	while (cnt <= words.size()) {
		// 先判断状态,再判断字符
		switch (state) {
		case 0: // 0状态
			switch (isDigitOrChar(q)) {
			case digit: // 数字
				word[i++] = q;
				state = 2; // 转移到2状态
				break;
			case Hh: // H or h
			case AF: // 字母A,B,C,D,E,F or a,b,c,d,e,f
			case letter: // 字母
				word[i++] = q;
				state = 1;
				break;
			case op: // 操作符
				result += q;
				state = 0;
				break;
			default: // 其它(非法字符 )
				word[i++] = q;
				state = 5;
			}
			break;
		case 1: // 1状态
			switch (isDigitOrChar(q)) {
			case Hh: // 当前状态遇到字母、数字往下读入
			case AF:
			case digit:
			case letter:
				word[i++] = q;
				state = 1;
				break;
			case op: // 读入完毕,识别为标识符
				word[i] = '\0';
				printf("%s is an identifier.\n", word);
				//result += "i";
				memset(word, 0, sizeof word);
				i = 0;
				state = 0;
				result = "-1";
				return result;
				break;
			default:
				word[i++] = q;
				state = 5;
			}
			break;
		case 2: // 2状态
			switch (isDigitOrChar(q)) {
			case digit: // 若为数字,不改变状态往下读入
				word[i++] = q;
				state = 2;
				break;
			case Hh: // 若为Hh,转移至状态3
				word[i++] = q;
				state = 3;
				break;
			case AF: // 若为AF,则有可能是16进制,转移至状态4
				word[i++] = q;
				state = 4;
				break;
			case op: // 成功识别为整数
				word[i] = '\0';
				printf("%s is an Integer.\n", word);
				result += "i";
				result += q;
				//cout << result << endl;
				memset(word, 0, sizeof word);
				i = 0;
				state = 0;
				break;
			default:
				word[i++] = q;
				state = 5;
			}
			break;
		case 3: // 3状态
			switch (isDigitOrChar(q)) {
			case op: // 识别为16进制数
				word[i] = '\0';
				printf("%s is a Hex digit.\n", word);
				result += "i";
				result += q;
				//cout << result << endl;
				memset(word, 0, sizeof word);
				i = 0;
				state = 0;
				break;
			default:
				word[i++] = q;
				state = 5;
			}
			break;
		case 4: // 4状态
			switch (isDigitOrChar(q)) {

			case digit: // 若为数字或A~F,仍为状态4,往下读入
			case AF:
				word[i++] = q;
				state = 4;
				break;
			case Hh:
				word[i++] = q;
				state = 3;
				break;
			case op: // 如果16进制没有以h或H结尾,转移至错误状态
				state = 5;
				cnt--;
				break;
			default:
				word[i++] = q;
				state = 5;
			}
			break;
		case 5: // 出错状态
			if (isDigitOrChar(q) == op) { // 若为空格,则识别为非标识符
				word[i] = '\0';
				printf("%s is not an identifier.\n", word);
				memset(word, 0, sizeof word);
				i = 0;
				state = 0;
				result = "-1";
				return result;
			}
			else { // 出错序列还未读取完毕,往下读入
				word[i++] = q;
				q = words[cnt++];
				continue;
			}
			break;
		}
		q = words[cnt++]; // 指针下移(指向输入符号串中的下一个字符)
	}

	return result;
}

// 判断是否是终结符
bool check_terminal(char ch) {
	if (isDigitOrChar(ch) == op || ch == 'i') return true;
	else return false;
}
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
算符优先分析文法是一种工具,在编译的过程中,隶属于语法分析环节,却又与中间代码的生成息息相关,编译可以分为五个阶段:词法分析语法分析、语义分析(中间代码的生成)、代码优化、目标代码生成。语法分析是指:在词法分析基础上,将单词符号串转化为语法单位(语法范畴)(短语、子句、句子、程序段、程序),并确定整个输入串是否构成语法上正确的程序。也就是说语法分析是检验输入串的语法是否正确,注意这里的语法正确,只是简单地符合自己定义的规范,而不能检测出运行时错误,比如"X/0",空指针错误,对象未初始化等错误。在这一个实验中,我将通过算符优先分析文法这一个工具,在语法分析的时候,顺便进行语义分析,也就是识别出语法单位,同时简要的将识别出的中间代码进行计算(目标代码的生成+运行),得到相应的结果,来检验自己设计的正确性。可以说题目虽然叫做算符优先分析文法,其实却是一个贯穿了“词法分析+语法分析+语义分析+中间代码优化+目标代码生成+运行”全过程的一个极具概括性的程序。如果能将这个程序得心应手的完成出来,我相信诸位对编译原理的掌握也算是炉火纯青了。时隔将近两年再来整理自己以前写过的实验报告,还是挺有感慨的,对一件东西感兴趣,原来影响还会如此深远,还记得自己当时连续六个小时全神贯注写出的实验报告,现在看看竟然写了五六十页,核心内容也有三四十页,不觉的感慨当年充满热情的时代慢慢的竟走出许久
算符优先分析C++ 编译原理 运行环境:Visual Studio 2005 #include "SStack.h" #include <iostream> #include <string> using namespace std; class Functor { private : char ** table; string ** production; string prog;//待分析字符串 int p;//字符指针 int num;//终结符个数 int num1;//产生式个数 SStack <char> stack; public: Functor(int m,int n,char ** T,string **prod,string pr) { num=m; num1=n; table=T; production=prod; prog=pr; p=0; stack.push('$'); } void traversal() { while(p<(prog.length())) { stack.display(); cout<<prog.substr(p)<<" "; char ch; if(Getnum(stack.gettop())) { ch=stack.gettop(); } else { ch=stack.getsecond(); } switch(compare(ch,prog[p])) { case 1: case 2:stack.push(prog[p]);p++;cout<<"移入"<<endl;break; case 3:reduct();cout<<"归约"<<endl;break; } } cout<<"分析成功!"<<endl; } int Getnum(char ch) { for(int i=1;i<num;i++) { if(ch==table[i][0]) { return i; } } return 0; } int compare(char col,char row) { int c=Getnum(col); int r=Getnum(row); switch( table[c][r]) { case '>': return 3;break; case '<': return 2;break; case '=': return 1;break; default:cout<<endl<<"输入串有误,程序将终止!"<<endl;system("pause");exit(0);break; } } void reduct() { //待定 string token=""; int temp; string str=""; if(!Getnum(stack.gettop())) { token+=stack.gettop(); stack.pop(); } char ch=stack.gettop(); str+=ch; temp=Haven(str); if(temp!=-1) { token+=production[temp][0]; } else { token+=ch; } stack.pop(); bool Nover=true; while(Nover) { if(Getnum(stack.gettop())) { if(compare(stack.gettop(),ch)==2) { Nover=false; } else { ch=stack.gettop(); str=""; str+=ch; temp=Haven(str); if(temp!=-1) { token+=production[temp][0]; } else { token+=ch; } stack.pop(); } } else { token+=stack.gettop(); stack.pop(); } } string token2=""; //cout<<token<<" "; for(int i=token.length()-1;i>=0;i--) { token2+=token[i]; } //cout<<token2<<endl; if(Haven(token2)!= -1) { stack.push(production[Haven(token2)][0][0]); } else { cout<<"输入串有误!分析终止!"<<endl; system("pause"); exit(0); } } int Haven(string temp) { for(int i=0;i<num1;i++) { int j=1; while(production[i][j]!="") { if(temp==production[i][j]) { return i; } j++; } } return -1; } public: ~Functor(void) { } };
编译标志(compile flags)是在编译C++代码时传递给编译器的选项,用于指定编译器的行为和生成的可执行文件的特性。通过设置编译标志,我们可以控制编译器的优化级别、警告级别、目标平台等。 以下是一些常见的C++编译标志: 1. `-O`:优化级别标志,用于指定编译器的优化级别。常见的优化级别包括`-O0`(无优化)、`-O1`(基本优化)、`-O2`(更高级别的优化)和`-O3`(最高级别的优化)。 2. `-Wall`:警告标志,用于开启编译器的警告信息。`-Wall`会开启大部分的警告信息,帮助我们发现潜在的问题。 3. `-std`:C++标准标志,用于指定编译器使用的C++标准。例如,`-std=c++11`表示使用C++11标准进行编译。 4. `-march`:目标平台标志,用于指定生成的可执行文件的目标平台。例如,`-march=native`表示使用当前机器的最佳优化。 5. `-I`:头文件路径标志,用于指定头文件的搜索路径。例如,`-I/path/to/include`表示将`/path/to/include`添加到头文件搜索路径中。 6. `-L`:库文件路径标志,用于指定库文件的搜索路径。例如,`-L/path/to/lib`表示将`/path/to/lib`添加到库文件搜索路径中。 7. `-l`:链接库标志,用于指定需要链接的库文件。例如,`-lmylib`表示链接名为`libmylib.so`或`libmylib.a`的库文件。 8. `-D`:预处理宏定义标志,用于定义预处理宏。例如,`-DDEBUG`表示定义一个名为`DEBUG`的宏。 下面是一个示例,演示了如何使用编译标志编译C++代码: ```shell g++ -O2 -Wall -std=c++11 -I/path/to/include -L/path/to/lib -lmylib -DDEBUG main.cpp -o main ``` 这个命令将使用`g++`编译器,将`main.cpp`文件编译为可执行文件`main`,并使用`-O2`优化级别、`-Wall`警告标志、`-std=c++11`C++标准、`-I/path/to/include`头文件路径、`-L/path/to/lib`库文件路径、`-lmylib`链接库、`-DDEBUG`预处理宏定义。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodeSlogan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值