编译原理 回填拉链实现短路运算 代码

背景

本学期有编译原理课程设计,我定义的语言是一个类C语言。过程中遇到了布尔表达式优化处理的难题。这部分上课讲的是回填拉链法,但网上的资料也是很少。
遂自己琢磨实现,最终可以实现自定义语言的中间代码生成,使用递归下降法。

文法定义

<布尔表达式> ::= <布尔项>{|| <布尔项>}
<布尔项> ::= <布尔因子>{&& <布尔因子>}
<布尔因子> ::= <表达式>[<关系><表达式>]
<关系> ::= ==|!=|<|<=|>|>=
<表达式> ::= <项>{<加法运算符><项>}
<项> ::= <因子>{<乘法运算符><因子>}
<因子> ::= <标识符>|<常数>|'('<表达式>')‘
<WHILE语句> ::= while<布尔表达式>do<语句>
<IF语句> ::= if<布尔表达式>then<语句>[else<语句>]

规定布尔表达式存在与、或两种运算,与的优先级高于或,未设计非运算与括号。但因为包含了所有的关系运算符,仍然能够实现所有的逻辑关系。

回填拉链

回填拉链,一个是回填,一个是拉链。举例如下:

while (x < y) do
{
	x = x + 1;
}
y = y + 1;

生成的中间代码如下:

7:      (j<,x,y,9)

8:      (jp,_,_,12)

9:      (+,x,1,t5)

10:     (=,t5,_,x)

11:     (jp,_,_,7)

12:     (+,y,1,t6)

13:     (=,t6,_,y)

回填:执行完判断x < y指令之后,下一条语句的位置是不确定的,需要回填。
拉链:把需要回填while的真出口与假出口的中间代码都保存起来。

对于本例,while的真出口是x = x + 1指令,假出口是y = y + 1指令。

拉那些链

while (x < y || x > z && z != 5 || (x + 2)) do
{
	x = x + 1;
}
y = y + 1;

对于存在与、或优先级的布尔表达式文法,需要拉三条链——while真链、while假链、布尔表达式假链。

如果x <y 我们要跳到while的真出口,如果x >y && z!=5,我们也要跳到真出口。
如果y>=x不成立,我们要跳到while的假出口。
如果x<= y,我们要跳到处理y>=x的部分,这条链我们叫做布尔表达式的假出口。
这就是我们要拉的三条链。

怎么拉链

规定每个布尔因子发射两条四元式中间代码,成真如何处理,成假如何处理。
维护程序计数器PC,使每个四元式,单独对应一个程序编号。
对于每一条链,维护一个vector,存储要填入回填地址的四元式对应的PC。

对于每条语句具体属于那条链,我们要看它后面的符号是什么。
也就是说,要通过向后看一个符号来决定他是属于哪条链。

怎么回填

回填地址是下一步要执行的程序的编号。借助拉的三条链,当确定要执行的指令后,每条链填写对应的回填地址即可。

代码

以处理while语句为例,if处理方法一样。throw表示错误处理,不用理会。
v1,v2,v3分别是while真链,while假链,布尔表达式假链。

void Semantic::whileSent()
{
	vector<int> v1, v2;
	if (sym == WHILE)
	{
		pos++;
		if (sym == ZUOXIAO)
		{
			int temp_PC = progCount;
			pos++;
			boolExp(v1, v2);
			if (sym == YOUXIAO)
			{
				pos++;
				
				if (sym == DO)
				{
					for (auto it : v1)
						qvec[it].dest = to_string(progCount);
					pos++;
					sentence();
					emit("jp", "_", "_", to_string(temp_PC));
					for (auto it : v2)
						qvec[it].dest = to_string(progCount);
				}
				else
					throw(15);
			}
			else
				throw(15);
		}
		else
			throw(15);
	}
	else
		throw(15);//while error
}
void Semantic::boolExp(vector<int>& v1, vector<int>& v2)//<boolTerm>{OR <boolTerm>}
{
	vector<int> v3;
	boolTerm(v1, v2, v3);
	for (auto it : v3)
		qvec[it].dest = to_string(progCount);
	while (sym == OR)
	{
		pos++;
		v3.clear();
		boolTerm(v1, v2, v3);
		for (auto it : v3)
			qvec[it].dest = to_string(progCount);
	}
	if (sym == YOUXIAO)//follow(boolExp),如果遇到了follow,不算错
	{
		return;
	}
	else
	{
		cout << __LINE__ << endl;
		throw(10);
	}
}
void Semantic::boolTerm(vector<int>& v1, vector<int>& v2, vector<int>& v3)//<boolFactor> {AND <boolFactor>}
{
	boolFactor(v1, v2, v3);
	while (sym == AND)
	{
		pos++;
		boolFactor(v1, v2, v3);
	}
	if (sym == YOUXIAO || sym == OR)//follow(boolTerm),如果遇到了follow,不算错
	{
		return;
	}
	else
	{
		cout << __LINE__ << endl;
		throw(10);
	}
}
void Semantic::boolFactor(vector<int>& v1, vector<int>& v2, vector<int>& v3)
{
	int d1, d2;
	string n1, n2;
	try
	{
		expression(n1, d1);//situation2:<expression>[<guanxi> <expression>]
		string op;
		switch (sym)
		{
		case DAYU:
			op = "j>";
			goto L;
		case XIAOYU:
			op = "j<";
			goto L;
		case DADENG:
			op = "j>=";
			goto L;
		case XIAODENG:
			op = "j<=";
			goto L;
		case DENGDENG:
			op = "j=";
			goto L;
		case BUDENG:
			op = "j!=";
		L:
			pos++;
			expression(n2, d2);
			if (sym == AND)
			{
				emit(op, n1, n2, to_string(progCount + 2));//真
				v3.push_back(progCount);
				emit("jp", "_", "_");//假
			}
			else if (sym == OR)
			{
				v1.push_back(progCount);
				emit(op, n1, n2);
				emit("jp", "_", "_", to_string(progCount + 1));

			}
			else if (sym == YOUXIAO)
			{
				v1.push_back(progCount);
				emit(op, n1, n2);
				v2.push_back(progCount);
				emit("jp", "_", "_");
			}
			else
			{
				cout << __LINE__ << endl;
				throw(10);
			}
			break;
		case AND:
			op = "jnz";
			emit(op, n1, "_", to_string(progCount + 2));//真
			v3.push_back(progCount);
			emit("jp", "_", "_");//假
			break;
		case OR:
			op = "jnz";
			v1.push_back(progCount);
			emit(op, n1, "_");
			emit("jp", "_", "_", to_string(progCount + 1));
			break;
		case YOUXIAO:
			op = "jnz";
			v1.push_back(progCount);
			emit(op, n1, "_");
			v2.push_back(progCount);
			emit("jp", "_", "_");
			break;
		default:
			throw(10);
		}
	}
	catch (int x)
	{
		throw(10);
	}
	

}

