在做lab6和lab7之前,先确保你已经有了一个结构完整的AST树。
如果你还没有生成AST,参考下面这篇文章
(5条消息) BITMINICC——利用Antlr的Listener生成AST_寒士°、的博客-CSDN博客
下面开始lab6和lab7具体的实验
这两个实验放在一起做,当做完lab7时,lab6就非常简单了,所以先说lab7
1.BITMINICC都给了什么?
ExampleCPrinter:打印你所生成的Quat
TemporaryValue:继承自ASTNode,用于生成临时的变量(例子中给的%1,%2之类的)
Quat:定义的四元式类,(op,res,opnd1,opnd2),op为String,其余为ASTNode(这也是TemporaryValue定义为ASTNode的原因。
所以,中间代码生成就是将你的AST树翻译为一个个的四元式,其中,中间变量由TemporaryValue来给出。
2.理解递归
递归大家都很熟悉,理解了ExampleICBuilder中的递归生成四元式,lab7就完成了大半了。
@Override
public void visit(ASTBinaryExpression binaryExpression) throws Exception {
String op = binaryExpression.op.value;
ASTNode res = null;
ASTNode opnd1 = null;
ASTNode opnd2 = null;
if (op.equals("=")) {
// 赋值操作
// 获取被赋值的对象res
visit(binaryExpression.expr1);
res = map.get(binaryExpression.expr1);
// 判断源操作数类型, 为了避免出现a = b + c; 生成两个四元式:tmp1 = b + c; a = tmp1;的情况。也可以用别的方法解决
if (binaryExpression.expr2 instanceof ASTIdentifier) {
opnd1 = binaryExpression.expr2;
}else if(binaryExpression.expr2 instanceof ASTIntegerConstant) {
opnd1 = binaryExpression.expr2;
}else if(binaryExpression.expr2 instanceof ASTBinaryExpression) {
ASTBinaryExpression value = (ASTBinaryExpression)binaryExpression.expr2;
op = value.op.value;
visit(value.expr1);
opnd1 = map.get(value.expr1);
visit(value.expr2);
opnd2 = map.get(value.expr2);
}else {
// else ...
}
}else if (op.equals("+")) {
// 加法操作,结果存储到中间变量
res = new TemporaryValue(++tmpId);
visit(binaryExpression.expr1);
opnd1 = map.get(binaryExpression.expr1);
visit(binaryExpression.expr2);
opnd2 = map.get(binaryExpression.expr2);
}else {
// else..
}
// build quat
Quat quat = new Quat(op, res, opnd1, opnd2);
quats.add(quat);
map.put(binaryExpression, res);
}
对于一个二元表达式,如a=b+(c+5)而言,在你的AST树上是这样的
"exprs" : [ {
"type" : "BinaryExpression",
"op" : {
"type" : "Token",
"value" : "=",
"tokenId" : 6
},
"expr1" : {
"type" : "Identifier",
"value" : "a",
"tokenId" : 5
},
"expr2" : {
"type" : "BinaryExpression",
"op" : {
"type" : "Token",
"value" : "+",
"tokenId" : 8
},
"expr1" : {
"type" : "Identifier",
"value" : "b",
"tokenId" : 7
},
"expr2" : {
"type" : "BinaryExpression",
"op" : {
"type" : "Token",
"value" : "+",
"tokenId" : 11
},
"expr1" : {
"type" : "Identifier",
"value" : "c",
"tokenId" : 10
},
"expr2" : {
"type" : "IntegerConstant",
"value" : 5,
"tokenId" : 12
}
}
}
} ]
即a=%1,%1=b+%2,%2=c+5
这其实就是我们要生成的四元式,不过为了避免出现多个四元式,所以特殊处理了一下而已。
再比如,res=~a
它再AST树上就是,一个ASTBinaryExpression,expr1=identifier(res),expr2=UnaryExpression。
所以你只需要添加
else if(astBinaryExpression.expr2 instanceof ASTUnaryExpression){
ASTUnaryExpression value = (ASTUnaryExpression) astBinaryExpression.expr2;
op = value.op.value;
visit(value);
opnd1 = map.get(value.expr);
}
以及对应的访问函数即可。
@Override
public void visit(ASTUnaryExpression unaryExpression) throws Exception {
// TODO Auto-generated method stub
}
3.语句翻译
因为Example中没有语句翻译的例子,所以我另外补充一个语句翻译。
语句翻译如表达式翻译一样,也是遍历你的AST树即可,唯一需要增加的是一个Label来标记语句块。
如翻译这样一个SelectStatement
int main()
{
if(a>0){
c=a+b;
}
else{
c=a-b;
}
}
它生成的AST树是
"type" : "SelectionStatement",
"cond" : [ {
"type" : "BinaryExpression",
"op" : {
"type" : "Token",
"value" : ">",
"tokenId" : 8
},
"expr1" : {
"type" : "Identifier",
"value" : "a",
"tokenId" : 7
},
"expr2" : {
"type" : "IntegerConstant",
"value" : 0,
"tokenId" : 9
}
} ],
"then" : {
"type" : "CompoundStatement",
"blockItems" : [ {
"type" : "ExpressionStatement",
"exprs" : [ {
"type" : "BinaryExpression",
"op" : {
"type" : "Token",
"value" : "=",
"tokenId" : 13
},
"expr1" : {
"type" : "Identifier",
"value" : "c",
"tokenId" : 12
},
"expr2" : {
"type" : "BinaryExpression",
"op" : {
"type" : "Token",
"value" : "+",
"tokenId" : 15
},
"expr1" : {
"type" : "Identifier",
"value" : "a",
"tokenId" : 14
},
"expr2" : {
"type" : "Identifier",
"value" : "b",
"tokenId" : 16
}
}
} ]
} ]
},
"otherwise" : {
"type" : "CompoundStatement",
"blockItems" : [ {
"type" : "ExpressionStatement",
"exprs" : [ {
"type" : "BinaryExpression",
"op" : {
"type" : "Token",
"value" : "=",
"tokenId" : 22
},
"expr1" : {
"type" : "Identifier",
"value" : "c",
"tokenId" : 21
},
"expr2" : {
"type" : "BinaryExpression",
"op" : {
"type" : "Token",
"value" : "-",
"tokenId" : 24
},
"expr1" : {
"type" : "Identifier",
"value" : "a",
"tokenId" : 23
},
"expr2" : {
"type" : "Identifier",
"value" : "b",
"tokenId" : 25
}
}
} ]
} ]
}
缩小来观察
SelectionStatement就是由一个ASTExpression(cond)和两个(ASTStatement)构成。
ASTExpression怎么翻译?递归交给你的visit(ASTExpression)函数即可。
ASTStatement同理
那么生成Lable来标记语句块,我的理解其实就是关键字
比如:上面这个例子,我定义(大家可以改改名字)的中间代码为
(Label,,,@1If)
(>,a,0,%1)
(JF,%1,,@1Otherwise)
(+,a,b,c)
(JMP,,,@1Endif)
(Label,,,@1Otherwise)
(-,a,b,c)
(Label,,,@1Endif)
细细一看,是否有点像汇编代码了?其中(JF为如果%1为False,就跳转,JMP为直接跳转)
所以,SelectionStatement的代码结构为
public static void visit(ASTSelectionStatement astSelectionStatement){
ASTNode StartCheckIfLabel=new LabelGenerator
quats.add(new Quat("Label",StartCheckIfLabel,null,null));
for(ASTExpression astExpression:astSelectionStatement.cond){
visit(astExpression);
}
ASTNode res = map.get(astSelectionStatement.cond.get(0));
ASTNode OtherwiseLabel=new LabelGenerator
quats.add(new Quat("JF",OtherwiseLabel,res,null));
visit(astSelectionStatement.then);
ASTNode EndifLabel=new LabelGenerator
if(astSelectionStatement.otherwise!=null){
quats.add(new Quat("JMP",EndifLabel,null,null));
quats.add(new Quat("Label",OtherwiseLabel,null,null));
visit(astSelectionStatement.otherwise);
}
quats.add(new Quat("Label",EndifLabel,null,null));
}
其中LabelGenerator是我自己定义的继承自ASTNode的生成Label的类
以上就是lab7的大致思路
但是缺少了对ASTDeclaration,ASTFunctionDefine的处理。这是因为我们对于定义的变量/函数,一般不放到中间代码中,而是维护一个FunctionTabel,一个Scope以及一个GlobalVaraibleTable
其实也就是对ASTDeclaration和ASTFunctionDefine的处理。
下面是int a的AST树。
"type" : "Declaration",
"specifiers" : [ {
"type" : "Token",
"value" : "int",
"tokenId" : 5
} ],
"initLists" : [ {
"type" : "InitList",
"declarator" : {
"type" : "VariableDeclarator",
"identifier" : {
"type" : "Identifier",
"value" : "a",
"tokenId" : 6
}
},
"exprs" : null
} ]
所以我们只需要访问Declaration时,访问需要的节点,并加入你的表里面即可。
如:
public static void visit(ASTDeclaration astDeclaration){
if(astDeclaration.parent.getClass()==ASTCompilationUnit.class){
//全局变量或函数声明
}
else if(astDeclaration.parent.getClass()==ASTCompoundStatement.class){
ASTToken astToken = astDeclaration.specifiers.get(0);
for(ASTInitList astInitList : astDeclaration.initLists){
}
}
else{
}
}
如果维护了这样一个表,那么lab6中的var_not_defined和var_defined_again就很容易处理了,只需要在访问/插入的时候遍历一下表即可。
以上就是我lab6和lab7的思路,这两个实验本质上是对AST树的操作,所以关键的关键还是如何生成AST树。