驱动代码
驱动代码功能很简单,即在解析时调用相应的解析函数。其中没有什么有趣的地方,让我们看看这部分的代码:
- /// top ::= definition | external | expression | ';'
- static void MainLoop() {
- while (1) {
- fprintf(stderr, "ready> ");
- switch (CurTok) {
- case tok_eof: return;
- case ';': getNextToken(); break; // ignore top-level semicolons.
- case tok_def: HandleDefinition(); break;
- case tok_extern: HandleExtern(); break;
- default: HandleTopLevelExpression(); break;
- }
- }
- }
这里我们忽略了分号。你也许会问,这是为什么呢?最基本的理由是:如果你在命令行输入“4 + 5”,解析器并不知道这个表达式是否结束。比如,你在下一行可能会输入“def foo…”,这时候“4 + 5”是一个完整的表达式;相反地,如果你下一行输入“* 6”,那么上面的表达式还要继续解析。所以,在解析层加入分号的解析,是用来辅助判断输入是否结束。
结论""
通过400行的代码(240行有效代码),我们完整地定义了最基本的语言,包括词法分析器,解析器,和AST树工厂。目前,我们的代码可以检测输入的代码是否具有正确的语法,比如,这里有一个简单的输入和输出:
- - ./a.out
- ready> def foo(x y) x+foo(y, 4.0);
- Parsed a function definition.
- ready> def foo(x y) x+y y;
- Parsed a function definition.
- Parsed a top-level expr
- ready> def foo(x y) x+y );
- Parsed a function definition.
- Error: unknown token when expecting an expression
- ready> extern sin(a);
- ready> Parsed an extern
- ready> ^D
- -
目前Kaleidoscope还有很多扩展空间,比如你可以定义新的AST节点,扩展语法等等。在下一章,我们将介绍如何从AST生成LLVM中间代码(Intermediate Representation,简称IR)