1.编译器简介

(1) 简介

该编译器通过Java语言,实现了词法分析,语法分析,符号表管理与错误处理,llvm代码生成功能,将输入的简化的符合SysY语法的代码转换为LLVM语言。
具体语法见文法定义及相关说明

(2) 错误处理

本编译器共可处理10种错误具体如下:

错误类型错误代码解释
非法符号a格式字符串中出现非法字符,如&等,报错行号为所在行数
名字重定义b函数名或者变量名在当前作用域下重复定义
未定义的名字c使用了未定义的标识符报错行号为所在行数
函数参数个数不匹配d函数调用语句中,参数个数与函数定义中的参数个数不匹配
函数参数类型不匹配e函数调用语句中,参数类型与函数定义中对应位置的参数类型不匹配
无返回值的函数存在不匹配的return语句f报错行号为‘return’所在行号
有返回值的函数缺少return语句g只需要考虑函数末尾是否存在return语句,无需考虑数据流
不能改变常量的值h为常量时,不能对其修改。报错行号为所在行号
缺少分号i报错行号为分号前一个非终结符所在行号
缺少右小括号j报错行号为右小括号前一个非终结符所在行号
缺少右中括号k报错行号为右中括号前一个非终结符所在行号
printf中格式字符与表达式个数不匹配l报错行号为‘printf’所在行号
在非循环块中使用break和 continue语句m报错行号为‘break’与’continue’所在行号

(3) 生成结果展示

输入代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int main(){
int i = 0;
int a[10];
for(i = 0;i < 10;i = i + 1){
a[i] = getint();
}
for(i = 0;i < 10;i = i+1){
printf("test%d\n",i);
int b;
b = a[i] - i;
if(b >= 5){
printf("a[i] - i > 5\n");
}
}
return 0;
}

输出

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
declare i32 @getint()
declare void @putint(i32)
declare void @putch(i32)
declare void @putstr(i8*)

define dso_local i32 @main() {
%x1 = alloca i32
store i32 0, i32* %x1
%x2 = alloca [10 x i32] ;
br label %block1

block1:
%x3 = load i32, i32* %x1
store i32 0, i32* %x1
br label %block2

block2:
%x4 = load i32, i32* %x1
%x5 = icmp slt i32 %x4, 10
%x6 = zext i1 %x5 to i32
%x7 = icmp ne i32 0, %x6
br i1 %x7, label %block3, label %block4

block3:
%x9 = load i32, i32* %x1
%x10 = getelementptr [10 x i32], [10 x i32]*%x2, i32 0, i32 %x9
%x11 = load i32, i32* %x10
%x12 = call i32 @getint()
store i32 %x12, i32* %x10
br label %block5

block5:
%x13 = load i32, i32* %x1
%x14 = load i32, i32* %x1
%x15 = add i32 %x14, 1
store i32 %x15, i32* %x1
br label %block2

block4:
br label %block6

block6:
%x16 = load i32, i32* %x1
store i32 0, i32* %x1
br label %block7

block7:
%x17 = load i32, i32* %x1
%x18 = icmp slt i32 %x17, 10
%x19 = zext i1 %x18 to i32
%x20 = icmp ne i32 0, %x19
br i1 %x20, label %block8, label %block9

block8:
%x22 = load i32, i32* %x1
call void @putch(i32 116)
call void @putch(i32 101)
call void @putch(i32 115)
call void @putch(i32 116)
call void @putint(i32 %x22)
call void @putch(i32 10)
%x23 = alloca i32
%x24 = load i32, i32* %x23
%x25 = load i32, i32* %x1
%x26 = getelementptr [10 x i32], [10 x i32]*%x2, i32 0, i32 %x25
%x27 = load i32, i32* %x26
%x28 = load i32, i32* %x1
%x29 = sub i32 %x27 ,%x28
store i32 %x29, i32* %x23
br label %block11

block11:
%x30 = load i32, i32* %x23
%x31 = icmp sge i32 %x30, 5
%x32 = zext i1 %x31 to i32
%x33 = icmp ne i32 0, %x32
br i1 %x33, label %block12, label %block13

block12:
call void @putch(i32 97)
call void @putch(i32 91)
call void @putch(i32 105)
call void @putch(i32 93)
call void @putch(i32 32)
call void @putch(i32 45)
call void @putch(i32 32)
call void @putch(i32 105)
call void @putch(i32 32)
call void @putch(i32 62)
call void @putch(i32 32)
call void @putch(i32 53)
call void @putch(i32 10)
br label %block13

block13:
br label %block10

block10:
%x35 = load i32, i32* %x1
%x36 = load i32, i32* %x1
%x37 = add i32 %x36, 1
store i32 %x37, i32* %x1
br label %block7

block9:
ret i32 0
}

2.编译器的总体设计

(1) 总体结构

程序总体架构如图:


程序运行后通过词法分析生成WordList, 输入到语法分析中生成Ast(抽象语法树),将Ast输入到错误处理部分中构建符号表,进行错误处理,无错误的抽象语法树会传入到代码生成部分生成LLVM代码,若存在错误则输出错误后程序结束,所有模块皆可通过调用Writer模块输出本模块的处理结果

  • Writer:将结果输出到对应文件
  • 词法分析:遍历按行读入testfile.txt中源程序,经过词法分析得到WordList
  • 语法分析:遍历WordList递归下降生成抽象语法树
  • 错误处理:遍历抽象语法树,构建符号表进行错误处理
  • 代码生成:遍历抽象语法树,生成LLVM代码

(2) 接口设计

