为C语言子集实现基于栈的虚拟机

7

大家好,我目前正在实现一种简单的编程语言以进行学习体验,但我需要一些建议。目前我正在设计我的解释器并遇到了问题。

我的语言是C语言的子集,我在堆栈解释器实现方面遇到了问题。在这种语言中,以下内容将会被编译:

somefunc ()
{
    1 + 2;
}

main ()
{
    somefunc ();
}

现在这个问题还好,但是当计算"1+2"时,结果被推到了一个栈上,然后函数返回,但是栈上仍然有一个数字,这不应该存在。我该如何解决这个问题?
我考虑过在函数调用之前保存栈的"状态",并在函数调用之后恢复该"状态"。例如,保存栈上元素的数量,然后执行函数代码,返回,然后从栈中弹出直到我们有与之前相同的元素数量(或者如果函数返回了某些东西,可能会多一个)。
有什么想法吗?感谢任何提示!
3个回答

9

好问题!我的爱好之一是为玩具语言编写编译器,因此感谢你对编程的卓越品味。

表达式语句是指语句中的代码仅为一个表达式。这意味着任何形如<expression> ;的内容都是表达式语句,包括赋值和函数调用,但不包括ifwhilereturn。任何表达式语句在结束时都会在堆栈上留下一个剩余值,应该将其丢弃。

1 + 2是一个表达式语句,但以下内容也是:

  • x = 5;
    赋值表达式会将值5留在堆栈上,因为赋值的结果是左操作数的值。语句完成后,您需要弹出未使用的值5。

  • printf("hello world!\n");
    printf()返回输出的字符数。语句结束时,您将在堆栈上留下此值,因此应该弹出它。

实际上,除非表达式的类型为void,否则每个表达式语句都会在堆栈上留下一个值。在这种情况下,您可以特殊处理void语句并且不需要在之后弹出任何内容,或者将一个假的“void”值推入堆栈中,以便始终可以弹出一个值。


很有趣,你这么命名,因为在我的AST表示中,我有一个名为“ASTStmtExpr”的节点专门用于此!我想我开始有点明白了...以下是我不确定的内容:由于这些评论回复的限制,我必须粘贴一小段代码: void Compiler::visit (const ASTStmtExpr& expr_stmt, std::shared_ptr<Function> func) { expr_stmt.expr ()->accept (*this, func); }你的意思是说,在此之后我应该添加OP_POP,对于像赋值这样的事情,我会推送一个虚拟的“Nil”对象,以便它随后被弹出? - Filip Jeremic
1
对于赋值语句,你不需要推送虚拟值,因为赋值的结果已经在栈中了。赋值只是一个使用 = 运算符的表达式,它与 +- 没有区别,除了 = 会将值赋给一个变量之外没有其他不同。否则,它的行为就像所有其他运算符一样。 - John Kugelman
1
一个 void 表达式就像调用一个返回 void 的函数。例如:abort();。但是,这可能只是一个学术上的考虑。你的示例代码没有列出函数返回类型,所以这对你来说可能根本不是问题。 - John Kugelman
是的,这对我来说不是问题,因为我的语言没有类型,即使基本类型在我的语言中也是“对象”,所以我认为这不会成为问题。谢谢回复! - Filip Jeremic

2

您需要一个更智能的解析器。当您看到一个表达式的值没有被使用时,您需要发出一个POP指令。


你好,感谢回答。我还处于编写编译器的初级阶段,我考虑过像这样进行优化,但是找不到可行的解决方案:(也许我会再试一次,再次感谢!! - Filip Jeremic

0

这是一个学习优化的重要机会。你有一个函数执行数字但整数运算,而整数运算结果根本没有被使用。

让编译器优化该函数可以减少生成和执行无用字节码的数量!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接