0%

ugo-lab5-符号表&错误检测

符号表

本实验使用如下结构来组织符号表:

1
2
3
4
type SymTab struct {
elements []*TypeSpec
top int
}
  • PS:目前该符号表仅用于变量,在后续实验完善函数模块后会添加函数符号

与之配套的函数如下:

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
func NewSymTab() *SymTab {
return &SymTab{
elements: nil,
top: 0,
}
}

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++
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) GetType(value *Ident) (*Ident, bool) {
for i := s.top - 1; i >= 0; i-- {
if s.elements[i].Name.Name == value.Name {
return s.elements[i].Type, true
}
}
return nil, false
}

func (s *SymTab) CheckType(value, typ *Ident) bool {
if tmp, ok := s.GetType(value); ok {
return tmp.Name == typ.Name
}
return false
}

func (s *SymTab) ShowAll() {
fmt.Println("----------SymTab-------------------------")
for i, e := range s.elements {
fmt.Printf("|@%d: {Name: %s, Type: %s}\t\t|\n", i+1, e.Name.Name, e.Type.Name)
}
fmt.Println("-----------------------------------------")
fmt.Println("")
}

错误检测

该错误检测不包括函数未定义和函数参数不匹配,后续实验会进行补充

错误类型:重复定义

该错误发生在变量定义的过程中,具体的函数为 parseStmt_var,需要在该函数中添加与符号表相关的代码:

1
2
3
4
5
6
if ok := p.SymTab.Push(&ast.Ident{
NamePos: tokIdent.Pos,
Name: tokIdent.Literal,
}, varSpec.Type); !ok {
p.errorf(tokIdent.Pos, "duplicate variable declaration: %s", tokIdent.Literal)
}

错误类型:使用时未定义

变量通常在表达式中使用,因此需要在 parseExpr_primary 中进行修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
case token.IDENT: // 变量 && 数组 && 结构体
p.MustAcceptToken(token.IDENT)
asti := &ast.IdentAS{
NamePos: tok.Pos,
Name: tok.Literal,
}
if _, ok := p.AcceptToken(token.LSB); ok {
asti.Offset = p.parseExpr()
p.AcceptToken(token.RSB)
}
if _, ok := p.AcceptToken(token.DOT); ok {
asti.Offset = p.parseExpr()
}

if _, ok := p.SymTab.GetType(tok.Literal); !ok {
p.errorf(tok.Pos, "unknown variable: %s", tok.Literal)
}
return asti

错误类型:未知类型

在进行类型检查之前需要先创建一个类型列表用以记录已有的类型:

1
2
3
4
type TypeTab struct {
elements []string
top int
}

相关函数可以模仿 SymTab:

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
func NewTypeTab() *TypeTab {
var re = &TypeTab{
elements: make([]string, 4),
top: 4,
}

re.elements[0] = "bool"
re.elements[1] = "char"
re.elements[2] = "int"
re.elements[3] = "string"

return re
}

func (s *TypeTab) Push(typ string) bool {
for i := s.top - 1; i >= 0; i-- {
if s.elements[i] == typ {
return false
}
}
s.elements = append(s.elements, typ)
s.top++
return true
}

func (s *TypeTab) Pop() bool {
if s.top == 0 {
return false
}
s.top--
return true
}

func (s *TypeTab) CheckType(typ string) bool {
for i := s.top - 1; i >= 0; i-- {
if s.elements[i] == typ {
return true
}
}
return false
}

func (s *TypeTab) ShowAll() {
fmt.Println("----------TypeTab------------------------")
for i, e := range s.elements {
fmt.Printf("|@%d: {Type: %s}\t\t\t|\n", i+1, e)
}
fmt.Println("-----------------------------------------")
fmt.Println("")
}

类型检查发生在变量定义的过程中,也就是 parseStmt_var 函数,往其中添加有关类型检查的代码:

1
2
3
4
5
6
7
8
9
if typ, ok := p.AcceptToken(token.IDENT); ok {
varSpec.Type = &ast.Ident{
NamePos: typ.Pos,
Name: typ.Literal,
}
if ok = p.TypeTab.CheckType(typ.Literal); !ok {
p.errorf(typ.Pos, "unknown type: %s", typ.Literal)
}
}

错误类型:重复类型定义

该错误发生在 type 语句,需要在 parseStmt_type 函数中添加对应的检查代码:

1
2
3
if ok := p.TypeTab.Push(tokIdent.Literal); !ok {
p.errorf(tokIdent.Pos, "duplicate type name: %s", tokIdent.Literal)
}

错误类型:类型不匹配

目前该编译器支持4种类型(bool,char,int,string),而需要修改的地方一共有3处

用于匹配类型的函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func (s *TypeTab) MatchType(left string, right interface{}) bool {
myType := reflect.TypeOf(right)
switch {
case myType.String() == "*ast.Strings":
if left != "string" {
return false
}
case myType.String() == "*ast.Number":
if left != "int" {
return false
}
case myType.String() == "*ast.Bool":
if left != "bool" {
return false
}
case myType.String() == "*ast.Char":
if left != "char" {
return false
}
}
return true
}

修改 parseStmt_var,检查变量初始化的类型是否匹配:

1
2
3
4
5
6
7
if _, ok := p.AcceptToken(token.ASSIGN); ok {
varSpec.Value = p.parseExpr()

if ok = p.TypeTab.MatchType(varSpec.Type.Name, varSpec.Value); !ok {
p.errorf(tokIdent.Pos, "The variable:%s type:%s not match", varSpec.Name.Name, varSpec.Type.Name)
}
}

修改 parseStmt_block,检查赋值语句的类型是否匹配:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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.IdentAS, len(exprList)),
OpPos: tok.Pos,
Op: tok.Type,
Value: make([]ast.Expr, len(exprList)),
}
for i, target := range exprList {
assignStmt.Target[i] = target.(*ast.IdentAS)
assignStmt.Value[i] = exprValueList[i]

if ok := p.TypeTab.MatchType(assignStmt.Target[i].Name, assignStmt.Value[i]); !ok {
p.errorf(tok.Pos, "The variable:%s type not match", assignStmt.Target[i].Name)
}
}
block.List = append(block.List, assignStmt)

修改 parseExpr_binary,字符串和布尔不能参与运算:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
op := p.PeekToken()
if op.Type.Precedence() < prec {
return x
}

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

p.MustAcceptToken(op.Type)
y := p.parseExpr_binary(op.Type.Precedence() + 1)
x = &ast.BinaryExpr{OpPos: op.Pos, Op: op.Type, X: x, Y: y}

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