项目文件组成和类与接口示意图如下:

  • Compiler:程序入口
  • Writer:文件写入模块,用于输出各模块处理结果
  • Lexer:词法分析模块
  • Parse:语法分析模块
  • Vistor:错误处理模块
  • Llvmgenerator:代码生成模块
  • 词法分析相关类:
    • Word:单词类,含该词的类别,内容,行号字段
    • WordType:枚举类,枚举单词类别
  • 语法分析相关类:
    • SyntaxWord:抽象语法树节点,用于构建抽象语法树
  • 错误处理相关类:
    • error:错误类,包含行号,错误类型字段
    • ErrorLog:错误输出单例,内含ArrayList,用于储存所有错误,及用于错误输出的接口
    • Symbol:符号类,内含构建符号表所需字段
    • SymbolTable:符号表类
  • 代码生成相关:
    • Symbol2:代码生成所使用的符号类,内含用于代码生成的符号信息
    • AddIR,MultIR···ZextIR:计算相关的llvmIR类,用于输出对应LLVM语句
    • BrIR,CallIR,IcmpIR,ReturnIR:跳转相关的llvmIR类,用于输出对应LLVM语句
    • AllocaIR,LoadIR,StoreIR:存储相关的llvmIR类,用于输出对应LLVM语句

(3) 文件组织

程序文件结构如下:

  • Compiler.java:程序入口
  • testfile.txt:待编译的源程序
  • output.txt:语法分析结果
  • error.txt:错误信息
  • llvm_ir.txt:llvm代码输出

3.词法分析设计

编码前设计

编码前设计Lexer模块逻辑如下:

由Compiler模块持续按行读入文件,将读入行内容和行号传入Lexer模块,Lexer模块循环读入单个字符进行处理

编码后修改

对于各类符号单词,原本设计存入HashMap中,传入该分支后用if else 进行分类处理,后改为使用LinkedHashMap,在处理完其他情况后使用for循环遍历处理,缩短了代码行数,提高了代码的简洁性。
对于注释的处理原本设计使用预读,逻辑为对读取到的’/‘的后继符号进行判断,若存在’/‘则为单行注释,存在’*‘为多行注释,其他情况则当前’/‘当做除号处理,回退读指针,编码完成后多次测试发现存在bug,未考虑到’/'为该行末尾符号,无后继符号,应当作为除号处理,后添加对行末符号的判断处理逻辑。

4.语法分析设计

编码前设计

向Parse模块中传入Lexer模块生成的WordList,遍历该列表的单词,递归下降处理各语法成分,对于含有左递归的文法,如AddExp,LOrExp等,为解决回溯问题使用预读进行处理。

编码后修改

1.左递归文法

对于处理左递归的方式,实际选用如下方式处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private SyntaxWord AddExp() {
SyntaxWord addExp = createMiddle("AddExp");
SyntaxWord Father = createMiddle("AddExp");
addExp.addChild(MulExp());
while (symTpyeCheck(sym, WordType.PLUS) || symTpyeCheck(sym, WordType.MINU)){
Father.addChild(addExp);
res.add("<AddExp>");
Father.addChild(createTerminal(sym));// + -
addExp = Father;
nextSym();
addExp.addChild(MulExp());
}
res.add("<AddExp>");
return addExp;
}

对于需要处理的AddExp,当前单词直接当做MulExp处理,添加到当前AddExp的子节点中,若该单词后继为’+‘||’-',则为当前的AddExp(含一MulExp的子节点)创建一个AddExp的父节点Father,为运算符号创建节点,添加仅Father,将后继单词作为MulExp处理,添加仅Father节点,最后返回Father节点。显然Father节点只在while循环外创建了一次,由于Father.addChild(addExp); addExp = Father;,当循环第二次运行时,Father节点与addExp节点是相同的,导致addExp中一子节点会指向自己,导致在遍历语法树的时候死循环,且由于在语法分析部分,处理结果都是在递归下降过程中添加字符串到res数组中,最后遍历输出数组产生,而不是由遍历抽象语法树生成,导致此Bug没有被发现,且由于Father节点在while循环之外被创建,对于例如1+1的输入可以生成期望的抽象语法树,但对于多层AddExp嵌套,如输入2+3-4+5会生成错误的语法树。

错误情况: AddExp的Child中存在自身,遍历树的时候产生死循环

后修复为如下代码,在while循环中新建Father对象,使得多次循环中Father改变,不会导致addExp的child中有指向自己的变量。其余含左递归的文法皆做相同处理修复BUG。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private SyntaxWord AddExp() {
SyntaxWord addExp = createMiddle("AddExp");
addExp.addChild(MulExp());
while (symTpyeCheck(sym, WordType.PLUS) || symTpyeCheck(sym, WordType.MINU)) {
SyntaxWord Father = createMiddle("AddExp"); // 创建新的 Father 对象
Father.addChild(addExp);
res.add("<AddExp>");
Father.addChild(createTerminal(sym)); // + -
nextSym();
Father.addChild(MulExp());
addExp = Father; // 将 addExp 更新为新的 Father
}
res.add("<AddExp>");
return addExp;
}

2.Stmt处理

在处理Stmt的[Exp];LVal = Exp;情况时,由于LVal和Exp的First集都含有Ident,对判断逻辑的编码产生了很大阻碍,最后采取对读取到的Ident记录其下标,并都按照LVal处理建立对应节点,当为[Exp];情况时则不将LVal节点加入Stmt的子节点中,同时调用back函数清除添加到res数组中的错误字符串,在back函数编写过程中,起先未考虑到LVal可能由多个终结符构成,即LVal为数组时,Ident后续的单词也会被加入LVal的子节点中,这些结果也是错误的需要回退,最终修改为如下代码,正确处理了Stmt节点,解决了back函数的BUG。

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
else if (symTpyeCheck(sym, WordType.IDENFR)) {
int resNowSize = res.size();
int oldIndex = index;//记录处理前指针位置
SyntaxWord tmp = LVAL();
if (symTpyeCheck(sym, WordType.ASSIGN)){//存在= ,为LVal = 情况,tmp为LVAl
stmt.addChild(tmp);
stmt.addChild(createTerminal(sym));
nextSym();
// LVal = getint()
if(symTpyeCheck(sym,WordType.GETINTTK)){
stmt.setStmtType("Input");
stmt.addChild(createTerminal(sym));
nextSym();
if(symTpyeCheck(sym, WordType.LPARENT)){//(
stmt.addChild(createTerminal(sym));
nextSym();
if (symTpyeCheck(sym, WordType.RPARENT)){//)
stmt.addChild(createTerminal(sym));
nextSym();
if (symTpyeCheck(sym, WordType.SEMICN)){//;
stmt.addChild(createTerminal(sym));
nextSym();
}else {
ErrorLog.instance.addError(getPreWordLine(), "i");
}
}else{
ErrorLog.instance.addError(getPreWordLine(), "j");
}
}
}else{// LVal = Exp;
stmt.setStmtType("Assignment");
stmt.addChild(Exp());
if (symTpyeCheck(sym, WordType.SEMICN)){
stmt.addChild(createTerminal(sym));
nextSym();
}else{
ErrorLog.instance.addError(getPreWordLine(), "i");
}

}
}else{//不存在=,为Exp;情况
back(resNowSize, oldIndex);//回退当前指针与添加的结果res
stmt.setStmtType("Exp");
stmt.addChild(Exp());
if(symTpyeCheck(sym, WordType.SEMICN)){//;
stmt.addChild(createTerminal(sym));
nextSym();
}else{
//e
ErrorLog.instance.addError(getPreWordLine(), "i");
}
}
}
private void back(int oldSize, int oldIndex) {
int nowSize = res.size();
for(int i = nowSize - 1; i >= oldSize; i--){
res.remove(i);
}
index = oldIndex - 1;//index - (index - oldIndex + 1)
nextSym();
}

