- KusonStack一站式可编程配置技术栈(Go): https://github.com/KusionStack/kusion
- KCL 配置编程语言(Rust): https://github.com/KusionStack/KCLVM
- 凹语言™: https://github.com/wa-lang/wa
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
其中if
、for
和等于比较运算符已经成功解析。