第2关:将赋值语句翻译为P-code代码

第2关:将赋值语句翻译为P-code代码
第2关:将赋值语句翻译为P-code代码

在编译原理的学习中,PL/0是一种非常经典的教学型语言,而P-code则是一种典型的栈式虚拟机指令。本文记录了如何使用递归下降的方法,将PL/0语言中的赋值语句翻译为相应的P-code代码,并给出了完整的实现细节。

在具体实现中,我们需要熟悉如下知识点:

  • 递归下降语法分析方法:通过递归调用函数来处理语法结构。
  • P-code栈式指令:典型的栈操作指令,如LODSTOLITOPR等。
  • 属性文法:基于语法规则传递信息的方法。

下面介绍具体的实现过程与代码细节。

任务目标

我们要实现将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_senexpression的完整实现代码(其他函数的完整实现请见完整源码):

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的完整翻译过程,不仅锻炼了对编译原理知识的理解和应用,也提高了动手实现复杂语法结构的能力。通过本文的介绍,希望能为大家的编译器学习之路提供一些有用的参考。

提示:登录后即可查看完整代码。
tengmmvp

tengmmvp

China