5.错误处理设计

编码前设计

经对所有错误类型分析,做出如下分配,a类错误在Lexer模块中处理,i,j,k类错误在Parse模块中处理,其余在错误在模块Vistor中处理。分析发现构建符号表仅需要对抽象语法树中FuncDef、Block、VarDef、MainFuncDef、LVal、Stmt、ConstDef、UnaryExp节点进行处理则设计编写traverseTree函数对抽象语法树前序遍历,handle函数使用if 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
public class Symbol {
private String token;//当前单词所对应的字符串
private int type;//用于区分LVal类型或获取数组维度,其中0 -> a, 1 -> a[], 2 -> a[][], -1 -> func
private int con;//是否为const,其中1 -> const, 0 -> var
boolean isFunc = false;
class Func{// type == -1时使用,存放函数相关信息
int retype;// 0 -> int, 1->void
int paramNum; //参数数量
ArrayList<Symbol> parameters;//参数符号数组
}
Func func = new Func();
public Symbol(String token, int type, int con){//创建普通符号
this.con = con;
this.token = token;
this.type = type;
}
public Symbol(String token, int type, int con, int retype, int paramNum, ArrayList<Symbol> parameters){//创建函数符号
this.type = type;
this.token = token;
this.con = con;
this.func.parameters = parameters;
this.func.paramNum = paramNum;
this.func.retype = retype;
}
}

对于符号表,使用HashMap<Interger, SymbolTable> floor_symbolTable存储符号表类,floor对程序层级进行计数,处理到Block则floor++,处理完毕后floor--, 对于函数,使用HashMap<String, Symbol> funcName2Symbol存储函数名对应的符号表,HashMap<String, Table> funcName2parTable 存储函数名对应的参数符号表。

编码后修改

1.符号表实现

实际编码中对符号表结构做出修改,不使用预先设计的HashMap<Interger, SymbolTable> floor_symbolTable存储符号表实现符号表嵌套关系,而是在符号表类中添加其父符号表指针,使用全局变量curTable指向当前处理的符号表,globelTable指向最顶级的符号表,符号表类设计如下:

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
public class SymbolTable {
String FuncName = null;//函数名
public static SymbolTable globelTable = new SymbolTable();//顶层符号表
public SymbolTable Father = null;//父级符号表
HashMap<String, Symbol> name_symbol = new HashMap<>();//参数名,对应符号表,处理函数参数时使用
private boolean isLoop = false;//当前符号表是否为循环
private boolean needReturn = false;//当前符号表是否需要return语句
public SymbolTable getFather(){return Father;}
public boolean exist(String name){//是否存在该函数名的参数
return name_symbol.containsKey(name);
}
public Symbol getSymbol(String name){
return name_symbol.getOrDefault(name, null);
}
public void addSymbol(String name,Symbol tmp){
name_symbol.put(name, tmp);
}
public String getFName(){
return FuncName;
}
public void setFuncName(String s){
this.FuncName = s;
}
public void setIsLoop(boolean flag){
isLoop = flag;
}
public boolean getIsLoop(){
return isLoop;
}
}

for循环语句处理

在编码中对于Stmt -> 'for' '(' [ForStmt] ';' [Cond] ';' [forStmt] ')' Stmt情况中最后的Stmt节点直接默认其为Stmt -> Block情况,未考虑如下情况且通过了错误处理测试,导致后续开启错误处理代码生成时产生BUG。

1
2
3
4
5
6
7
//for语句后非Block情况
for(;;);

int i,j;
for(i = 0;i < 10;i = i + 1)
for(j = 0;j < 10;j = j + 1)
···

对于上述BUG,显然创建新的Block节点加入终结符节点LBRACE,非终结符节点BlockItem及终结符节点RBRACE,将for末尾的Stmt节点从取出添加到新建的BlockItem节点并从父级Stmt节点删除,向父级Stmt节点添加Block节点对程序的运行结果是没有影响的,因此后续添加在处理逻辑中添加了wrapWithBlock函数向其中传入Stmt节点返回用Block包装后的Block节点,对for语句末尾Stmt的类型判断,若为非Block情况则调用wrapWithBlock函数包装后处理。

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
private void Stmt(SyntaxWord syntaxWord) {
ArrayList<SyntaxWord> children = syntaxWord.getChildren();
if(children.get(0).isEnd()){
String token = syntaxWord.getChild0Content();
if(token.equals("for")) {
SyntaxWord stmt = children.get(children.size() - 1);
//stmt child0 isEnd 或非end type 不是Block时,用block包装后处理
//new:
//-------------------------------------------------
if(!stmt.isEnd()){
if(stmt.getChildren().get(0).isEnd() || !stmt.getChildren().get(0).getType().equals("Block")){
children.remove(children.size() - 1);
children.add(wrapWithBlock(stmt));
stmt = children.get(children.size() - 1);
}
}
//-------------------------------------------------
if(!stmt.isEnd() && stmt.getChildren().get(0).getType().equals("Block")){
isLoop = true;
}
stmt.setLoop(true);
}
···
}else{
···
}
}
private SyntaxWord wrapWithBlock(SyntaxWord stmt) {
SyntaxWord Stmt = new SyntaxWord("Stmt",null);
SyntaxWord L = new SyntaxWord(null, new Word(-1,WordType.LBRACE.toString(), "{"));
SyntaxWord R = new SyntaxWord(null, new Word(-1, WordType.RBRACE.toString(), "}"));
SyntaxWord blockItem = new SyntaxWord("BlockItem", null);
SyntaxWord block = new SyntaxWord("Block", null);
blockItem.addChild(stmt);
block.addChild(L);
block.addChild(blockItem);
block.addChild(R);
Stmt.addChild(block);
return Stmt;
}