测试样例

while (x < y || x > z && z != 5 || (x + 2)) do
{
 x = x + 1;
}
y = y + 1;

输出如下:

7:      (j<,x,y,16)

8:      (jp,_,_,9)

9:      (j>,x,z,11)

10:     (jp,_,_,13)

11:     (j!=,z,5,16)

12:     (jp,_,_,13)

13:     (+,x,2,t5)

14:     (jnz,t5,_,16)

15:     (jp,_,_,19)

16:     (+,x,1,t6)

17:     (=,t6,_,x)

18:     (jp,_,_,7)

19:     (+,y,1,t7)

20:     (=,t7,_,y)
  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
⒈ 题目 分析对象的BNF定义如下: 〈算术表达式〉∷=〈项〉|〈算术表达式〉+〈项〉|〈算术表达式〉-〈项〉 〈项〉∷=〈因式〉|〈项〉*〈因式〉|〈项〉/〈因式〉 〈因式〉∷=〈变量〉│(〈算术表达式〉) 〈变量〉∷=〈字母〉 〈字母〉∷=A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z (a)总程序过程 (b) ( E过程)算术表达式处理 (c)项处理(T过程) (d)因式处理过程)(F过程) (e) (f) 图2-7-5 递归下降法分析表达式之框图 (a) ZC 过程;(b) E 过程;(c) T 过程; (d) F 过程;(e) 函数过程 SYM ;(f) 过程 ADVANCE ⒉ 算法 用递归下降法分析上述算术表达式的框图,如图2-7-5所示。这里,ZC过程为总控程序,主要完成: ⑴ 通知外界键入算术表达式; ⑵ 控制E过程分析算术表达式; ⑶ 根据分析结果之正误,分别通知外界不同的信息。 ZC过程被设计成可以分析无穷多个算术表达式。E、T和F三个过程分别对应〈算术表达式〉、〈项〉和〈因式〉三个产生式的处理。它们用到两个公共过程。一个是函数过程SYM,它负责从输入字符串ST中取出下一个字符,并存入SYM中等待分析。另一个过程ADVANCE负责剔除ST中的首字符。 算法的书写和实现也请参考课堂教学所给出的方法和实例,不一定照搬以上的框图。 ⒋ 小结 ⑴ 实习前的准备 按实习目的和要求,用PASCAL语言编写一个语法分析程序,同时考虑相应的数据结构。 ⑵ 调试 调试例子应包括符合语法规则的算术表达式,以及分析程序能够判别的若干错例。 ⑶ 输出 对于所输入的算术表达式,不论对错,都应有明确的信息告诉外界。 ⑷编写上机实习报告。
编译原理可视化四则运算编译器代码比较复杂,需要涉及到很多编译原理的知识。下面我简单介绍一下该编译器代码结构和主要实现步骤。 1. 词法分析器(Lexer) 词法分析器用于将输入的四则运算表达式转换为词法单元(Token)。代码中通常使用正则表达式来匹配输入的字符串,并将其拆分为相应的词法单元。例如,输入表达式 "2+3*4",词法分析器将其拆分为 "2"、"+"、"3"、"*"、"4" 五个词法单元。 2. 语法分析器(Parser) 语法分析器用于将词法单元转换为语法树(Syntax Tree)。代码中通常使用递归下降分析法或者LR分析法来实现语法分析器。例如,对于输入的表达式 "2+3*4",语法分析器将其转换为如下的语法树: ``` + / \ 2 * / \ 3 4 ``` 3. 语义分析器(Semantic Analyzer) 语义分析器用于检查语法树中的语义错误,并生成中间代码。例如,对于输入的表达式 "2+3*4",语义分析器将检查乘法和加法操作的类型是否匹配,并生成如下的中间代码: ``` t1 = 3 * 4 t2 = 2 + t1 ``` 4. 代码生成器(Code Generator) 代码生成器用于将中间代码转换为目标代码(通常是汇编代码或机器码)。例如,对于上述的中间代码代码生成器将其转换为如下的汇编代码: ``` MOV AX, 3 MUL 4 MOV BX, AX MOV AX, 2 ADD AX, BX ``` 5. 可视化界面 编译器还需要一个可视化界面,用于显示输入的表达式、词法单元、语法树、中间代码和目标代码等信息。通常使用图形库(如Qt或Java Swing)来实现。 以上是编译原理可视化四则运算编译器的主要实现步骤和代码结构。当然,具体实现还需要考虑很多细节问题,例如错误处理、运行时性能优化等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值