0%

ugo-lab6-函数表&完善函数

完善函数语句

对于一个函数而已,关键信息有:函数名,参数列表,返回类型,语句块

对应的 AST 结点结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 全局函数/方法
type FuncDecl struct {
FuncPos int // func 关键字位置
Params *FieldList // 传参类型
Name *Ident // 函数名
Type *Ident // 返回类型
Body *BlockStmt // 函数内的语句块
}

// 参数/属性 列表
type FieldList struct {
List []*Field
}
  • 暂时不涉及多值返回

处理函数定义

函数 parseFunc 用于解析函数定义,其初步实现如下:

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
func (p *Parser) parseFunc() *ast.FuncDecl {
tokFunc := p.MustAcceptToken(token.FUNC)
tokFuncIdent := p.MustAcceptToken(token.IDENT)

p.MustAcceptToken(token.LPAREN)
funcFieldList := &ast.FieldList{}

if _, ok := p.AcceptToken(token.RPAREN); !ok {
for {
tokVal := p.MustAcceptToken(token.IDENT)
tokTyp := p.MustAcceptToken(token.IDENT)
funcField := &ast.Field{
Name: &ast.Ident{
NamePos: tokVal.Pos,
Name: tokVal.Literal,
},
Type: &ast.Ident{
NamePos: tokTyp.Pos,
Name: tokTyp.Literal,
}}
funcFieldList.List = append(funcFieldList.List, funcField)
if ok = p.SymTab.Push(funcField.Name, funcField.Type); !ok {
p.errorf(tokVal.Pos, "duplicate variable declaration: %s", tokVal.Literal)
}
if _, ok := p.AcceptToken(token.COMMA); !ok {
break
}
}
p.MustAcceptToken(token.RPAREN)
}

funcName := &ast.Ident{
NamePos: tokFuncIdent.Pos,
Name: tokFuncIdent.Literal,
}

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

return &ast.FuncDecl{
FuncPos: tokFunc.Pos,
Params: funcFieldList,
Name: funcName,
Type: funcType,
Body: p.parseStmt_block(),
}
}
  • 暂时不支持Go语言中多个参数共用一个类型的写法
  • 函数参数将会被暂时添加入符号表中

对于一个函数而言,需要在函数返回之后在符号表中清除其传参和临时变量,这里的处理方式为:记录语句块中传参和临时变量的个数,然后在语句块结束之后执行对应数量的 Pop

这样做有一个好处,那就是当遇到嵌套语句块时也可以区分各个变量的作用域

为此需要在 SymTab 中新添加一个条目:

1
2
3
4
5
6
type SymTab struct {
elements []*TypeSpec
block []int
top int
level int
}
  • 每一层都有一个数字来记录当前语句块中有多少个变量

修改一下 Push 和 Pop 语句,并添加一个 Pops 用于弹出全部的传参和临时变量:

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
func (s *SymTab) Push(name, typ *Ident) bool {
for i := s.top - 1; i >= 0; i-- {
if s.elements[i].Name.Name == name.Name {
return false
}
}
s.elements = append(s.elements, &TypeSpec{
Name: name,
Type: typ,
})
s.top++
s.block[s.level]++
return true
}

func (s *SymTab) Pop() bool {
if s.top == 0 {
return false
}
s.top--
s.elements = s.elements[:len(s.elements)-1]
return true
}

func (s *SymTab) Pops() bool {
for s.block[s.level] > 0 {
if ok := s.Pop(); !ok {
return false
}
s.block[s.level]--
}
return true
}

添加两个辅助函数用于换层:

1
2
3
4
5
6
7
8
9
func (s *SymTab) AddLevel() {
s.level++
s.block = append(s.block, 0)
}

func (s *SymTab) SubLevel() {
s.level--
s.block = s.block[:len(s.block)-1]
}

最后修改 parseStmt_block 和 parseFunc,往其中添加换层与清空临时变量的代码:

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

p.SymTab.AddLevel()

......

tokEnd := p.MustAcceptToken(token.RBRACE) // 获取'}'的位置
block.Lbrace = tokBegin.Pos
block.Rbrace = tokEnd.Pos

p.SymTab.Pops()
p.SymTab.SubLevel()