6.代码生成设计

编码前设计

代码生成选择LLVM IR作为目标代码,设计参考课程网站上的LLVM指导手册。由于用于错误处理的SymbolTable类无法从父级表获得子表,设计用于代码生成的Symbol类如下,同时原抽象语法树节点类添加若干字段。利用LLVM编译时的特性,设计基本块命名为"block" + blockId,即字符串+数字形式,这样在编译时编译器会自动为块重新标号,这样在输出目标代码时可以不用保证块号递增,大大减轻对基本块标号的工作量,例如生成如下目标代码, 其块号不是单调递增的,通过LLVM编译后可正确运行。

1
2
3
4
5
6
7
8
9
block3:
···
br label %block2
block1:
···
br label %block2
block:
···
···

Symbol类:

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
public class Symbol2 {
int dim;//维度
int x;//数组一维大小
int y;//数组二维大小
String Addr = "i32";//地址类别,默认为i32
String intVal = "";//初始值
ArrayList<String> dim1= new ArrayList<>();//一维数组内容
String [][] dim2 = null;//二维数组内容

public Symbol2(int dim){//新建普通变量符号
this.dim = dim;
}
public Symbol2(int dim, int x){//新建一维数组符号
this.x = x;
this.dim = dim;
}
public Symbol2(int dim, int x, int y){//新建二维数组符号
this.x = x;
this.y = y;
this.dim = dim;
if(x == 0){
dim2 = new String[11111][y];
}else {
dim2 = new String[x][y];
}
}
public void setAddr(String s){
Addr = s;
}
public void setDim1(ArrayList<String> list) {
dim1.addAll(list);
}
public ArrayList<String> getDim1() {
return dim1;
}
public void setDim(int dim){
this.dim = dim;
}
public void setDim2(String[][] dim2) {
this.dim2 = dim2;
}
public String[][] getDim2() {
return dim2;
}
public String getAddr(){
return Addr;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getDim() {
return dim;
}
public void setIntVal(String intVal) {
this.intVal = intVal;
}
public String getIntVal() {
return intVal;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
if(x == 0){
dim2 = new String[11111][y];
}else {
dim2 = new String[x][y];
}
}
}

SyntaxWord类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class SyntaxWord {
boolean isLoop;
Word token;
String type;
ArrayList<SyntaxWord> children;.
//new:
Symbol2 symbol = null;//新增符号类
String returnType = "";//返回类型
String regId = ""; //LLVM变量id
String value = ""; //取值
int TrueId;// 用于处理条件跳转,为真时跳转的块号
int FalseId;//为假时跳转的块号
int StmtId = -1;//条件跳转产生的块号
int ContinueId = -1;//Continue跳转块号
int BreakId = -1;//Break跳转块号
}

继续使用递归下降遍历抽象语法树,处理各个节点,设计handle函数将输入入节点传入对应函数处理,其中LOrExp与LAndExp传入对应函数前记录该节点为顶级父节点,用于实现短路求职。

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
private void handle(SyntaxWord node){
if(node.isEnd()){
return;
}
if(node.typeCheck("CompUnit") || node.typeCheck("Decl") ||node.typeCheck("ConstDecl") || node.typeCheck("VarDecl")){
for (SyntaxWord child:node.getChildren()){
handle(child);
}
}else if (node.typeCheck("ConstDef")){
ConstDef(node);
} else if (node.typeCheck("ConstInitVal")) {
ConstInitVal(node);
} else if (node.typeCheck("ConstExp")) {
ConstExp(node);
} else if (node.typeCheck("VarDef")){
VarDef(node);
} else if (node.typeCheck("InitVal")) {
InitVal(node);
} else if (node.typeCheck("FuncDef")) {
FuncDef(node);
} else if (node.typeCheck("FuncFParams")){
FuncFParams(node);
} else if (node.typeCheck("FuncFParam")) {
FuncFparam(node);
} else if (node.typeCheck("MainFuncDef")) {
MainFuncDef(node);
} else if (node.typeCheck("Block")) {
Block(node);
} else if (node.typeCheck("Stmt")) {
Stmt(node);
} else if (node.typeCheck("ForStmt")) {
ForStmt(node);
} else if (node.typeCheck("Exp")){
Exp(node);
} else if (node.typeCheck("Cond")){
Cond(node);
} else if (node.typeCheck("LVal")) {
LVal(node);
} else if (node.typeCheck("NumBer")) {
Number(node);
} else if (node.typeCheck("FuncRParams")){
FuncRParams(node);
} else if (node.typeCheck("AddExp")) {
AddExp(node);
} else if (node.typeCheck("MulExp")) {
MulExp(node);
} else if (node.typeCheck("UnaryExp")){
UnaryExp(node);
} else if (node.typeCheck("PrimaryExp")) {
PrimaryExp(node);
} else if (node.typeCheck("LOrExp")) {
FatherLOr = node;
LOrExp(node);
FatherLOr = null;
} else if (node.typeCheck("LAndExp")) {
FatherLAnd = node;
LAndExp(node);
FatherLAnd = null;
} else if (node.typeCheck("EqExp")) {
EqExp(node);
} else if (node.typeCheck("RelExp")) {
RelExp(node);
}

}

对于目标代码设计继承IR接口的各语句类如下:

重写各类toString方法用于生成对应语句
例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MultIR implements IR {
private final String op1;
private final String op2;
private static final String operation = "mul";
private final String resReg;

public MultIR(String resReg, String op1, String op2){
this.op1 = op1;
this.op2 = op2;
this.resReg = resReg;
}
@Override
public String toString(){
return "\t" + resReg + " = " + operation + " i32" + " " + op1 +" ,"+op2;
}
}

对于仅运算符不同的IR语句设计calIR函数区分对应语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private String calIR(String leftValue, String op, String rightValue) {
String Ir = "";
String resAddress = "%x" + regId;
switch (op){
case "+"->{
Ir = new AddIR(resAddress, leftValue, rightValue).toString();
}
case "-"->{
Ir = new SubIR(resAddress, leftValue, rightValue).toString();
}
case "*"->{
Ir = new MultIR(resAddress, leftValue, rightValue).toString();
}
case "/"->{
Ir = new SDivIR(resAddress, leftValue, rightValue).toString();
}
case "%"->{
Ir = new SremIR(resAddress, leftValue, rightValue).toString();
}
}
return Ir;
}

符号表部分使用栈式符号表,全局变量表,函数表是哪个表实现

1
2
3
HashMap<String, SyntaxWord> global;
ArrayList<SyntaxWord> stack;
HashMap<SyntaxWord, Integer> floorMap = new HashMap<>();

可知代码生成时AddExp与MulExp唯一区别在于中间的运算符号不同,故合并到MulExp中处理,同时一些常数可以直接计算出值在进行代码生成,设计该节点及其子节点是否全为常数的判断逻辑,调用calculateNum函数计算该值。

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
private void AddExp(SyntaxWord node) {
MulExp(node);
}

private void MulExp(SyntaxWord node) {
ArrayList<SyntaxWord> children = node.getChildren();
SyntaxWord left = children.get(0);
handle(left);
String leftValue = left.getValue();
if(children.size() == 1){ // 单表达式不运算
//直接向上传值
node.setRegId(left.getRegId());
node.setValue(leftValue);
node.setSymbol(left.getSymbol());
}else{
//运算 add-> add (+ || -) mul
// mul (* || /) Unary
// 运算后向上传值
String op = children.get(1).getToken().getContent();
SyntaxWord right = children.get(2);
handle(right);
String rightValue = right.getValue();
boolean isAllNum = true;
for(SyntaxWord child:children){
if (!child.isEnd()&&!isNum(child.getValue())){
isAllNum = false;
break;
}
}
if(isAllNum){
right.setValue(calculateNum(leftValue, op, rightValue));
}else {
writeRes(calIR(leftValue, op, rightValue) + "\n");
right.setRegId("%x" + regId);
right.setValue("%x" + regId);
++regId;
}
node.setSymbol(right.getSymbol());
node.setValue(right.getValue());
}
}
private String calculateNum(String leftValue, String op, String rightValue) {
int left = Integer.parseInt(leftValue);
int right = Integer.parseInt(rightValue);
String res = "None";
switch(op){
case "+"->{res = String.valueOf(left + right);}
case "-"->{res = String.valueOf(left - right);}
case "*"->{res = String.valueOf(left * right);}
case "/"->{res = String.valueOf(left / right);}
case "%"->{res = String.valueOf(left % right);}
case "=="->{ res = String.valueOf((left == right) ? 1 : 0);}
case "!="->{ res = String.valueOf((left != right) ? 1 : 0);}
case ">"->{ res = String.valueOf((left > right) ? 1 : 0);}
case ">="->{ res = String.valueOf((left >= right) ? 1 : 0);}
case "<"->{ res = String.valueOf((left < right) ? 1 : 0);}
case "<="->{ res = String.valueOf((left <= right) ? 1 : 0);}
}
if (res.equals("None")){
System.out.println("None result error in caculateNum");
System.exit(-2);
}
return res;
}

短路求值,首先对于LAndExp的抽象语法树,当前节点子节点数为1时即该节点仅有1子节点EqExp,处理该EqExp,向上传值后结束,对于如图形式的抽象语法树,左杈LAnd节点继承其父节点的FalseId,递归处理左杈LAnd,处理完后创建新TrueBlock跳转到右杈EqExp块。当左杈LAnd节点的父节点为顶级LAnd节点时,顶级LAnd的TrueId,FalseId,StmtId同父级LOr节点,则在处理完最后的EqExp后续输出LOr节点对应LLVM IR语句。
LOrExp抽象语法树的处理基本同理,仅需注意左杈LOr节点要继承父节点TrueId新建FalseBlock跳转至右杈LAnd,当左杈LOr的父节点为顶级LOr节点时需要输出父级Cond节点对应LLVM IR语句

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
private void LAndExp(SyntaxWord node) {
// LAndExp → EqExp | LAndExp '&&' EqExp
ArrayList<SyntaxWord> children = node.getChildren();
if(children.size() == 1){//单节点无需计算直接向上传值
SyntaxWord eqExp = children.get(0);
handle(eqExp);
node.setValue(eqExp.getValue());
}else{ // 三节点,递归处理后传值
SyntaxWord lAndExp = children.get(0);
lAndExp.setFalseId(node.getFalseId());
LAndExp(lAndExp);
String resReg = "%x" + regId;
String left = "0";
String right = lAndExp.getValue();
writeRes(new IcmpIR(resReg, left, "ne", right) + "\n");
String trueBlock = "block" + blockId;
String falseBlock = "block" + node.getFalseId();
regId++;
writeRes(new BrIR(resReg, trueBlock, falseBlock) + "\n");
writeRes("\nblock" + blockId+":\n");
blockId++;
SyntaxWord eqExp = children.get(2);
handle(eqExp);
node.setValue(eqExp.getValue());
// 当前节点是顶级LAnd节点才需输出如下
if(node.equals(FatherLAnd)){
String cond = "%x" + regId;
resReg = cond;
right = eqExp.getValue();
trueBlock = "block" + node.getTrueId();
falseBlock= "block" + node.getFalseId();
++regId;
writeRes(new IcmpIR(cond, left, "ne", right) + "\n"
+ new BrIR(resReg, trueBlock, falseBlock) + "\n");
}
}
}
private void LOrExp(SyntaxWord node){
ArrayList<SyntaxWord> children = node.getChildren();
String TOPLORright;
if(children.size() == 1) {//单推LANd,传值后处理,进LAnd
SyntaxWord lAndExp = children.get(0);
lAndExp.setTrueId(node.getTrueId());
lAndExp.setFalseId(node.getFalseId());
lAndExp.setStmtId(node.getStmtId());
handle(lAndExp);
node.setValue(lAndExp.getValue());
TOPLORright = lAndExp.getValue();
}else{// 三节点,递归处理
// 左杈LOr true 同父级, False开新的给lAnd,
SyntaxWord lOrExp = children.get(0);
SyntaxWord lAndExp = children.get(2);
lOrExp.setTrueId(node.getTrueId());
lOrExp.setFalseId(blockId++);
LOrExp(lOrExp);
String resReg = "%x" + regId;
String left = "0";
String right = lOrExp.getValue();
writeRes(new IcmpIR(resReg, left, "ne", right) + "\n");
String trueBlock = "block" + node.getTrueId();
String falseBlock = "block" + lOrExp.getFalseId();
regId++;
writeRes(new BrIR(resReg, trueBlock, falseBlock) + "\n");
writeRes("\nblock" + lOrExp.getFalseId()+":\n");
blockId++;
lAndExp.setFalseId(node.getFalseId());
handle(lAndExp);
node.setValue(lAndExp.getValue());
TOPLORright = lAndExp.getValue();

}
//当前节点为顶级父节则需为当前CondBlock输出如下:
if(node.equals(FatherLOr)){
String cond = "%x" + regId;
String trueBlock = "block" + node.getTrueId();
String falseBlock= "block" + node.getFalseId();
++regId;
writeRes(new IcmpIR(cond, "0", "ne", TOPLORright) + "\n"
+ new BrIR(cond, trueBlock, falseBlock) + "\n");
}
}

LVal处理较为复杂,首先将LVal分为全局与局部,全局的变量可从global表中获取,之后又分为全局变量赋值和全局变量使用,变量类型为普通变量,一维数组与二维数组,局部变量需按对应变量类型(普通变量,一维数组与二维数组)处理。LVal节点子节点数可能为1,4,7,size == 7时最为简单,仅为对二维数组具体元素的使用,当size == 4时可能为一维数组具体元素的使用或二维数组的部分(二维数组的部分传参)使用,当size == 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
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
private void LVal(SyntaxWord node) {
//Ident size = 1
//Ident [Exp] size = 4
//Ident [Exp][Exp] size = 7
ArrayList<SyntaxWord> children = node.getChildren();
int LValType =children.size();
SyntaxWord ident = children.get(0);
String ident_name = ident.getToken().getContent();
SyntaxWord identSymbol = querySymbolOnStack(ident_name);//在栈上查询该变量的符号
Symbol2 sym = null;
if (identSymbol == null){//不在栈上则为全局变量
sym = global.get(ident_name).getSymbol();//获取全局变量符号
identSymbol = global.get(ident_name);
if(identSymbol == null){
//e
System.out.println("no Define");
}
if(floor == 0){// 全局变量赋值;
assert identSymbol != null;
switch (LValType){
case 1->{//全局普通变量赋值
node.setValue(identSymbol.getSymbol().getIntVal());
}
case 4->{//全局一维数组赋值
SyntaxWord exp = children.get(2);
Exp(exp);
int i = Integer.parseInt(exp.getValue());
node.setValue(identSymbol.getSymbol().getDim1().get(i));
}
case 7->{//全局二维数组赋值
SyntaxWord exp1,exp2;
exp1 = children.get(2);
exp2 = children.get(5);
Exp(exp1);
handle(exp2);
int x,y;
x = Integer.parseInt(exp1.getValue());
y = Integer.parseInt(exp2.getValue());
node.setValue(identSymbol.getSymbol().getDim2()[x][y]);
}
}
}else {
//块内使用全局变量
switch (LValType){
//二维数组元素使用
case 7->{
SyntaxWord exp1,exp2;
exp1 = children.get(2);
exp2 = children.get(5);
Exp(exp1);
handle(exp2);
int x,y;
x = sym.getX();
y = sym.getY();
String address = "%x" + regId;
regId++;
String srcReg = "%x" + regId;
regId++;
String geteIR = tab + address + " = getelementptr [" + x + " x [" + y + " x i32]], ["
+ x + " x [" + y + " x i32]]" + "* @" + ident_name + ", i32 0, i32 " + exp1.getValue()+
", i32 " + exp2.getValue() + "\n";
writeRes(geteIR);
writeRes(new LoadIR(srcReg, address) + "\n");
node.setValue(srcReg);
node.setRegId(address);
}
//一维数组元素使用 或 二维数组部分使用
case 4->{
SyntaxWord exp = children.get(2);
Exp(exp);
if(sym.getDim() == 1){
String res = "%x"+regId;
String base = "@" + ident_name;
int len = sym.getX();
String tmp ="\t" + res + " = getelementptr [" + len + " x i32]," +
" [" + len + " x i32]* " + base + ", i32 0, i32 " + exp.getValue()+ "\n";
writeRes(tmp);
writeRes(new LoadIR("%x"+(regId+1), res)+"\n");
sym.setAddr("i32");
regId++;
node.setValue("%x"+regId);
node.setRegId(res);
regId++;
}else if(sym.getDim() == 2){
String res = "%x" + regId;
String op1, op2;
op1 = exp.getValue();
op2 = String.valueOf(sym.getY());
regId++;
writeRes(new MultIR(res, op1, op2) + "\n");
int m, n;
m = sym.getX();
n = sym.getY();
writeRes(tab+"%x"+regId+" = getelementptr ["+m+" x ["+n+" x i32]], ["+m+" x ["+n+" x i32]]* @"+ident_name+", i32 0, i32 0\n");
regId++;
writeRes(GepIrDim2("%x" + regId, String.valueOf(n), "%x" + (regId - 1), "%x" + (regId - 2) + "\n"));
sym.setAddr("i32*");
node.setValue("%x" + regId);
node.setRegId("%x" + regId);
regId++;
}
}
//普通变量使用 或 传参一维数组首地址, 或传参二维数组首地址
case 1->{
int dim = sym.getDim();
switch (dim){
case 0->{
String address = "@" + ident_name;
String res = "%x" + regId;
regId++;
writeRes(new LoadIR(res, address) + "\n");
node.setValue(res);
node.setRegId(address);
}
case 1->{
String res = "%x" + regId;
String address = "@" + ident_name;
int len = sym.getX();
sym.setAddr("i32*");
writeRes(new GepIR(res, len, address) + "\n");
node.setValue(res);
node.setRegId(res);
regId++;
}
case 2->{
String reg = "%x" + regId;
int m,n;
m = sym.getX();
n = sym.getY();
sym.setAddr("[" + n + " x i32]*");
String s = tab + reg +" = getelementptr ["+ m +" x ["+ n +" x i32]], ["+ m +" x ["+ n +" x i32]]* "+("@" + ident_name)+", i32 0, i32 0\n";
writeRes(s);
node.setRegId(reg);
node.setValue(reg);
regId++;
}
}
}
}

}
}else {// 在栈式符号表上
sym = identSymbol.getSymbol();//获取栈上节点的符号
switch (LValType){
case 1->{
//使用栈上普通变量
int dim = sym.getDim();//获取变量维数
if(dim == 0){
//Ident 普通变量 = 普通变量
sym.setAddr("i32");
String resReg = "%x" + regId;
String address = identSymbol.getRegId();
PropagationLVal(node, resReg, address);//RegId ++ DIF: RegId += 2;
}
else if(dim == 1){
//使用栈上一维数组首地址
sym.setAddr("i32*");
/*
int func(int arr[]){
int brr[2] = {1,2};
printf("%d, arr[1]); //k.getX() == 0
printf("%d, brr[1]); //k.getX() != 0
}
*/
if(sym.getX() == 0){
//使用定义函数参数中一维数组首地址(传入的一维数组的首地址)
String reg1 = "%x" + regId;
String address = identSymbol.getRegId();
//%x341 = load i32, i32* %x302
//%v397 = load i32*, i32* * %v352
writeRes(tab+ reg1 + " = load i32*, i32* * "+address + "\n");
node.setValue(reg1);
node.setRegId(reg1);
regId++;
}else{
//使用块(父级块)内定义的一维数组的首地址
String res = "%x" + regId;
int len = sym.getX();
String base = identSymbol.getRegId();
writeRes(new GepIR(res, len, base) + "\n");
node.setValue(res);
node.setRegId(res);
regId++;
}
}
else if(dim == 2){
//使用栈上二维数组的首地址
/*
int func(int arr[]3){
int brr[2][2] = {{1,2},{1,2}};
printf("%d, arr[1][0]); //k.getX() == 0
printf("%d, brr[1][0]); //k.getX() != 0
}
*/
if (sym.getX() == 0){
String reg = "%x" + regId;
String address = identSymbol.getRegId();
int y = sym.getY();
sym.setAddr("["+y+" x i32]*");
String s = tab+reg+" = load ["+ y +" x i32] *, [" + y + " x i32]* * "+address+"\n";
writeRes(s);
node.setValue(reg);
node.setRegId(address);
++regId;
}else {
String nowReg = "%x" + regId;
int m, n;
m = sym.getX();
n = sym.getY();
String address = identSymbol.getRegId();
sym.setAddr("["+ n +" x i32]*");
String output = tab + nowReg + " = getelementptr [" + m + " x ["
+ n +" x i32]], ["
+ m +" x ["
+ n +" x i32]]*"
+ address
+", i32 0, i32 0";
writeRes(output + "\n");
node.setValue(nowReg);
node.setRegId(nowReg);
++regId;
}
}
}
case 4->{ //Ident[Exp]
//一维数组元素使用
int symbolDim = identSymbol.getSymbol().getDim();
int x = sym.getX();
SyntaxWord exp = children.get(2);
Exp(exp);
switch (symbolDim){
//使用栈上一维数组的元素
case 1-> {
/*
void func(int arr[]){
int brr[2] = {1,2};
int if_true = arr[0]; // x == 0;
int if_false = brr[0]; // x!= 0;
}
*/
sym.setAddr("i32");
if(x == 0){
String reg = "%x" + regId;
writeRes(tab + reg + " = load i32*, i32* * " + identSymbol.getRegId() + "\n");
writeRes(tab + "%x" + (regId+1) + " = getelementptr i32, i32* %x"+ regId +", i32 " + exp.getValue() + "\n");
regId++;
node.setRegId("%x" + regId);
writeRes(tab + "%x" + (1 + regId) + " = load i32, i32* %x" + regId + "\n");
regId++;
node.setValue("%x" + regId);
regId++;
} else {
int m = sym.getX();
writeRes(tab + "%x" + regId + " = getelementptr [" + m + " x i32], [" + m + " x i32]*" + identSymbol.getRegId() + ", i32 0, i32 " + exp.getValue() + "\n");
writeRes(tab + "%x"+(regId+1) + " = load i32, i32* %x" + regId + "\n");
node.setRegId("%x"+regId);
regId++;
node.setValue("%x"+regId);
regId++;
}
}
//使用栈上二维数组的某一行首地址
/*
void func(int arr[][5]){
int brr[2][2] = {{1112,2222},{1111,2222}};
int if_true = arr[0][3]; // x == 0;
int if_false = brr[0][0]; // x!= 0;
}
*/
case 2-> {
sym.setAddr("i32*");
if(x == 0){
int len = sym.getY();
String s = tab + "%x" + regId + " = load [" + len + " x i32] *, [" + len + " x i32]* * " + identSymbol.getRegId();
writeRes(s + "\n");
regId++;
s = tab + "%x" + regId + " = getelementptr [" + len+" x i32], [" + len + " x i32]* %x" + (regId - 1) + ", i32 " + exp.getValue();
writeRes(s + "\n");
regId++;
String reg = "%x" + regId;
String base = "%x" + (regId - 1);
writeRes(new GepIR(reg, len, base) + "\n");
node.setValue(reg);
node.setRegId(reg);
}else {
int m, n;
String output;
m = sym.getX();
n = sym.getY();
String res = "%x" + regId;
String op1 = exp.getValue();
String op2 = String.valueOf(sym.getY());
writeRes(new MultIR(res, op1, op2) + "\n");
regId++;
output = tab + "%x" + regId + " = getelementptr [" + m + " x [" + n + " x i32]], [" + m + " x [" + n + " x i32]]*" + identSymbol.getRegId() + ", i32 0, i32 0";
writeRes(output + "\n");
regId++;
output = tab + "%x" + regId + " = getelementptr [" + n + " x i32], [" + n + " x i32]* %x" + (regId - 1) + ", i32 0, i32 %x" + (regId - 2) ;
writeRes(output + "\n");
node.setValue("%x" + (regId));
node.setRegId("%x" + (regId));
}
regId++;
}
}

}
case 7->{
//二维数组元素使用
SyntaxWord exp1,exp2;
exp1 = children.get(2);
exp2 = children.get(5);
Exp(exp1);
Exp(exp2);
int m = sym.getX();
int n = sym.getY();
// 使用参数中定义的二维数组元素,定义时不规定二维数组行数
if(sym.getX() == 0){
sym.setAddr("i32");
String s = tab + "%x" + regId + " = load ["+ n +" x i32] *, [" + n + " x i32]* * " + identSymbol.getRegId();
writeRes(s + "\n");
regId++;
s = tab + "%x"+ regId + " = getelementptr [" + n + " x i32], [" + n + " x i32]* " + "%x" + (regId - 1) + ", i32 " + exp1.getValue();
writeRes(s + "\n");
regId++;
s = tab + "%x" + regId + " = getelementptr [" + n + " x i32], [" + n +" x i32]* %x" + (regId - 1) + ", i32 0, i32 " + exp2.getValue();
writeRes(s + "\n");
writeRes(new LoadIR("%x" + (1 + regId), "%x" + regId) + "\n");
node.setRegId("%x"+ regId);
regId++;
node.setValue("%x"+ regId);
regId++;
}
// 使用(父)块内定义的二维数组元素
else {
String loadAddress = "%x" + regId;
regId++;
String loadReg = "%x" + regId;
writeRes(tab+loadAddress
+ " = getelementptr ["
+ m +" x ["
+ n +" x i32]], ["
+ m +" x ["
+ n +" x i32]]*"
+ identSymbol.getRegId()
+ ", i32 0, i32 "
+ exp1.getValue()
+ ", i32 "
+ exp2.getValue()
+ "\n"
);
writeRes(new LoadIR(loadReg, loadAddress) + "\n");
node.setValue(loadReg);
node.setRegId(loadAddress);
sym.setAddr("i32");
regId++;
}
}
}
}
node.setSymbol(sym);//设置当前节点Symbol
}

编码后修改

基本同编码前设计,仅针对UnaryExp的遗漏情况进行修改,以及Parse模块添加语句减少代码生成工作量。

