万花筒:教程介绍与词法分析器

    • 教程简介
    • 基础语言
    • Lexer
    • 第1章:Kaleidoscope语言简介及其Lexer的定义 - 这显示了我们的目标以及我们希望它执行的基本功能。为了使本教程最容易理解和破解,我们选择用C ++实现所有内容,而不是使用词法分析器和解析器生成器。LLVM显然可以很好地使用这些工具,如果您愿意,可以随意使用它。
    • 第2章:实现解析器和AST - 使用词法分析器,我们可以讨论解析技术和基本的AST构造。本教程描述了递归下降解析和运算符优先级解析。第1章或第2章中没有任何内容是特定于LLVM的,此时代码甚至不在LLVM中链接。:)
    • 第3章:LLVM IR的代码生成 - 在AST就绪的情况下,我们可以展示LLVM IR的生成是多么容易。
    • 第4章:添加JIT和优化器支持 - 因为很多人都有兴趣将LLVM用作JIT,我们将直接进入它并向您展示添加JIT支持所需的3行。LLVM在许多其他方面也很有用,但这是展示其强大功能的一种简单而“性感”的方式。:)
    • 第5章:扩展语言:控制流 - 随着语言的启动和运行,我们将展示如何使用控制流操作扩展它(if / then / else和’for’循环)。这让我们有机会谈论简单的SSA构造和控制流程。
    • 第6章:扩展语言:用户定义的操作符 - 这是一个愚蠢但有趣的章节,讨论扩展语言以让用户程序定义自己的任意一元和二元运算符(具有可赋值的优先级!)。这让我们可以构建一个重要的“语言”作为库例程。
    • 第7章:扩展语言:可变变量 - 本章讨论添加用户定义的局部变量以及赋值运算符。关于这个有趣的部分是它是多么容易和琐碎构造SSA形式LLVM:没有,LLVM并没有要求您的前端构造SSA形式!
    • 第8章:编译到目标文件 - 本章介绍如何获取LLVM IR并将其编译为目标文件。
    • 第9章:扩展语言:调试信息 - 使用控制流,函数和可变变量构建了一个不错的编程语言,我们考虑将调试信息添加到独立可执行文件所需的内容。此调试信息将允许您在Kaleidoscope函数中设置断点,打印参数变量和调用函数 - 所有这些都来自调试器!

    关于本教程的说明:我们希望您扩展语言并自行使用它。接受代码并疯狂地破解它,编译器不需要是可怕的生物 - 使用语言可以很有趣!

    本教程将使用我们称之为“ Kaleidoscope ” 的玩具语言进行说明(源自“意为美丽,形式和视图”)。Kaleidoscope是一种过程语言,允许您定义函数,使用条件,数学等。在本教程中,我们将扩展Kaleidoscope以支持if / then / else构造,for循环,用户定义的运算符,JIT使用简单的命令行界面进行编译等

    1. extern cos(arg);
    2. extern atan2(arg1 arg2);
    3. atan2(sin(.4), cos(42))

    第6章中包含了一个更有趣的例子,我们编写了一个小的Kaleidoscope应用程序,它以不同的放大倍数显示Mandelbrot Set。

    让我们深入探讨这种语言的实现!

    在实现一种语言时,首先需要的是能够处理文本文件并识别它所说的内容。传统的方法是使用“ 词法分析器 ”(又名“扫描仪”)将输入分解为“令牌”。词法分析器返回的每个标记包括标记代码和可能的一些元数据(例如,数字的数值)。首先,我们定义了可能性:

    1. /// gettok - Return the next token from standard input.
    2. static int gettok() {
    3. static int LastChar = ' ';
    4. // Skip any whitespace.
    5. LastChar = getchar();

    接下来gettok需要做的是识别标识符和特定关键字,如“def”。Kaleidoscope通过这个简单的循环实现了这一点:

    请注意,此代码在设置IdentifierStr标识符时设置“全局”。此外,由于语言关键字由相同的循环匹配,我们在这里处理它们。数值类似:

    1. if (isdigit(LastChar) || LastChar == '.') { // Number: [0-9.]+
    2. std::string NumStr;
    3. do {
    4. LastChar = getchar();
    5. } while (isdigit(LastChar) || LastChar == '.');
    6. NumVal = strtod(NumStr.c_str(), 0);
    7. }

    我们通过跳到行尾来处理注释,然后返回下一个令牌。最后,如果输入与上述情况之一不匹配,则它是像“+”这样的运算符字符或文件的结尾。这些代码使用以下代码处理:

    1. // Check for end of file. Don't eat the EOF.
    2. if (LastChar == EOF)
    3. return tok_eof;
    4. // Otherwise, just return the character as its ascii value.
    5. int ThisChar = LastChar;
    6. LastChar = getchar();
    7. return ThisChar;

    有了这个,我们有了基本的万花筒语言的完整词法分析器(Lexer 的完整代码清单可以在本教程的下一章中找到)。接下来,我们将构建一个简单的解析器,使用它来构建抽象语法树。当我们有这个时,我们将包含一个驱动程序,以便您可以一起使用词法分析器和解析器。

    下一页:实现解析器和AST