使用Golang创建解释器

简而言之

筆者半年前开始自学Python,为了更深入理解编程,决定自己动手制作一个解释器。选择Go语言的原因是想接触静态类型语言,并且觉得Go语言很流行。本文将参考《用Go创建解释器》进行开发。由于已经完成第一章的阅读,下面我将进行总结。

第一章,解析文字和句子

字句解析是什么意思?

将源代码转换为令牌序列被称为”词法分析”。

let x = 5 + 5;
名称未設定.png

解析器字句

    令牌的定义
type Token struct {
    Type string
    Literal string
}
const (
    ILLEGAL = "ILLEGAL" // 未知の文字列
    EOF     = "EOF" // 終わりを示す

    IDENT = "IDENT" // 識別子
    INT   = "INT" // 数値型

    ASSIGN   = "="
    PLUS     = "+"

    [...]
)
    Lexer(词法分析器)
type Lexer struct {
    input        string
    position     int // 読み込んでる文字
    readPosition int // 次に読み込む文字
    ch           byte // 検査中の文字
}

func New(input string) *Lexer {
    // Lexerに引数inputをセットしreturn
    l := &Lexer{input: input}
    l.readChar()
    return l
}

func (l *Lexer) readChar() {
    // 入力が終わったらchを0に
    if l.readPosition >= len(l.input) {
        l.ch = 0
    // まだ終わっていない場合readPositionをchにセット
    } else {
        l.ch = l.input[l.readPosition]
    }
    // positionを次に進める
    l.position = l.readPosition
    // readpositonを次に進める
    l.readPosition += 1
}

func (l *Lexer) NextToken() token.Token {
    var tok token.Token

    // トークンを生成する
    switch l.ch {
    case '=':
        tok = newToken(token.ASSIGN, l.ch)
    case '+':
        tok = newToken(token.PLUS, l.ch)
    case '-':
        tok = newToken(token.MINUS, l.ch)
    [...]

    l.readChar()
    return tok
}

func newToken(tokenType TokenType, ch byte) Token {
    return Token{Type: tokenType, Literal: string(ch)}
}

现在可以生成令牌了,但是仍需要逐个字符生成令牌。

let five = 5;
let ten = 10;

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

let result = add(five, ten):

对于像上述的句子进行分析仍然存在一些问题。

    由于只能一次解析一个字符,所以使用 let(关键字)会引发错误。与 1 相同,使用 five(标识符)也会引发错误。解析数字。

首先,我们需要实现一个函数来判断字符是否为英文字母1或2,并且实现一个函数来读取并推进字符,直到遇到非英文字母。

// 非英字まで読み込みを進める
func (l *Lexer) readIdentifier() string {
    position := l.position
    // for文でisLetter関数からfalseが返ってくるまで読み込みを進める
    for isLetter(l.ch) {
        l.readChar()
    }
    // 最初の文字から非英字まで進めたpositionまでを返す
    return l.input[position:l.position]
}
// 判断する関数
func isLetter(ch byte) bool {
    return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z'
}

使用这两个函数,给NextToken()的switch语句添加分支,就可以成功生成字符串的标记。
然后需要对变量声明(如let)和函数声明(如fn)进行分类。


[...]
    FUNCTION = "FUNCTION"
    LET      = "LET"

var keywords = map[string]TokenType{
    "fn":     FUNCTION,
    "let":    LET,
}

func LookupIdent(ident string) TokenType {
    // readIdentifierで読み込んだ文字列を引数にとり、キーワードに存在する場合は適切なTypeを返す
    if tok, ok := keywords[ident]; ok {
        return tok
    }
    // 識別子の場合
    return IDENT
}

与 isLetter 相同的是,将其分辨为数字令牌。代码将被省略。

在第一章的最后实现REPL。

REPL的实现

REPL 是什么?

阅读

E 评价

打印

循环

REPL是上述的缩写。在解释型语言中非常常见,它接收输入、进行评估并重复输出结果。

const PROMPO = ">>"

func Start(in io.Reader, out io.Writer) {
    // scannerの生成
    scanner := bufio.NewScanner(in)

    for {
        fmt.Printf(PROMPO)
        scanned := scanner.Scan()
        if !scanned {
            return
        }
        // 入力を受け取る
        line := scanner.Text()
        // 解析器に入力された文字列を入れる
        l := lexer.New(line)

        // 文字列を評価し出力、これを繰り返す。
        for tok := l.NextToken(); tok.Type != token.EOF; tok = l.NextToken() {
            fmt.Printf("%+v\n", tok)
        }
    }
}

以上是第一章的结束。这是我第一次以这种形式总结和分享所学到的内容,我觉得这种方式能够加深理解,非常有意义。接下来,我将继续更新第二章。

bannerAds