• Home
  • About
    • 吾青 photo

      吾青

      么西么西,听得到吗?

    • Learn More
    • Github
    • Steam
  • Posts
    • All Posts
    • All Tags
  • Projects

LLVM教程(一)简介与词法分析器

16 Dec 2019

Reading time ~1 minute

本文为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:

  1. LLVM Tutorial


编译器 Share Tweet +1