我该如何实现一种简单的基于堆栈的编程语言呢



我有兴趣通过实现基于堆栈的编程语言来扩展我的计算机编程知识。我正在寻求从哪里开始的建议,因为我希望它具有像"pushint 1"这样的函数,它可以将值为1的整数推到堆栈顶部,并通过"L01: jump L01:"这样的标签进行流控制。

到目前为止,我已经制作了一个C#实现,实现了我希望我的语言的行为(我想链接到它,但IDEOne被阻止了),但它非常混乱,需要优化。它将输入转换为XML,然后对其进行解析。我的目标是使用较低级别的语言(也许是C/C++),但我的问题是实现一个可以容纳各种数据类型并且没有固定大小的堆栈。

最后,我还想实现数组和函数。此外,我认为我需要一个更好的Lexer,我想知道对于这样一种简单的语言,解析树是否是一个好主意。

欢迎任何建议/批评,请考虑到我对编程还是相当陌生的(我最近刚刚完成AP CompSci I)。此外,还欢迎链接到基于堆栈的开源语言。

下面是一个我想尝试解释/编译的基本程序(其中[this is a comment]):

[Hello World!]
pushchar    'n'
pushstring  "Hello World!"
print
[Count to 5 and then count down!]
pushint     1
setlocal    0
L01:
pushchar    'n'
getlocal    0
print           [print x + 'n']
getlocal    0
increment
setlocal    0   [x = x + 1]
pushint     5
getlocal    0
lessthan        [x < 5]
iftrue      L01
L02:
pushchar    'n'
getlocal    0
print           [print x + 'n']
getlocal    0
decrement
setlocal    0   [x = x - 1]
pushint     0
getlocal    0
greaterthan     [x > 0]
iftrue      L02

预期输出为:

Hello World!
1
2
3
4
5
4
3
2
1

Factor等基于堆栈的语言具有以下语法:

2 3 + 5 - print

这相当于以下C风格的代码:

print(2 + 3 - 5);

使用基于堆栈的语言的优点是实现起来很简单。此外,如果该语言像大多数基于堆栈的语言一样使用反向抛光表示法,那么您的语言前端所需要的就是lexer。您不需要将令牌解析到语法树中,因为只有一种方法可以解码令牌流。

您试图创建的不是基于堆栈的编程语言,而是基于堆栈的虚拟机。应用程序虚拟机可以是基于堆栈的,也可以是基于寄存器的。例如,Java虚拟机是基于堆栈的。它执行Java字节码(这就是您为虚拟机创建的字节码)。然而,编译到此字节码的编程语言(例如Java、Erlang、Groovy等)不是基于堆栈的。

你试图创建的就像你自己的虚拟机的汇编级语言,它恰好是基于堆栈的。话虽如此,这将相当容易做到——基于堆栈的虚拟机更容易实现基于寄存器的虚拟机。同样,您所需要的只是一个lexer,比如flex。下面是一个JavaScript中使用lexer库的小例子:

var program = "[print(2 + 3)]";
program += "n push 2";
program += "n push 3";
program += "n add";
program += "n print";
lexer.setInput(program);
var token;
var stack = [];
var push = false;
while (token = lexer.lex()) {
switch (token) {
case "NUMBER":
if (push) stack.push(lexer.yytext);
else alert("Unexpected number.");
break;
case "ADD":
if (push) alert("Expected number.");
else stack.push(stack.pop() + stack.pop());
break;
case "PRINT":
if (push) alert("Expected number.");
else alert(stack.pop());
break;
}
push = token === "PUSH";
}
<script src="https://rawgit.com/aaditmshah/lexer/master/lexer.js"></script>
<script>
var lexer = new Lexer;
lexer.addRule(/s+/, function () {
// matched whitespace - discard it
});
lexer.addRule(/[.*]/, function () {
// matched a comment - discard it
});
lexer.addRule(/d+/, function (lexeme) {
this.yytext = parseInt(lexeme);
return "NUMBER";
});
lexer.addRule(/push/, function () {
return "PUSH";
});
lexer.addRule(/add/, function () {
return "ADD";
});
lexer.addRule(/print/, function () {
return "PRINT";
});
</script>

这真的很简单。你可以随意修改这个程序,并根据自己的需要进行修改。祝你好运。

我想你会发现一篇关于"MetaII"的论文真的很有启发性。它展示了如何在10页简短但令人费解的页面中定义下推堆栈编译机及其编译器。请参阅以下答案:https://stackoverflow.com/a/1005680/120163一旦您理解了这一点,编写下推堆栈解释器将永远变得简单。

最新更新