0%

ugo-lab4-字符串&数组&结构体

首先需要在 token.go 中添加对应的定义:

1
2
3
4
5
6
7
8
const (
......
STRINGS
ARRAY
TYPE
STRUCT
......
)
1
2
3
4
5
6
7
8
var tokens = [...]string{
......
STRINGS: "STRINGS",
ARRAY: "ARRAY",
TYPE: "type",
STRUCT: "struct",
......
}

字符串处理

在词法分析阶段,只需要添加对 “双引号” 的处理即可:

1
2
3
4
5
6
7
case r == '"':
for {
if r := p.src.Read(); r == '"' {
p.emit(token.STRINGS)
break
}
}

在语法分析阶段,字符串 AST 结点的结构可以仿造 Number:

1
2
3
4
5
6
7
// 一个字符串
type Strings struct {
ValuePos int // 字符串的开始位置
ValueEnd int // 字符串的结束位置
Value string // 字符串
ValueLen int // 字符串长度
}

字符串通常会作为表达式出现,因此需要在 parseExpr_primary 中添加与字符串有关的处理:

1
2
3
4
5
6
7
case token.STRINGS: // 字符串
tokStrings := p.MustAcceptToken(token.STRINGS)
return &ast.Strings{
ValuePos: tokStrings.Pos + 1,
ValueEnd: tokStrings.Pos + int(len(tokStrings.Literal)) - 1,
Value: tokStrings.Literal[1 : len(tokStrings.Literal)-1],
}

数组处理

数组的处理分为数组定义和数组使用这两个过程,首先需要定义 “[]” 的符号:

1
2
3
4
5
6
const (
......
LSB // [
RSB // ]
......
)
1
2
3
4
5
6
var tokens = [...]string{
......
LSB: "[",
RSB: "]",
......
}

然后在语法分析中添加对 “[]” 的识别:

1
2
3
4
case r == '[':
p.emit(token.LSB)
case r == ']':
p.emit(token.RSB)

另外还需要修改变量的 AST 结点结构:

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

// 一个变量 && 数组
type IdentArr struct {
NamePos int // 字符位置
Name string // 字符
Offset Expr // 数组位置
}
  • 其实就是在变量的基础上添加了一个 Expr 用于记录数组偏移

数组定义的格式如下:

1
var arrName [arrLen]arrType

对比于普通变量的定义多了 [arrLen]arrType,因此直接对 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
27
28
29
30
31
32
33
34
35
36
37
38
39
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.IdentArr{
NamePos: tokIdent.Pos,
Name: tokIdent.Literal,
Offset: nil,
}

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

if _, ok := p.AcceptToken(token.LSB); ok { /* 若检测到'['则为Offset添加数据 */
varSpec.Name.Offset = p.parseExpr()
p.AcceptToken(token.RSB)

typ, _ := p.AcceptToken(token.IDENT)
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
}

数组多用于表达式中,需要在 parseExpr_primary 添加对于数组的处理:

1
2
3
4
5
6
7
8
9
10
11
case token.IDENT: // 变量 && 数组
p.MustAcceptToken(token.IDENT)
asti := &ast.IdentArr{
NamePos: tok.Pos,
Name: tok.Literal,
}
if _, ok := p.AcceptToken(token.LSB); ok { /* 检测'['判断是否为数组 */
asti.Offset = p.parseExpr()
p.AcceptToken(token.RSB)
}
return asti

为了与赋值语句匹配,最后需要修改 parseStmt 和 parseStmt_block 的部分代码:

1
2
3
4
5
6
7
8
9
10
var assignStmt = &ast.AssignStmt{
Target: make([]*ast.IdentArr, len(exprList)),
OpPos: tok.Pos,
Op: tok.Type,
Value: make([]ast.Expr, len(exprList)),
}
for i, target := range exprList {
assignStmt.Target[i] = target.(*ast.IdentArr)
assignStmt.Value[i] = exprValueList[i]
}

结构体处理

在词法分析阶段,需要添加对 “type” 和 “struct” 这两个关键词的匹配:

1
2
3
4
5
6
var tokens = [...]string{
.....
TYPE: "type",
STRUCT: "struct",
.....
}

另外需要将它们添加入关键词列表中:

1
2
3
4
5
6
var keywords = map[string]TokenType{
......
"type": TYPE,
"struct": STRUCT,
......
}

结构体的 AST 结点结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
// 类型定义
type TypeSpec struct {
Name *Ident // 类型名
Type *Ident // 类型
}

// 结构体定义
type StructType struct {
TypePos int // type 关键字位置
Name *Ident // 结构体名字
Types []*TypeSpec // 类型列表
}

结构体定义发生在全局,需要先修改 parseFile 函数:

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
func (p *Parser) parseFile() {
p.file = &ast.File{
Filename: p.Filename(),
Source: p.Source(),
}

p.file.Pkg = p.parsePackage()

for {
switch tok := p.PeekToken(); tok.Type {
case token.EOF:
return
case token.ERROR:
panic(tok)
case token.SEMICOLON:
p.AcceptTokenList(token.SEMICOLON)
case token.FUNC:
p.file.Funcs = append(p.file.Funcs, p.parseFunc())
case token.VAR: /* 全局变量处理 */
p.file.Globals = append(p.file.Globals, p.parseStmt_var())
case token.TYPE: /* 结构体处理 */
p.file.Types = append(p.file.Types, p.parseStmt_type())
default:
p.errorf(tok.Pos, "unknown token: %v", tok)
}
}
}

核心函数 parseStmt_type 如下:

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
func (p *Parser) parseStmt_type() *ast.StructType {
tokpos := p.MustAcceptToken(token.TYPE)
tokIdent := p.MustAcceptToken(token.IDENT)

var StructType = &ast.StructType{
TypePos: tokpos.Pos,
}

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

switch tok := p.PeekToken(); tok.Type {
case token.IDENT:
tokty := p.MustAcceptToken(token.IDENT)
StructType.Types = append(StructType.Types, &ast.TypeSpec{
Name: nil,
Type: &ast.Ident{
NamePos: tokty.Pos,
Name: tokty.Literal,
},
})
case token.STRUCT:
if _, ok := p.AcceptToken(token.STRUCT); ok {
p.AcceptToken(token.LBRACE)

for {
if _, ok := p.AcceptToken(token.RBRACE); ok {
break
}
if toks, ok := p.AcceptTokenList(token.IDENT); ok {
StructType.Types = append(StructType.Types, &ast.TypeSpec{
Name: &ast.Ident{
NamePos: toks[0].Pos,
Name: toks[0].Literal,
},
Type: &ast.Ident{
NamePos: toks[1].Pos,
Name: toks[1].Literal,
},
})
}
p.ReadToken()
}
} else {
panic("Grammatical parsing errors")
}
default:
p.errorf(tok.Pos, "unknown tok: type=%v, lit=%q", tok.Type, tok.Literal)
}

p.AcceptToken(token.SEMICOLON)
return StructType
}
  • 这里分别实现了 type 的两种用法

结构体多用于表达式中,需要在 parseExpr_primary 添加对于结构体的处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
case token.IDENT: // 变量 && 数组 && 结构体
p.MustAcceptToken(token.IDENT)
asti := &ast.IdentArr{
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()
}
return asti