- 用Go语言自制解释器
- (德)索斯藤·鲍尔
- 1026字
- 2022-06-17 10:50:31
1.2 定义词法单元
首先要做的是定义词法分析器输出的词法单元。这里先定义少量的词法单元,之后在扩展词法分析器时再添加更多的定义。
第一次要解析的Monkey语言代码如下所示:
let five = 5;
let ten = 10;
let add = fn(x, y) {
x + y;
};
let result = add(five, ten);
来详细看看,这个例子中包含哪些类型的词法单元。首先,有5
和10
这样的数字,这很明显;之后是x
、y
、add
和result
这样的变量名。最后Monkey语言中还有一些单词,它们既不是数字也不是变量名,例如let
和fn
。当然,还有很多特殊字符,如(
、)
、{
、}
、=
、,
、;
。
这些数字都是整数,将按字面量处理,并赋予其一个单独的类型。在词法分析器或语法分析器中,数字的值是5
还是10
并不重要,只要知道它是一个数字就行。变量名也是如此,都统一用作标识符。除此之外还有一些单词,看起来像标识符但实际上不是,这些称为关键字,也是Monkey语言的一部分。后面在语法分析阶段遇到let
或fn
这样的关键字时,都会特殊处理,所以它们不能与标识符归为一类。最后的特殊字符也会单独列出来,其中每个特殊字符都会有相应的处理方式,例如在源代码中,括号会对代码的含义产生很大影响。
基于这些分析,现在来定义Token
数据结构。它需要哪些字段呢?正如刚刚看到的,肯定需要一个类型属性,这样就可以区分“整数”和“右括号”这样不同的词法单元。然后还需要一个字段用于保存词法单元的字面量,以便后续步骤复用,比如对于表示数字的词法单元,这个字段能记录5
或10
这样的信息。
新建一个token
包,以便定义Token
结构和TokenType
类型:
// token/token.go
package token
type TokenType string
type Token struct {
Type TokenType
Literal string
}
TokenType
类型定义成了字符串,这样我们就可以使用各种TokenType
值,而根据TokenType
值能区分不同类型的词法单元。使用字符串对调试也有帮助,会让调试更容易,而无须再使用许多样板和辅助函数,只需打印一个字符串即可。当然,与使用int
或byte
类型相比,使用字符串会导致程序在性能上有损失。但对于本书而言,使用字符串完全没有问题。
从刚刚的分析中可以看到,Monkey语言中的词法单元类型并不多。这意味着可以将所有的TokenType
都定义为常量,所以在上面的代码中可以再添加以下内容:
// token/token.go
const (
ILLEGAL = "ILLEGAL"
EOF = "EOF"
// 标识符+字面量
IDENT = "IDENT" // add, foobar, x, y, ...
INT = "INT" // 1343456
// 运算符
ASSIGN = "="
PLUS = "+"
// 分隔符
COMMA = ","
SEMICOLON = ";"
LPAREN = "("
RPAREN = ")"
LBRACE = "{"
RBRACE = "}"
// 关键字
FUNCTION = "FUNCTION"
LET = "LET"
)
如你所见,上面的代码中还出现了ILLEGAL
和EOF
这两种特殊类型。这两种类型在之前的示例中并没有遇到,却是必不可少的。ILLEGAL
表示遇到未知的词法单元或字符,EOF
则表示文件结尾(End Of File),用于通知后续章节会介绍的语法分析器停机。
目前一切顺利,下面准备开始编写词法分析器。