  • 实际测试中发现UnaryExp与UnaryOp也存在递归情况,设计对UnaryExp的抽象语法树自底向上进行运算,先递归访问到最右端的UnaryExp,计算其初值,之后自底向上,遇到"+“则直接向上传值,”-"则新增sub语句,用0减去当前值实现取负后在向上传值
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
private void UnaryExp(SyntaxWord node) {
ArrayList<SyntaxWord> children = node.getChildren();
// UnaryExp → PrimaryExp | Ident '(' [FuncRParams] ')'| UnaryOp UnaryExp
if(!children.get(0).isEnd() && children.get(0).getType().equals("PrimaryExp")){
SyntaxWord primaryExp = children.get(0);
handle(primaryExp);
node.setValue(primaryExp.getValue());
node.setRegId(primaryExp.getRegId());
node.setSymbol(primaryExp.getSymbol());
}
else if (!children.get(0).isEnd() && children.get(0).getType().equals("UnaryOp")) {
String unaryOp = children.get(0).getChild0Content();
SyntaxWord unaryExp = children.get(1);
handle(unaryExp);
switch (unaryOp){
case"+"-> node.setValue(unaryExp.getValue());
case "-"->{
if(floor == 0){
int preValue = Integer.parseInt(unaryExp.getValue());
preValue = -preValue;
node.setValue(String.valueOf(preValue));
}else{//添加Sub 0 语句
String left = "0";
String right = unaryExp.getValue();
writeRes(calIR(left, "-", right) + "\n");
String reg = "%x" + regId;
node.setRegId(reg);
node.setValue(reg);
++regId;
}
}
}
···
}
}
  • 为减少代码冗余,减轻代码生成阶段工作量,在SynTaxWord类中新添一维数组forElements及字符串’StmtType’,在Parse模块Stmt函数中将for语句的元素存在情况存于forElemets中,判断Stmt类型后将其类型储存在StmtType中以便LlvmGeneratror模块中直接使用switch分类处理。
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
>public class SyntaxWord {
···
int[] forElemets = null;
//size = 3的一维数组以1,0标记该for语句是否有ForStmt,Cond和forStmt
//如for(i = 1;;); 其forElements为{1,0,0}
···

>}
>private void Stmt(SyntaxWord node) {
ArrayList<SyntaxWord> children = node.getChildren();
String stmtType = node.getStmtType();
switch (stmtType){
case "Assignment"->{
···
}
case "Block" ->{
···
}
case "Exp" ->{
···
}
case "Return"->{
···
}
case "Input"->{
···
}
case "Output"->{
···
}
case "If"->{
···
}
case "Break"->{
···
}
case "Continue"->{
···
}
case "Loop"->{
···
}
}
if(node.getStmtId() == -1){
return;
}
writeRes(tab + "br label %block" + node.getStmtId() + "\n");

}