return block
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func (p *Parser) parseFunc() *ast.FuncDecl {
tokFunc := p.MustAcceptToken(token.FUNC)
tokFuncIdent := p.MustAcceptToken(token.IDENT)

p.MustAcceptToken(token.LPAREN)
funcFieldList := &ast.FieldList{}

p.SymTab.AddLevel()

......

p.SymTab.Pops()
p.SymTab.SubLevel()

return &ast.FuncDecl{
FuncPos: tokFunc.Pos,
Params: funcFieldList,
Name: funcName,
Type: funcType,
Body: funcBody,
}
}

处理返回语句

return 语句对应的 AST 结点结构如下:

1
2
3
4
5
// 表示一个 return 语句节点
type RetStmt struct {
Ret int // return 关键字的位置
Expr Expr // 返回值表达式
}

核心函数 parseStmt_return 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func (p *Parser) parseStmt_return() *ast.RetStmt {
tokRet := p.MustAcceptToken(token.RETURN)
tokExp := p.parseExpr()

myTypeX := reflect.TypeOf(tokExp)
if myTypeX.String() == "*ast.Strings" || myTypeX.String() == "*ast.Bool" {
p.errorf(tokExp.Pos(), "Non-numbers cannot participate in the calculation")
}

return &ast.RetStmt{
Ret: tokRet.Pos,
Expr: tokExp,
}
}

处理函数调用

函数调用对应的 AST 结点结构如下:

1
2
3
4
5
6
7
// 表示一个函数调用
type CallExpr struct {
FuncName *Ident // 函数名字
Lparen int // '(' 位置
Args []Expr // 调用参数列表
Rparen int // ')' 位置
}

函数调用发生在表达式中,需要处理的函数为 parseExpr_primary:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
if lp, ok := p.AcceptToken(token.LPAREN); ok {
var rp token.Token
astc := &ast.CallExpr{
FuncName: &ast.Ident{
NamePos: tok.Pos,
Name: tok.Literal,
},
}
for {
if rp, ok = p.AcceptToken(token.RPAREN); ok {
break
}
arg := p.parseExpr()
astc.Args = append(astc.Args, arg)
p.AcceptToken(token.COMMA)
}
astc.Lparen = lp.Pos
astc.Rparen = rp.Pos
asti.Offset = astc

return asti
}

函数表

函数表被记录于全局函数中,这里主要是通过函数表完成与函数有关的各种错误检查

错误类型:函数重复定义

1
2
3
4
5
6
7
8
func (p *Parser) checkFuncName(name string) bool {
for _, fu := range p.file.Funcs {
if fu.Name.Name == name {
return true
}
}
return false
}
1
2
3
4
5
tokFunc := p.MustAcceptToken(token.FUNC)
tokFuncIdent := p.MustAcceptToken(token.IDENT)
if ok := p.checkFuncName(tokFuncIdent.Literal); ok {
p.errorf(tokFuncIdent.Pos, "duplicate function declaration: %s", tokFuncIdent.Literal)
}

错误类型:函数未定义

1
2
3
4
5
6
7
8
9
10
11
if lp, ok := p.AcceptToken(token.LPAREN); ok {
if ok = p.checkFuncName(tok.Literal); !ok {
p.errorf(lp.Pos, "Function name:%s not find", tok.Literal)
}
var rp token.Token
astc := &ast.CallExpr{
FuncName: &ast.Ident{
NamePos: tok.Pos,
Name: tok.Literal,
},
}

错误类型:传参不匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func (p *Parser) checkFuncArgs(name string, args []ast.Expr) bool {
for _, fu := range p.file.Funcs {
if fu.Name.Name != name {
continue
}

if len(args) != len(fu.Params.List) {
return false
}

for i, arg := range fu.Params.List {
myType := reflect.TypeOf(args[i])
if arg.Type.Name == "string" && myType.String() != "*ast.Strings" {
return false
}
if arg.Type.Name == "bool" && myType.String() != "*ast.Bool" {
return false
}
}
break
}
return true
}
1
2
3
4
5
6
7
8
9
10
11
for {
if rp, ok = p.AcceptToken(token.RPAREN); ok {
break
}
arg := p.parseExpr()
astc.Args = append(astc.Args, arg)
p.AcceptToken(token.COMMA)
}
if ok = p.checkFuncArgs(tok.Literal, astc.Args); !ok {
p.errorf(lp.Pos, "The function parameters do not match")
}