本文为LLVM教程的笔记(一)。
1.初级语言
Kaleidoscope是一个过程式语言,用户可以定义函数,执行条件语句,执行数学运算等。
为了方便,kaleidoscope只支持64位浮点数。用kaleidoscope实现的斐波那契数列示例:
// 计算第x个斐波那契数
def fib(x)
if x < 3 then
1
else fib(x-1) + fib(x-2)
kaleidoscope还可以通过LLVM JIT调用标准库函数,但是要用“extern”关键字对函数进行申明:
extern sin(arg);
extern cos(arg);
extern atan2(arg1 arg2);
atan2(sin(.4), cos(42))
2.词法分析器
“实现语言的首要任务是处理文本文件,搞明白其中写了什么。”
编译原理课介绍过我们可以用词法分析器把输入切分成“语元(token)”,然后再处理。词法分析器返回的每个语元都带有语元编号,也可能带有某些元数据,比如某个数值。
2.1定义语元
//===----------------------------------------------------------------------===//
// Lexer
//===----------------------------------------------------------------------===//
// The lexer returns tokens [0-255] if it is an unknown character, otherwise one
// of these for known things.
enum Token {
tok_eof = -1,
// commands
tok_def = -2, tok_extern = -3,
// primary
tok_identifier = -4, tok_number = -5
};
static std::string IdentifierStr; // Filled in if tok_identifier
static double NumVal; // Filled in if tok_number
如果语元不是上述枚举值,如果是一些“+”之类的字符,词法分析器将返回ASCII值;如果语元是标识符,词法分析器会把名称存入全局变量IdentifierStr;如果语元是数值常量,词法分析器会把它存入NumVal。
“但是真正的语言中,使用全局变量不是明智之举。”
2.2关于gettok()的函数实现:
/// gettok - Return the next token from standard input.
static int gettok() {
static int LastChar = ' ';
// Skip any whitespace.
while (isspace(LastChar))
LastChar = getchar();
调用这个函数,就可以得到标准输入的下一个语元。gettok()先去除空白字符,再用getchar()读入字符,一边识别一边将读入的字符存入LastChar。
2.3识别标识符
接着gettok()将识别标识符(如“def”等),并存入全局变量:
if (isalpha(LastChar)) { // identifier: [a-zA-Z][a-zA-Z0-9]*
IdentifierStr = LastChar;
while (isalnum((LastChar = getchar())))
IdentifierStr += LastChar;
if (IdentifierStr == "def") return tok_def;
if (IdentifierStr == "extern") return tok_extern;
return tok_identifier;
}
2.4识别数值
数值识别,碰到数值的字符串就用strtod转换为数值并存入NumVal(其实会造成一些错误):
if (isdigit(LastChar) || LastChar == '.') { // Number: [0-9.]+
std::string NumStr;
do {
NumStr += LastChar;
LastChar = getchar();
} while (isdigit(LastChar) || LastChar == '.');
NumVal = strtod(NumStr.c_str(), 0);
return tok_number;
}
2.5识别注释
接着处理注释,碰到注释就跳过:
if (LastChar == '#') {
// Comment until end of line.
do LastChar = getchar();
while (LastChar != EOF && LastChar != '\n' && LastChar != '\r');
if (LastChar != EOF)
return gettok();
}
2.6其他情况
如果碰到其他情况,比如运算符或到文件末尾:
// Check for end of file. Don't eat the EOF.
if (LastChar == EOF)
return tok_eof;
// Otherwise, just return the character as its ascii value.
int ThisChar = LastChar;
LastChar = getchar();
return ThisChar;
}
Reference: