EagleBear2002 的博客

这里必须根绝一切犹豫,这里任何怯懦都无济于事

Lab3-类型检查和重命名

实现功能

本次实验完成了以下功能:

  1. 类型检查
  2. 重命名

实验设计

笔者参考老师上课的演示代码和助教在文档中的提示,做出如下类设计。该设计采取了依赖倒置原则。

在实现功能方面,笔者采用了如下思路:

  1. 通过 Visitor 对语法树进行遍历,遍历过程中输出语义错误;
  2. visitTerminal 方法中,对每个 Symbol 记录变量名、函数名的出现位置,并存储待输出语法树内容;
  3. 如果语法树中无语义错误,则输出语法树的内容,输出过程中对待重命名的变量、函数进行重命名。

整个过程总共遍历语法树一遍

调用入口设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Main {
public static void main(String[] args) throws IOException {
// do something
ParseTree tree = sysYParser.program();
Visitor visitor = new Visitor();
visitor.visit(tree);

if (!visitor.getErrorFound()) {
for (Object obj : visitor.getMsgToPrint()) {
if (obj instanceof Symbol) {
if (((Symbol) obj).findUsage(lineNo, columnNo)) {
System.err.print(name);
} else {
System.err.print(((Symbol) obj).getName());
}
} else {
System.err.print(obj);
}
}
}
}
}

遍历过程 Visitor 设计

该类在项目中共 471 行,以下是 Visitor 类实现遍历的核心逻辑:

  1. 获得报错行号的方法:第 47 行
  2. 获得行号列号的方法:第 8 行
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
public class Visitor extends SysYParserBaseVisitor<Void> {
@Override
public Void visitTerminal(TerminalNode node) {
// 一些前置工作
if (ruleName.equals("INTEGR_CONST")) {
tokenText = toDecimalInteger(tokenText);
} else if (ruleName.equals("IDENT")) {
int lineNO = token.getLine();
int columnNO = token.getCharPositionInLine();
if (symbol != null) {
symbol.addUsage(lineNO, columnNO);
}
}

if (!color.equals("no color")) {
// 在 msgToPrint 中加入需要输出的语法树的内容
}

return super.visitTerminal(node);
}

@Override
public Void visitProgram(SysYParser.ProgramContext ctx) {
// 进入新的 Scope
Void ret = super.visitProgram(ctx);
// 回到上一层 Scope
return ret;
}

@Override
public Void visitFuncDef(SysYParser.FuncDefContext ctx) {
// 报告 Error type 4
// 进入新的 Scope,定义新的 Symbol
Void ret = super.visitFuncDef(ctx);
// 回到上一层 Scope
return ret;
}

@Override
public Void visitBlock(SysYParser.BlockContext ctx) {
// 进入新的 Scope
Void ret = super.visitBlock(ctx);
// 回到上一层 Scope
return ret;
}

int getLineNo(ParserRuleContext ctx) {
return ctx.getStart().getLine();
}

@Override
public Void visitVarDecl(SysYParser.VarDeclContext ctx) {
for (SysYParser.VarDefContext varDefContext : ctx.varDef()) {
// 报告 Error type 3
if (varDefContext.ASSIGN() != null) {
// 报告 Error type 5
}
// 定义新的 Symbol
}

return super.visitVarDecl(ctx);
}

@Override
public Void visitConstDecl(SysYParser.ConstDeclContext ctx) {
// 结构同 visitVarDecl
return super.visitConstDecl(ctx);
}

@Override
public Void visitFuncFParam(SysYParser.FuncFParamContext ctx) {
// 报告 Error type 3
// 定义新的 Symbol
return super.visitFuncFParam(ctx);
}

private Type getLValType(SysYParser.LValContext ctx) {
return varType;
}


@Override
public Void visitLVal(SysYParser.LValContext ctx) {
// 报告 Error type 1

for (int i = 0; i < arrayDimension; ++i) {
// 报告 Error type 9
}

return super.visitLVal(ctx);
}

@Override
public Void visitStmt(SysYParser.StmtContext ctx) {
if (ctx.ASSIGN() != null) {
// 报告 Error type 11
// 报告 Error type 5
} else if (ctx.RETURN() != null) {
// 报告 Error type 7
}
return super.visitStmt(ctx);
}

private Type getExpType(SysYParser.ExpContext ctx) {
if (ctx.IDENT() != null) { // IDENT L_PAREN funcRParams? R_PAREN
} else if (ctx.L_PAREN() != null) { // L_PAREN exp R_PAREN
} else if (ctx.unaryOp() != null) { // unaryOp exp
} else if (ctx.lVal() != null) { // lVal
} else if (ctx.number() != null) { // number
} else if (ctx.MUL() != null || ctx.DIV() != null || ctx.MOD() != null || ctx.PLUS() != null || ctx.MINUS() != null) {
}
return new BasicTypeSymbol("noType");
}

@Override
public Void visitExp(SysYParser.ExpContext ctx) {
if (ctx.IDENT() != null) { // IDENT L_PAREN funcRParams? R_PAREN
// 报告 Error type 2
// 报告 Error type 10
// 报告 Error type 8
} else if (ctx.unaryOp() != null) { // unaryOp exp
// 报告 Error type 6
} else if (ctx.MUL() != null || ctx.DIV() != null || ctx.MOD() != null || ctx.PLUS() != null || ctx.MINUS() != null) {
// 报告 Error type 6
}
return super.visitExp(ctx);
}

@Override
public Void visitCond(SysYParser.CondContext ctx) {
// 报告 Error type 6
return super.visitCond(ctx);
}
}

测试用例

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
int globalVar;
int globalArray[2];

void voidFunc1() {
return;
}

void voidFunc2(int param1) {
return 1; // type 7
}

void voidFunc3(int param1, int param2[]) {
return param1; // type 7
}

int intFunc1() {
return 1;
}

int intFunc1(int param) { // type 4
return intFunc1(); // valid
}

int intFunc2(int param1) {
return intFunc2(param1); // valid
}

int intFunc3(int param1, int param2[]) {
return param2; // type 7
}

int voidFunc1() { // type 4

}

int test(int i, int i){ // type 3
return test(i + 1); // valid
}

int main(){
a = 3; // type 1
int b = func(); // type 2
int b; // type 3

int c[3][3][3];
int d[2][2];
c[1] = d; // valid
c[1][1] = d; // type 5
c[1][1] = d; // type 5
int f = voidFunc1() + intFunc1(); // type 6
int g = intFunc3(b); // type 8
int h = intFunc3(b, globalArray); // valid

int i = b[5]; // type 9
int j = c(); // type 10
c = 6; // type 5
voidFunc1 = 6; // type 11

if (1 || globalArray || 2 || d) { // type 6 for 3 times

}

int k = voidFunc1(); // type 5
int l = voidFunc(); // type 2
int m = k;
return z(); // type 2
}