Cha*_* Ma 24
开始编写解释器的一个好方法是编写一个简单的机器模拟器.这是一个简单的语言,你可以写一个解释器:
该语言有一个堆栈和6条指令:
push <num> #将数字推入堆栈
pop #弹出堆栈上的第一个数字
add#弹出堆栈顶部的2个项目并将其总和推到堆栈.(记住你可以添加负数,所以你也有减法).您还可以使用其中一些其他指令创建循环.
ifeq <address>#检查堆栈的顶部,如果是0,则继续,否则,跳到<address>哪里<address>是行号
jump <address> #跳转到一个行号
print #打印堆栈顶部的值
dup #将堆栈顶部的内容复制回堆栈.
一旦编写了可以执行这些指令并执行它们的程序,您基本上就创建了一个非常简单的基于堆栈的虚拟机.由于这是一种非常低级的语言,因此您不需要了解AST是什么,如何将语法解析为AST,并将其转换为机器代码等.这对于教程项目来说太复杂了.从这开始,一旦你创建了这个小VM,你就可以开始思考如何将一些常见的构造转换成这台机器.例如,您可能想要考虑如何将C if/else语句或while循环转换为此语言.
编辑:
从下面的评论中,您可能需要更多的C经验才能解决此问题.
我建议首先了解以下主题:
然后学习更多关于string.h库的信息会很好 - strcmp,strdup - 一些有用的字符串函数.
总之,C比Python有更高的学习曲线,只是因为它是一个低层次的语言,你必须管理你自己的内存,所以这是很好首先尝试编写一个解释之前,了解一下C一些基本的东西,甚至如果你已经知道如何在python中编写一个.
小智 9
我看到这是一个有点晚的回复,但是由于当我搜索编写解释器时该线程出现在结果列表中的第二位并且没有人提到任何非常具体的内容,我将提供以下示例:
免责声明:这只是我匆忙编写的一些简单代码,以便为下面的解释奠定基础,因此并不完美,但它可以编译和运行,并且似乎给出了预期的答案。
从下到上阅读以下 C 代码:
#include <stdio.h>
#include <stdlib.h>
double expression(void);
double vars[26]; // variables
char get(void) { char c = getchar(); return c; } // get one byte
char peek(void) { char c = getchar(); ungetc(c, stdin); return c; } // peek at next byte
double number(void) { double d; scanf("%lf", &d); return d; } // read one double
void expect(char c) { // expect char c from stream
char d = get();
if (c != d) {
fprintf(stderr, "Error: Expected %c but got %c.\n", c, d);
}
}
double factor(void) { // read a factor
double f;
char c = peek();
if (c == '(') { // an expression inside parantesis?
expect('(');
f = expression();
expect(')');
} else if (c >= 'A' && c <= 'Z') { // a variable ?
expect(c);
f = vars[c - 'A'];
} else { // or, a number?
f = number();
}
return f;
}
double term(void) { // read a term
double t = factor();
while (peek() == '*' || peek() == '/') { // * or / more factors
char c = get();
if (c == '*') {
t = t * factor();
} else {
t = t / factor();
}
}
return t;
}
double expression(void) { // read an expression
double e = term();
while (peek() == '+' || peek() == '-') { // + or - more terms
char c = get();
if (c == '+') {
e = e + term();
} else {
e = e - term();
}
}
return e;
}
double statement(void) { // read a statement
double ret;
char c = peek();
if (c >= 'A' && c <= 'Z') { // variable ?
expect(c);
if (peek() == '=') { // assignment ?
expect('=');
double val = expression();
vars[c - 'A'] = val;
ret = val;
} else {
ungetc(c, stdin);
ret = expression();
}
} else {
ret = expression();
}
expect('\n');
return ret;
}
int main(void) {
printf("> "); fflush(stdout);
for (;;) {
double v = statement();
printf(" = %lf\n> ", v); fflush(stdout);
}
return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)
这是一个简单的递归下降解析器,用于支持单字母变量的基本数学表达式。运行它并输入一些语句会产生以下结果:
> (1+2)*3
= 9.000000
> A=1
= 1.000000
> B=2
= 2.000000
> C=3
= 3.000000
> (A+B)*C
= 9.000000
Run Code Online (Sandbox Code Playgroud)
您可以更改 get()、peek() 和 number() 以从文件或代码行列表中读取。您还应该创建一个函数来读取标识符(基本上是单词)。然后扩展 statement() 函数,以便能够更改它接下来运行的行以进行分支。最后你将你想要的分支操作添加到语句函数中,比如
if "condition" then
"statements"
else
"statements"
endif.
while "condition" do
"statements"
endwhile
function fac(x)
if x = 0 then
return 1
else
return x*fac(x-1)
endif
endfunction
Run Code Online (Sandbox Code Playgroud)
显然,您可以根据自己的喜好决定语法。您需要考虑定义函数的方式以及如何处理参数/参数变量、局部变量和全局变量。如果首选数组和数据结构。参考?指针。输入输出?为了处理递归函数调用,您可能需要使用堆栈。
在我看来,用 C++ 和 STL 来做这一切会更容易。例如,一个 std::map 可用于保存局部变量,而另一个映射可用于全局变量...
当然可以编写一个编译器,从代码中构建一个抽象的语法树。然后遍历这棵树以生成机器代码或在虚拟机(如 Java 和 .Net)上执行的某种字节码。与天真地逐行解析并执行它们相比,这提供了更好的性能,但在我看来,这不是在编写解释器。即编写编译器及其目标虚拟机。
如果有人想学习写一个口译员,他们应该尝试制作最基本的简单实用的工作口译员。