0%

ugo-lab3-完善语句

变量和作用域

最小µGo的编译器中没有等号,需要在 (p *Lexer) run() (tokens []token.Token) 中添加有关等号的处理:

1
2
3
4
5
6
7
8
case r == '=': // =, ==
switch p.src.Read() {
case '=':
p.emit(token.EQL)
default:
p.src.Unread()
p.emit(token.ASSIGN)
}

本次实验需要完成语法分析中的:“变量声明”,“嵌套语句块”,“赋值语句”

在语法分析中对于语句的处理如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
func (p *Parser) parseStmt() ast.Stmt {
switch tok := p.PeekToken(); tok.Type {
case token.EOF:
return nil
case token.ERROR:
p.errorf(tok.Pos, "invalid token: %s", tok.Literal)
case token.SEMICOLON:
p.AcceptTokenList(token.SEMICOLON)
return nil
case token.LBRACE: // 语句块
return p.parseStmt_block()
default:
exprList := p.parseExprList() // 表达式列表 exprList
switch tok := p.PeekToken(); tok.Type {
case token.SEMICOLON, token.LBRACE: // exprList;
if len(exprList) != 1 {
p.errorf(tok.Pos, "unknown token: %v", tok.Type)
}
return &ast.ExprStmt{
X: exprList[0],
}
case token.DEFINE, token.ASSIGN:
// exprList := exprList; && exprList = exprList;
p.ReadToken()
exprValueList := p.parseExprList()
if len(exprList) != len(exprValueList) {
p.errorf(tok.Pos, "unknown token: %v", tok)
}
var assignStmt = &ast.AssignStmt{
Target: make([]*ast.Ident, len(exprList)),
OpPos: tok.Pos,
Op: tok.Type,
Value: make([]ast.Expr, len(exprList)),
}
for i, target := range exprList {
assignStmt.Target[i] = target.(*ast.Ident)
assignStmt.Value[i] = exprValueList[i]
}
return assignStmt
default:
p.errorf(tok.Pos, "unknown token: %v", tok)
}
}

panic("unreachable")
}
  • 对于 default 分支来说有如下3种情况:
1
2
3
exprList ;
exprList := exprList;
exprList = exprList;
  • 然后根据这3种情况分别生成赋值语句结点,该结点的 AST 结构如下:
1
2
3
4
5
6
7
// 一个赋值语句结点
type AssignStmt struct {
Target []*Ident // 要赋值的目标
OpPos int // ':=' 的位置
Op token.TokenType // '=' or ':='
Value []Expr // 值
}

语句块的 AST 结构体定义如下:

1
2
3
4
5
6
// 块语句
type BlockStmt struct {
Lbrace int // '{'的位置
List []Stmt
Rbrace int // '}'的位置
}

核心函数为 parseStmt_block,实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
func (p *Parser) parseStmt_block() *ast.BlockStmt {
block := &ast.BlockStmt{}
tokBegin := p.MustAcceptToken(token.LBRACE) // 获取'{'的位置

Loop:
for {
switch tok := p.PeekToken(); tok.Type {
case token.EOF:
break Loop
case token.ERROR:
p.errorf(tok.Pos, "invalid token: %s", tok.Literal)
case token.SEMICOLON:
p.AcceptTokenList(token.SEMICOLON)
case token.RBRACE: // }
break Loop
default:
block.List = append(block.List, p.parseStmt_expr()) // 表达式中可能有子语句块
}
}

tokEnd := p.MustAcceptToken(token.RBRACE) // 获取'}'的位置
block.Lbrace = tokBegin.Pos
block.Rbrace = tokEnd.Pos
return block
}
  • break label 这种语法可以一次性跳出 for 和 switch

用于管理变量的 AST 结构体如下:

1
2
3
4
5
6
7
// 变量信息
type VarSpec struct {
VarPos int // var 关键字位置
Name *Ident // 变量名字
Type *Ident // 变量类型, 可省略
Value Expr // 变量表达式
}

变量可以为全局也可以存在于语句块中,因此需要将函数 parseStmt_var 添加入 parseStmt 和 parseStmt_block 中

核心函数 parseStmt_var 实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
func (p *Parser) parseStmt_var() *ast.VarSpec {
tokVar := p.MustAcceptToken(token.VAR)
tokIdent := p.MustAcceptToken(token.IDENT)

var varSpec = &ast.VarSpec{
VarPos: tokVar.Pos,
}

varSpec.Name = &ast.Ident{
NamePos: tokIdent.Pos,
Name: tokIdent.Literal,
}

if typ, ok := p.AcceptToken(token.IDENT); ok {
varSpec.Type = &ast.Ident{
NamePos: typ.Pos,
Name: typ.Literal,
}
}

if _, ok := p.AcceptToken(token.ASSIGN); ok {
varSpec.Value = p.parseExpr()
}
p.AcceptToken(token.SEMICOLON)
return varSpec
}
  • 利用之前实现的 API 获取 AST 结点的关键信息,最后生成并返回该 AST 结点

if分支和for循环

在 token 类型和注册的关键字中添加 if for,以保证词法分析可以顺利匹配

if 语句结点和 for 语句结点的定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 表示一个 if 语句节点
type IfStmt struct {
If int // if 关键字的位置
Init Stmt // 初始化语句
Cond Expr // if 条件, *BinaryExpr
Body *BlockStmt // if 为真时对应的语句列表
Else Stmt // else 对应的语句
}

// 表示一个 for 语句节点
type ForStmt struct {
For int // for 关键字的位置
Init Stmt // 初始化语句
Cond Expr // 条件表达式
Post Stmt // 迭代语句
Body *BlockStmt // 循环对应的语句列表
}

