5.1 完善token包和lex包

本节我们先完善token包和lex包,为后续的语法解析提供新关键字的词法输入。

5.1.1 素数的例子

以下是打印100以内素数的代码,也是本章的最终目标:

package main

func main() {
	for n := 2; n <= 100; n = n + 1 {
		var isPrime int = 1
		for i := 2; i*i <= n; i = i + 1 {
			if x := n % i; x == 0 {
				isPrime = 0
			}
		}
		if isPrime != 0 {
			println(n)
		}
	}
}

其中for和if是新出现的关键字,运算表达式新出现了%取模运算,同时if分支的条件位置出现了比较运算。

5.1.2 定义新的Token类型

为if和for关键字、新运算符增加对应的类型:

package token

// 记号类型
type TokenType int

// ch3中 µGo程序用到的记号类型
const (
	EOF TokenType = iota // = 0

	...
	IF  // if
	FOR // for
    ...

	...
	MOD // %

	EQL // ==
	NEQ // !=
	LSS // <
	LEQ // <=
	GTR // >
	GEQ // >=
	...
)

注册关键字:

var keywords = map[string]TokenType{
	"if":      IF,
	"for":     FOR,
}

更新二元表达式的优先级:

func (op TokenType) Precedence() int {
	switch op {
	case EQL, NEQ, LSS, LEQ, GTR, GEQ:
		return 1
	case ADD, SUB:
		return 2
	case MUL, DIV, MOD:
		return 3
	}
	return 0
}

为了调试方便,读者可以自行为Token类型实现fmt.Stringer接口,具体可以参考代码。

5.1.3 完善lex包

关键字的词法解析不需要变化,主要是增加运算符的词法解析:

func (p *Lexer) run() (tokens []token.Token) {
	...
	for {
		...
		switch {
		...
		case r == '%': // %
			p.emit(token.MOD)

		case r == '=': // =, ==
			switch p.src.Read() {
			case '=':
				p.emit(token.EQL)
			default:
				p.src.Unread()
				p.emit(token.ASSIGN)
			}

		case r == '!': // !=
			switch p.src.Read() {
			case '=':
				p.emit(token.NEQ)
			default:
				p.errorf("unrecognized character: %#U", r)
			}

		case r == '<': // <, <=
			switch p.src.Read() {
			case '=':
				p.emit(token.LEQ)
			default:
				p.src.Unread()
				p.emit(token.LSS)
			}

		case r == '>': // >, >=
			switch p.src.Read() {
			case '=':
				p.emit(token.GEQ)
			default:
				p.src.Unread()
				p.emit(token.GTR)
			}
		...
		}
	}
	...
}

其中等于运算符和赋值运算符有着相同的前缀,需要稍作调整以适应新的语法变化。大于和小于的运算符也是采用类似的方式处理即可。

5.1.4 解析素数的程序的词法

补全词法解析后,解析以下代码:

package main

func main() {
	for {
		if 1 == 0 {}
	}
}

输出结果如下:

$ go run main.go lex ./_examples/hello.ugo 
00: package     : "package"            // ./_examples/hello.ugo:1:1
01: IDENT       : "main"               // ./_examples/hello.ugo:1:9
02: ;           : ""                   // ./_examples/hello.ugo:2:1
03: func        : "func"               // ./_examples/hello.ugo:3:1
04: IDENT       : "main"               // ./_examples/hello.ugo:3:6
05: (           : "("                  // ./_examples/hello.ugo:3:10
06: )           : ")"                  // ./_examples/hello.ugo:3:11
07: {           : "{"                  // ./_examples/hello.ugo:3:13
08: for         : "for"                // ./_examples/hello.ugo:4:2
09: {           : "{"                  // ./_examples/hello.ugo:4:6
10: if          : "if"                 // ./_examples/hello.ugo:5:3
11: NUMBER      : "1"                  // ./_examples/hello.ugo:5:6
12: ==          : "=="                 // ./_examples/hello.ugo:5:8
13: NUMBER      : "0"                  // ./_examples/hello.ugo:5:11
14: {           : "{"                  // ./_examples/hello.ugo:5:13
15: }           : "}"                  // ./_examples/hello.ugo:5:14
16: }           : "}"                  // ./_examples/hello.ugo:6:2
17: }           : "}"                  // ./_examples/hello.ugo:7:1
18: EOF         : ""                   // ./_examples/hello.ugo:8:1

其中iffor和等于比较运算符已经成功解析。


© 2021-2022 | 柴树杉 保留所有权利