Skip to content

Latest commit

 

History

History
63 lines (49 loc) · 3.35 KB

File metadata and controls

63 lines (49 loc) · 3.35 KB

1.2定义我们的Tokens

我们必须要做的第一件事就是定义词法分析器输出的Tokens。我们将从几个标记定义开始,然后在拓展词法分析器时添加更多。

我们将在第一部中的词法的Monkey语言的子集如下所示:

let five = 5;
let ten = 10;

let add = fn(x,y) {
    x + y;
};

let result = add(five,ten);

让我们分析一下:这个例子中包含哪些类型的Tokens?首先,有像5和10这样的数字。这些是很明显的。然后我们有变量名称x,y,add和result。然后还有语言的这些部分不是数字,只是单词,但也没有变量名,像let和fn。当然,也有很多特殊字符:(,),{,}=,,,,;。数字只是整数,我们将这一对待它们并给他们一个单独的类型。在词法分析器或者解析器中,我们不关心数字是5还是10,我们只想知道它是否是数字。"变量名"也是如此:我们称它们为“标识符”,并且一视同仁。现在,换句话说,那些看起来像标识符但实际上不是标识符的词,因为它们是语言的一部分,被称为“关键字”。我们不会将这些组合在一起,因为无论我们是否遇到aletor afn,它们都会在解析阶段产生影响。

我们确定的最后一个类别也是如此:特殊字符。我们将分别对待它们中的每一个,因为源代码中是否有a(或者a)是一个很大的区别。让我们定义我们的Token数据结构。它需要哪些领域?正如我们刚刚看到的,我们肯定需要一个“类型”属性,例如,我们可以区分“整数”和“右括号”。并且它需要一个字段来保存Token的字面值,这样我们之后可以重用它并且"number"Tokens是a5或者a10的信息不会丢失。在一个newtoken包中,我们定义我们的Token类型和我们的TokenType类型。

// token/token.go
package token

type TokenType string

type Token struct {
    Type TokenTyoe
    Literal string
}

我们定义TokenType类型为string。这允许我们使用多种不同的值作为Token类型,这反过来又允许我们区分不同类型的Tokens。使用字符串还有一个优点是易于调试,无需大量样板和辅助函数:我们可以只打印字符串。当然,使用一个字符串可能不会带来和使用一个int或者一个byte一样的性能,但对于本书而言,string是完美的。

正如我们刚刚看到的,Monkey语言中的不同Token类型数量有限。这意味着我们将可能定义TokenTypes为常量。在同一个文件内,我们添加这些内容:

const(
    ILLEGAL ="ILLEGAL"
    EOF="EOF"

    // Identifiers + literals
    IDENT ="IDENT"// add, foobar, x, y,...
    INT   ="INT"// 1343456

    // Operators
    ASSIGN   ="="
    PLUS="+"

    // Delimiters
    COMMA=","
    SEMICOLON =";"
    LPAREN ="("
    RPAREN =")"
    LBRACE ="{"
    RBRACE ="}"
    
    // Keywords
    FUNCTION ="FUNCTION"
    LET="LET"
)

正如你所看到的两种特殊类型:ILLEGAL和EOF。我们在之前的例子中没有看到它们,但是我们仍然需要它们。ILLEGAL表示我们不知道的标记/字符,而EOF代表"文件结束"(end of file),它告诉我们的解析器稍后它可以停止了。到目前为止一切顺理。我们准备开始编写我们的词法分析器(lexer)。

< 1.1词法分析 > 1.3词法分析器