P4911 河童重工的计算机 题解

· · 题解

Update at June 30th

这道题目最重要的就是实现指令的操作

然后实现操作离不开变量与内存系统

所以我们必须先实现内存系统,然后才能够进行下一步的工作。

=====正文分割线=====

1.内存系统

内存系统中包含寄存器,这个相对好模拟,直接使用变量保存即可

还包含许多内存单元,而且有地址,这时我们用一个数组来充当就行了

具体来说,在访问变量时我们要判断是什么类型的变量:

有一些细节(比如编译期常量 #line)需要注意去实现,可参见代码。

2.指令系统

指令还行,按照题目意思去模拟即可,算术指令代码量大,好在思维难度不高

判断第一个字符串就可以知道是什么类型的指令了,然后按照上面所说的方式去获取操作数

另外:

注意判断操作数个数

注意判断操作数个数!

注意判断操作数个数!!!

很容易在这里卡住(事实上我就是这样),因为如果操作数不够要把运行结果放到 %val 寄存器当中。

然后比较难的就是调用 call跳转 jmp 指令

怎么办呢?

其实可以使用一个额外的变量 line 来代表“当前运行到程序的哪一行”,这样就顺手解决了 #line 常量的问题。

call 指令还需要实现一个调用栈

什么?不知道什么是调用栈???

。。。。。。看过来,下面是简单的解释:

在一个线程中(可能不是很严谨,望包涵),同一时刻只能有一个函数在运行

也就是 CPU 控制权是在这个函数的栈帧上的,

然后呢,这个函数运行完之后,CPU 控制权会回到这个函数的调用者身上

所以说,每调用一层函数,控制权就会到这个函数身上;

然后调用完,回到调用者身上

这不就是一个先进后出的栈吗?

在本道题中就是使用栈保存当前 line 的值

每次返回都弹出栈顶

总之,调用 call 时调用栈 push;返回时调用栈 pop,解决。

3. 读取系统

读取时我们可以直接忽略注释

当检测到没有闭合的中括号 [] 时就只读入字符不送进指令流

在专业的代码编译 / 解释器中(比如浏览器的 HTML 解析),运行方式如下:

本道题我们实现的就是一个虚拟机,执行字节码(bytecode)

我们忽略所有的空白,包括注释在内,运行时就不用去特判了

其实如果开发过语言解释器或者编译器的话,相对比较容易理解。

(但是开发解释器真的很不容易……亲身经历)

=====分割线=====

关键代码如下,想找完整源代码可以戳剪贴板传送门

//这是一个运行指令的函数
...
    String type = tokens[0];
    if(type == "set") {
        setval(tokens[1], expression(tokens[2]));
        line++;
    }
    else if(type == "rint") {
        int x;
        cin >> x;
        if(opcnt == 1) setval("%val", x);
        else setval(tokens[1], x);
        line++;
    }
    else if(type == "rch") {
        char x;
        cin >> x;
        if(opcnt == 1) setval("%val", x);
        else setval(tokens[1], x);
        line++;
    }
    else if(type == "wint") {
        if(opcnt == 1) cout << expression("%val");
        else cout << expression(tokens[1]);
        line++;
    }
    else if(type == "wch") {
        if(opcnt == 1) cout << (char)expression("%val");
        else cout << (char)expression(tokens[1]);
        line++;
    }
    else if(type == "Warfarin") {
        cout << "っ!おい!何をしている……" << endl;
        line++;
    }
    else if(type == "Ptilopsis") {
        cout << "エラー発生。" << endl;
        line++;
    }
    else if(type == "inv") {
        int ans = !expression(tokens[1]); 
        if(opcnt == 2) setval("%val", ans);
        else setval(tokens[2], ans);
        line++;
    }
    else if(type == "add") {
        int ans = expression(tokens[1]) + expression(tokens[2]); 
        if(opcnt == 3) setval("%val", ans);
        else setval(tokens[3], ans);
        line++;
    }
    else if(type == "sub") {
        int ans = expression(tokens[1]) - expression(tokens[2]); 
        if(opcnt == 3) setval("%val", ans);
        else setval(tokens[3], ans);
        line++;
    }
    else if(type == "mult") {
        int ans = expression(tokens[1]) * expression(tokens[2]); 
        if(opcnt == 3) setval("%val", ans);
        else setval(tokens[3], ans);
        line++;
    }
    else if(type == "idiv") {
        int ans = expression(tokens[1]) / expression(tokens[2]); 
        if(opcnt == 3) setval("%val", ans);
        else setval(tokens[3], ans);
        line++;
    }
    else if(type == "mod") {
        int ans = expression(tokens[1]) % expression(tokens[2]); 
        if(opcnt == 3) setval("%val", ans);
        else setval(tokens[3], ans);
        line++;
    }
    else if(type == "lsft") {
        int ans = expression(tokens[1]) << expression(tokens[2]); 
        if(opcnt == 3) setval("%val", ans);
        else setval(tokens[3], ans);
        line++;
    }
    else if(type == "rsft") {
        int ans = expression(tokens[1]) >> expression(tokens[2]); 
        if(opcnt == 3) setval("%val", ans);
        else setval(tokens[3], ans);
        line++;
    }
    else if(type == "band") {
        int ans = expression(tokens[1]) & expression(tokens[2]); 
        if(opcnt == 3) setval("%val", ans);
        else setval(tokens[3], ans);
        line++;
    }
    else if(type == "bor") {
        int ans = expression(tokens[1]) | expression(tokens[2]); 
        if(opcnt == 3) setval("%val", ans);
        else setval(tokens[3], ans);
        line++;
    }
    else if(type == "bxor") {
        int ans = expression(tokens[1]) xor expression(tokens[2]); 
        if(opcnt == 3) setval("%val", ans);
        else setval(tokens[3], ans);
        line++;
    }
    else if(type == "lgr") {
        int ans = expression(tokens[1]) > expression(tokens[2]); 
        if(opcnt == 3) setval("%flag", ans);
        else setval(tokens[3], ans);
        line++;
    }
    else if(type == "lls") {
        int ans = expression(tokens[1]) < expression(tokens[2]); 
        if(opcnt == 3) setval("%flag", ans);
        else setval(tokens[3], ans);
        line++;
    }
    else if(type == "lge") {
        int ans = expression(tokens[1]) >= expression(tokens[2]); 
        if(opcnt == 3) setval("%flag", ans);
        else setval(tokens[3], ans);
        line++;
    }
    else if(type == "lle") {
        int ans = expression(tokens[1]) <= expression(tokens[2]); 
        if(opcnt == 3) setval("%flag", ans);
        else setval(tokens[3], ans);
        line++;
    }
    else if(type == "leql") {
        int ans = expression(tokens[1]) == expression(tokens[2]); 
        if(opcnt == 3) setval("%flag", ans);
        else setval(tokens[3], ans);
        line++;
    }
    else if(type == "land") {
        int ans = expression(tokens[1]) && expression(tokens[2]); 
        if(opcnt == 3) setval("%flag", ans);
        else setval(tokens[3], ans);
        line++;
    }
    else if(type == "lor") {
        int ans = expression(tokens[1]) || expression(tokens[2]); 
        if(opcnt == 3) setval("%flag", ans);
        else setval(tokens[3], ans);
        line++;
    }
    else if(type == "callfunc") {
        String func = CUT(tokens[1]);
        callFunc(func);
    }
    else if(type == "jmp") {
        line = funcline[callstack.top()];
        line = line + expression(tokens[1]);
    }
    else if(type == "jif") {
        bool condition;
        if(opcnt == 2) condition = getval("%flag");
        else condition = expression(tokens[2]);
        if(condition) {
            line = funcline[callstack.top()];
            line = line + expression(tokens[1]);
        }
        else line++;
    }
    else if(type == "ret") {
        if(opcnt == 2) setval("%ret", expression(tokens[1]));
        return false;
    } 
    else if(type == "hlt") {
        exit(0);
        line++;
    }
    else line++;
...

最后,给那些想了解解释器、编译器、虚拟机等的oier们安利一下WarfarinBloodanger 博客哦

求通过~