在实现语言时,首先需要的是能够处理文本文件并识别它所说的内容。传统的方法是使用“词法分析器”(又名“扫描仪”)将输入分解为“令牌”。词法分析器返回的每个标记包括标记代码和可能的一些元数据(例如,数字的数值)。首先,我们定义了可能性:
// 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
我们的词法分析器返回的每个标记将是Token枚举值之一,或者它将是一个’未知’字符,如’+’,它将作为ASCII值返回。如果当前令牌是标识符,则IdentifierStr全局变量保存标识符的名称。如果当前标记是数字文字(如1.0),则NumVal保持其值。请注意,为简单起见,我们使用全局变量,这不是真正的语言实现的最佳选择:)。 词法分析器的实际实现是一个名为gettok的函数。调用gettok函数以从标准输入返回下一个标记。其定义开始于:
/// gettok - Return the next token from standard input.
static int gettok() {
static int LastChar = ' ';
// Skip any whitespace.
while (isspace(LastChar))
LastChar = getchar();
gettok通过调用C getchar()函数从标准输入一次读取一个字符来工作。它在识别它们时吃它们并在LastChar中存储读取但未处理的最后一个字符。它必须做的第一件事就是忽略令牌之间的空格。这是通过上面的循环完成的。 gettok需要做的下一件事是识别标识符和特定的关键字,如“def”。 Kaleidoscope通过这个简单的循环实现了这一点:
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;
}
请注意,此代码在设置标识符时设置“IdentifierStr”全局。此外,由于语言关键字由相同的循环匹配,我们在这里处理它们。数值类似:
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;
}
这是处理输入的非常简单的代码。当从输入读取数值时,我们使用C strtod函数将其转换为我们存储在NumVal中的数值。请注意,这没有进行足够的错误检查:它将错误地读取“1.23.45.67”并像处理“1.23”一样处理它。随意扩展它:)。接下来我们处理评论:
if (LastChar == '#') {
// Comment until end of line.
do
LastChar = getchar();
while (LastChar != EOF && LastChar != '\n' && LastChar != '\r');
if (LastChar != EOF)
return gettok();
}
我们通过跳到行尾来处理注释,然后返回下一个令牌。最后,如果输入与上述情况之一不匹配,则它是“+”之类的运算符字符或文件的结尾。这些代码使用以下代码处理:
// 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;
}