Kaleidoscope: Tutorial Introduction and the Lexer
The Basic Language
Kaleidoscope 是一个面向过程的语言, 允许你定义函数, 使用条件判断, 数学计算等. 在后面的教程中, 我们还会扩展 Kaleidoscope 使它支持 if/then/else
结构, 循环结构和用户定义的操作符.
Kaleidoscope 中唯一的数据类型是 64 bit 的浮点类型 (double
in C). 下面是使用 Kaleidoscope 实现的斐波拉契数列.
1 | # Compute the x'th Fibonacci Number |
我们还允许 Kaleidoscope 能够调用标准库函数 (LLVM JIT 能够轻易的实现它). 你可以使用 extern
关键字来定义一个函数.
1 | extern sin(arg); |
The Lexer
当实现一个语言时, 第一件事就是能够处理源代码文件, 并知道它想表达的意思. 传统的做法是使用 lexer (scanner) 将输入的源文件处理为 tokens. Lexer 返回的每个 token 都包括一个 token code 和一些 metadata (比如数字的值). 首先我们定义这些可能使用到的数据:
1 | // The lexer return tokens [0-255] |
Lexer 返回的每个 token 要么是 Token 枚举中的值, 要么是一个 “未知” 的字符的 ascii
值, 比如 +
. 如果当前的 token 是一个标识符, 那么 IdentifierStr
会保存它的名字. 如果当前 token 是一个数字, 那么 NumVal
会保存它的值.
Lexer 实现是 gettok()
函数, gettok()
被调用之后, 会返回从标注输入中获取的下一个 token.
1 | int gettok() { |
gettok
通过调用 getchar()
从标准输入中读取字符, 并忽略 tokens 之间的空格, 它仅仅是将字符存储到 LastChar
中, 不进行处理.
gettok
接下来要识别标识符和关键字.
1 | if (isalpha(LastChar)) { |
如果不是标识符和关键字,那进一步判断是否为数字.
1 | if (isdigit(LastChar) || LastChar == '.') { |
当读取的字符为数字时, 使用 strtod()
将它转化为 double
并存储在 NumVal
中. 这一步没有做异常处理, 有需要的可以添加.
然后我们处理注释:
1 | if (LastChar == '#') { |
注释会跳过 #
后面所有的字符,直到下一行或者 EOF
. 当跳过之后, 需要重新进行处理并返回下一个 token, 所以直接尾递归调用 gettok()
即可. 如果输入不匹配标识符和关键字, 也不匹配数字, 只有可能是 +
之类的操作符, 和 EOF
.
1 | // Check for end of file. Don't eat EOF |
以上就是 Kaleidoscope 的 lexer.