第2关:将赋值语句翻译为P-code代码
在编译原理的学习中,PL/0是一种非常经典的教学型语言,而P-code则是一种典型的栈式虚拟机指令。本文记录了如何使用递归下降的方法,将PL/0语言中的赋值语句翻译为相应的P-code代码,并给出了完整的实现细节。
在具体实现中,我们需要熟悉如下知识点:
- 递归下降语法分析方法:通过递归调用函数来处理语法结构。
- P-code栈式指令:典型的栈操作指令,如
LOD、STO、LIT、OPR等。 - 属性文法:基于语法规则传递信息的方法。
下面介绍具体的实现过程与代码细节。
任务目标
我们要实现将PL/0语言中的赋值语句翻译为对应的P-code。例如:
a := b + 3 * (c - 1);
对应的P-code可能如下:
lod 0 b
lit 0 3
lod 0 c
lit 0 1
opr 0 3 // 减法
opr 0 4 // 乘法
opr 0 2 // 加法
sto 0 a
栈式P-code指令说明
| 指令 | 含义 |
|---|---|
LIT 0,a |
将常量a放入栈顶 |
LOD l,a |
将层次差为l的相对地址a的变量放入栈顶 |
STO l,a |
将栈顶元素存入层次差为l的相对地址a的变量 |
OPR 0,a |
执行算术逻辑运算,a表示操作类型(如:2为加法,3为减法等) |
核心代码实现(C++版本)
在本实现中,我们使用了递归下降的翻译策略,并对PL/0中的赋值语句进行了完整的翻译处理。以下是关键函数assi_sen和expression的完整实现代码(其他函数的完整实现请见完整源码):
void assi_sen(treeNode* tn)
{
if (sym == "identifier") {
auto *idt = new treeNode(debugId);
tn->child.push_back(new treeNode("标识符"));
tn->child.back()->child.push_back(idt);
string tmp = debugId; // 保存待赋值的变量名
sym = GETSYM();
if (sym == ":=") {
tn->child.push_back(new treeNode(":="));
sym = GETSYM();
auto *expressiont = new treeNode("表达式");
tn->child.push_back(expressiont);
expression(expressiont);
// 生成赋值存储指令
int addr = findAddrInTable(tmp);
if (addr == -1) error_exc(var_undefined);
addCode("sto", abs(tables[addr].level - procedureLevel), tables[addr].addr);
} else error_exc(var_assign_wrong);
} else return;
}
void expression(treeNode* tn)
{
if (sym == "+" || sym == "-" ) {
string opr = sym;
sym = GETSYM();
if (opr == "-") {
addCode("lit", 0, 0); // 为负号准备
addCode("opr", 0, MinusOpr); // 执行取负
}
}
auto *itemt = new treeNode("项");
tn->child.push_back(itemt);
item(itemt);
while (sym == "+" || sym == "-") {
string opr = sym;
sym = GETSYM();
auto *itemts = new treeNode("项");
tn->child.push_back(itemts);
item(itemts);
// 生成加减运算指令
if (opr == "+")
addCode("opr", 0, AddOpr); // 加法
else
addCode("opr", 0, SubOpr); // 减法
}
}
符号表查找函数findAddrInTable
本函数用于在符号表中查找变量的地址:
int findAddrInTable(string &s)
{
for (int i =(int)tables.size()-1; i >= 0; --i)
if (s == tables[i].name && tables[i].level <= procedureLevel)
return i;
return -1;
}
P-code指令生成函数addCode
此函数用于向代码列表中添加新的P-code指令:
struct codeEle {
string func;
int level_d;
int offset;
};
vector<codeEle> codes;
void addCode(string str, int lev, int off)
{
codeEle coe;
coe.func = str;
coe.level_d = lev;
coe.offset = off;
codes.push_back(coe);
cx++;
}
示例运行与输出结果
假设输入为:
var a,b,c;
begin
a := b + 3 * (c - 1)
end.
输出的P-code代码如下:
目标代码序列
0: jmp 0 1
1: int 0 6
2: lod 0 4 // b
3: lit 0 3 // 常量 3
4: lod 0 5 // c
5: lit 0 1 // 常量 1
6: opr 0 3 // 减法
7: opr 0 4 // 乘法
8: opr 0 2 // 加法
9: sto 0 3 // 结果存储至 a
10: opr 0 0 // 程序结束
此结果验证了我们的递归下降翻译方法实现正确。
小结与心得
本次实践通过递归下降的方式,实现了PL/0赋值语句到P-code的完整翻译过程,不仅锻炼了对编译原理知识的理解和应用,也提高了动手实现复杂语法结构的能力。通过本文的介绍,希望能为大家的编译器学习之路提供一些有用的参考。
提示:登录后即可查看完整代码。