驱动代码

驱动代码功能很简单,即在解析时调用相应的解析函数。其中没有什么有趣的地方,让我们看看这部分的代码:

  1. /// top ::= definition | external | expression | ';'
  2. static void MainLoop() {
  3. while (1) {
  4. fprintf(stderr, "ready> ");
  5. switch (CurTok) {
  6. case tok_eof: return;
  7. case ';': getNextToken(); break; // ignore top-level semicolons.
  8. case tok_def: HandleDefinition(); break;
  9. case tok_extern: HandleExtern(); break;
  10. default: HandleTopLevelExpression(); break;
  11. }
  12. }
  13. }

这里我们忽略了分号。你也许会问,这是为什么呢?最基本的理由是:如果你在命令行输入“4 + 5”,解析器并不知道这个表达式是否结束。比如,你在下一行可能会输入“def foo…”,这时候“4 + 5”是一个完整的表达式;相反地,如果你下一行输入“* 6”,那么上面的表达式还要继续解析。所以,在解析层加入分号的解析,是用来辅助判断输入是否结束。

结论""

通过400行的代码(240行有效代码),我们完整地定义了最基本的语言,包括词法分析器,解析器,和AST树工厂。目前,我们的代码可以检测输入的代码是否具有正确的语法,比如,这里有一个简单的输入和输出:

  1. - ./a.out
  2. ready> def foo(x y) x+foo(y, 4.0);
  3. Parsed a function definition.
  4. ready> def foo(x y) x+y y;
  5. Parsed a function definition.
  6. Parsed a top-level expr
  7. ready> def foo(x y) x+y );
  8. Parsed a function definition.
  9. Error: unknown token when expecting an expression
  10. ready> extern sin(a);
  11. ready> Parsed an extern
  12. ready> ^D
  13. -

目前Kaleidoscope还有很多扩展空间,比如你可以定义新的AST节点,扩展语法等等。在下一章,我们将介绍如何从AST生成LLVM中间代码(Intermediate Representation,简称IR)