在语法分析中与 if for 语句只能在语句块中,因此需要添加入 parseStmt_block:

1
2
3
4
case token.IF:
block.List = append(block.List, p.parseStmt_if())
case token.FOR:
block.List = append(block.List, p.parseStmt_for())

对于 if 语句而言,有如下几种情况:

1
2
3
4
if x > 0 {}
if x := 1; x > 0 {}
if x > 0 {} else {}
if x := 1; x > 0 {} else {}

对应的处理函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
func (p *Parser) parseStmt_if() *ast.IfStmt {
tokIf := p.MustAcceptToken(token.IF)
ifStmt := &ast.IfStmt{
If: tokIf.Pos,
}
stmt := p.parseStmt()
if _, ok := p.AcceptToken(token.SEMICOLON); ok {
ifStmt.Init = stmt
ifStmt.Cond = p.parseExpr()
ifStmt.Body = p.parseStmt_block()
} else {
ifStmt.Init = nil
if cond, ok := stmt.(*ast.ExprStmt); ok {
ifStmt.Cond = cond.X
} else {
p.errorf(tokIf.Pos, "if cond expect expr: %#v", stmt)
}
ifStmt.Body = p.parseStmt_block()
}

if _, ok := p.AcceptToken(token.ELSE); ok {
switch p.PeekToken().Type {
case token.IF: // else if
ifStmt.Else = p.parseStmt_if()
default:
ifStmt.Else = p.parseStmt_block()
}
}

return ifStmt
}

对于 for 语句而言,有如下几种情况:

1
2
3
for {}
for x > 10 {}
for x := 0; x < 10; x = x+1 {}

对应的处理函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
func (p *Parser) parseStmt_for() *ast.ForStmt {
tokFor := p.MustAcceptToken(token.FOR)

forStmt := &ast.ForStmt{
For: tokFor.Pos,
}

// for {}
if _, ok := p.AcceptToken(token.LBRACE); ok {
p.UnreadToken()
forStmt.Body = p.parseStmt_block()
return forStmt
}

// for Cond {}
// for Init?; Cond?; Post? {}

// for ; ...
if _, ok := p.AcceptToken(token.SEMICOLON); ok {
forStmt.Init = nil

// for ;; ...
if _, ok := p.AcceptToken(token.SEMICOLON); ok {
if _, ok := p.AcceptToken(token.LBRACE); ok {
// for ;; {}
p.UnreadToken()
forStmt.Body = p.parseStmt_block()
return forStmt
} else {
// for ; ; postStmt {}
forStmt.Post = p.parseStmt()
forStmt.Body = p.parseStmt_block()
return forStmt
}
} else {
// for ; cond ; ... {}
forStmt.Cond = p.parseExpr()
p.MustAcceptToken(token.SEMICOLON)
if _, ok := p.AcceptToken(token.LBRACE); ok {
// for ; cond ; {}
p.UnreadToken()
forStmt.Body = p.parseStmt_block()
return forStmt
} else {
// for ; cond ; postStmt {}
forStmt.Post = p.parseStmt()
forStmt.Body = p.parseStmt_block()
return forStmt
}
}
} else {
stmt := p.parseStmt()

if _, ok := p.AcceptToken(token.LBRACE); ok {
// for cond {}
p.UnreadToken()
if expr, ok := stmt.(ast.Expr); ok {
forStmt.Cond = expr
}
forStmt.Body = p.parseStmt_block()
return forStmt
} else {
// for init;
p.MustAcceptToken(token.SEMICOLON)
forStmt.Init = stmt

// for ;; ...
if _, ok := p.AcceptToken(token.SEMICOLON); ok {
if _, ok := p.AcceptToken(token.LBRACE); ok {
// for ;; {}
p.UnreadToken()
forStmt.Body = p.parseStmt_block()
return forStmt
} else {
// for ; ; postStmt {}
forStmt.Post = p.parseStmt()
forStmt.Body = p.parseStmt_block()
return forStmt
}
} else {
// for ; cond ; ... {}
forStmt.Cond = p.parseExpr()
p.MustAcceptToken(token.SEMICOLON)
if _, ok := p.AcceptToken(token.LBRACE); ok {
// for ; cond ; {}
p.UnreadToken()
forStmt.Body = p.parseStmt_block()
return forStmt
} else {
// for ; cond ; postStmt {}
forStmt.Post = p.parseStmt()
forStmt.Body = p.parseStmt_block()
return forStmt
}
}
}
}
}

赋值语句

赋值语句出现在语句块中,直接修改 parseStmt_block 函数即可

核心思路和 parseStmt 中关于变量初始化的处理一样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
default:
// exprList ;
// exprList := exprList;
// exprList = exprList;
exprList := p.parseExprList()
switch tok := p.PeekToken(); tok.Type {
case token.SEMICOLON:
if len(exprList) != 1 {
p.errorf(tok.Pos, "unknown token: %v", tok.Type)
}
block.List = append(block.List, &ast.ExprStmt{
X: exprList[0],
})
case token.DEFINE, token.ASSIGN:
p.ReadToken()
exprValueList := p.parseExprList()
if len(exprList) != len(exprValueList) {
p.errorf(tok.Pos, "unknown token: %v", tok)
}
var assignStmt = &ast.AssignStmt{
Target: make([]*ast.Ident, len(exprList)),
OpPos: tok.Pos,
Op: tok.Type,
Value: make([]ast.Expr, len(exprList)),
}
for i, target := range exprList {
assignStmt.Target[i] = target.(*ast.Ident)
assignStmt.Value[i] = exprValueList[i]
}
block.List = append(block.List, assignStmt)
default:
p.errorf(tok.Pos, "unknown token: %v", tok)
}
}