(8)stmts
stmts逻辑上代表一个语句块或一组语句块,对应的生成式和在节点中使用的type如下:
stmts --> NUL| type-->0
stmt stmts| type-->1
if(expr) { stmts} stmts| type-->2
if(expr) {stmts} else {stmts} stmts| type-->3
while(expr) { stmts} stmts| type-->4
接下来给出生成后端代码的代码:
public void genCode() {
if(type==1)
{
st.genCode();
stmts1.genCode();
}
else if(type==2)
{
condition.genCode();
Code code=new Code(0x18);
BackendClassManager.tFunc.codes.add(code);
stmts1.genCode();
code.Operands.add(String.valueOf(BackendClassManager.tFunc.codes.size()));
stmts3.genCode();
}
else if(type==3)
{
condition.genCode();
Code code=new Code(0x18);
code.Opcode=0x18;
BackendClassManager.tFunc.codes.add(code);
stmts1.genCode();
Code code1=new Code(0x01);
code1.Operands.add(String.valueOf(0));
BackendClassManager.tFunc.codes.add(code1);
Code code2=new Code(0x18);
BackendClassManager.tFunc.codes.add(code2);
code.Operands.add(String.valueOf(BackendClassManager.tFunc.codes.size()));
stmts2.genCode();
code2.Operands.add(String.valueOf(BackendClassManager.tFunc.codes.size()));
stmts3.genCode();
}
else if(type==4)
{
ArrayList<Code> albreak=new ArrayList<Code>();
ArrayList<Code> alcontinue=new ArrayList<Code>();
BackendClassManager.loopbreak.add(albreak);
BackendClassManager.loopcontinue.add(alcontinue);//循环入口,首先判断expr
int pos=BackendClassManager.tFunc.codes.size();
condition.genCode();
//跳转指令
Code code=new Code(0x18);
BackendClassManager.tFunc.codes.add(code);
//循环体
stmts1.genCode();
// code.Operands.add(String.valueOf(end));//表达式回填
Code code1=new Code(0x01);
code1.Operands.add(String.valueOf(0));
BackendClassManager.tFunc.codes.add(code1);//压入0
Code code2=new Code(0x18);
code2.Operands.add(String.valueOf(pos));
BackendClassManager.tFunc.codes.add(code2);//跳转到循环入口
int end=BackendClassManager.tFunc.codes.size();
code.Operands.add(String.valueOf(end));//表达式回填
for(Code c:albreak)
{
c.Operands.add(String.valueOf(end));
}
for(Code c:alcontinue)
{
c.Operands.add(String.valueOf(pos));
}
BackendClassManager.loopbreak.pop();
BackendClassManager.loopcontinue.pop();
stmts3.genCode();
}
}
对于type=0,没必要生成任何代码;对于type=1,生成stmt的代码然后递归再生成stmts的代码;对于type=2,首先生成条件expr的代码,在这段代码执行过后,会将结果放在堆栈顶,然后加入code(0x18)进行跳转,但此刻的跳转地址还不能确定,因此先要生成stmts1的代码,之后回填跳转地址,再生成stmts3的代码;对于type=3,和type=2类似,但要在stmts1代码之后加入无条件跳转指令,跳转到else块后面,无条件跳转的方法是首先压入0,再使用0x18也就是ifz进行跳转;对于type=4要稍微复杂些,首先要计算循环开始的地址(包括判断expr),然后生成循环体代码,得到循环结束地址,再回填循环判断的相关代码,除此之外,还要回填此循环体内出现的所有break和continue语句的地址。
(9)stmt
首先给出语句stmt的生成式:
stmt --> continue;| type=0
break;| type=1
var-declare;| type=2
class-init;| type=3
setvalue;| type=4
expr;| type=5
接下来给出代码:
public void genCode() {
ArrayList<Code> al=BackendClassManager.tFunc.codes;
if(type==0)
{
CodeGenHelper.JMP(al, 0);
BackendClassManager.loopcontinue.peek().add(al.get(al.size()-1));
}
if(type==1)
{
CodeGenHelper.JMP(al, 0);
BackendClassManager.loopbreak.peek().add(al.get(al.size()-1));
}
if(type==5)
{
ep.genCode();
}
if(type==2)
{
vc.genCode();
}
if(type==3)
{
ci.genCode();
}
if(type==4)
{
sv.genCode();
}
}
stmt代码比较简单,对于break和continue,只需要加入一个跳转语句,然后把此跳转语句加入到相应的列表里(因为循环是嵌套的,所以使用堆栈来表示这一关系),由外层循环体进行地址回填即可,使用了CodeGenHelper的JMP方法作为辅助函数;对于其它类型则调用其相应节点的genCode方法。
另外给出CodeGenHelper的JMP方法,封装了压入0和ifz两条指令:
public static void JMP(ArrayList<Code> al,int pos)
{
Code code1=new Code(0x01);
code1.Operands.add(String.valueOf(0));
BackendClassManager.tFunc.codes.add(code1);//压入0
Code code2=new Code(0x18);
code2.Operands.add(String.valueOf(pos));
BackendClassManager.tFunc.codes.add(code2);//跳转
}
(10)classinit
递推式:class-init --> ids = new
type ( args )| type[expr]
type: 0 1
代码:
public void genCode() {
ArrayList<Code> al=BackendClassManager.tFunc.codes;
ag.genCode();
if(type==0)
{
Code code=new Code(0x1C);
code.Operands.add(tp.toString());
al.add(code);
}
else
{
Code code=new Code(0x1D);
code.Operands.add(tp.toString());
al.add(code);
}
if(is.type==1)
{
int slot=CodeGenHelper.SearchByName(is.ID.toString());
Code code=new Code(0x05);
code.Operands.add(String.valueOf(slot));
al.add(code);
}
if(is.type==2)
{
if(is.getLastIDS().type!=3)
{
CodeGenHelper.PutObjectToStack(al, is);
CodeGenHelper.ChangeStackTopEle(al);
CodeGenHelper.CodePutField(al, is.getLastID().toString());
}
else
{
int value=CodeGenHelper.StoreToLocalTable(al);
CodeGenHelper.PutObjectToStack(al, is);
CodeGenHelper.CodeGetField(al, is.getLastID().toString());
int array=CodeGenHelper.StoreToLocalTable(al);
is.getLastIDS().EXPR.genCode();
CodeGenHelper.LoadToStack(al, array);
CodeGenHelper.LoadToStack(al, value);
Code code=new Code(0x26);
al.add(code);
}
}
if(is.type==3)
{
int slot=CodeGenHelper.StoreToLocalTable(al);
is.EXPR.genCode();
CodeGenHelper.LoadToStackByName(al, is.ID.toString());
CodeGenHelper.LoadToStack(al, slot);
Code code=new Code(0x26);
al.add(code);
}
}
首先ag.genCode()把所有参数压栈,然后根据类别判断是初始化对象还是数组来使用0x1C或者0x1D指令并把类型作为操作数,当此条指令执行完之后,堆栈顶此时放着返回的对象或数组的句柄,接下来的任务是将该句柄赋值给ids。
接着判断ids的类型,ids的推导式如下:
ids->
* id| type 1
id.ids| type 2
id[expr] type 3
this| type=4
如果type=1,则说明是一个局部变量,从nameSlot中找到该局部变量在局部变量表里的位置(相关操作封装到了CodeGenHelper的searchByName中),赋值给这个槽即可。
如果type=2,说明是某个对象的成员变量,则需要先把这个对象压入堆栈,再通过putfield指令给此对象赋值,若是某个对象的成员变量中的某个元素(即该成员变量是个数组),则需要使用给数组赋值的指令。
如果type=3,说明给数组某个元素赋值,需要先把下标计算出来,然后通过相应指令给数组赋值。
其中的很多操作都封装在了CodeGenHelper中。
(11)vardeclare
递推式:
var-declare --> type args|type[] args
代码:
public void genCode() {
ArrayList<Code> al=BackendClassManager.tFunc.codes;
ArrayList<ids> idlist=ags.getidsList();
for(ids id:idlist)
{
BackendClassManager.nameSlot.put(id.getLastID().toString(), BackendClassManager.nameSlot.size());
}
}
可以看到,在变量声明时并没有生成任何实际代码,只是在局部变量表中给其开辟了一个存储位置而已。
(12)setvalue
setvalue--> ids = expr
public void genCode() {
ArrayList<Code> al=BackendClassManager.tFunc.codes;
ep.genCode();
if(is.type==1)
{
int slot=CodeGenHelper.SearchByName(is.ID.toString());
Code code=new Code(0x05);
code.Operands.add(String.valueOf(slot));
al.add(code);
}
if(is.type==2)
{
if(is.getLastIDS().type!=3)
{
CodeGenHelper.PutObjectToStack(al, is);
CodeGenHelper.ChangeStackTopEle(al);
CodeGenHelper.CodePutField(al, is.getLastID().toString());
}
else
{
int value=CodeGenHelper.StoreToLocalTable(al);
CodeGenHelper.PutObjectToStack(al, is);
CodeGenHelper.CodeGetField(al, is.getLastID().toString());
int array=CodeGenHelper.StoreToLocalTable(al);
is.getLastIDS().EXPR.genCode();
CodeGenHelper.LoadToStack(al, array);
CodeGenHelper.LoadToStack(al, value);
Code code=new Code(0x26);
al.add(code);
}
}
if(is.type==3)
{
int slot=CodeGenHelper.StoreToLocalTable(al);
is.EXPR.genCode();
CodeGenHelper.LoadToStackByName(al, is.ID.toString());
CodeGenHelper.LoadToStack(al, slot);
Code code=new Code(0x26);
al.add(code);
}
}
setvalue和classinit的代码几乎一样,唯一的区别是classinit中需要调用new和newarray指令得到需要给ids赋的值,而setvalue则是通过计算expr来得到,即使用ep.genCode();
(13)ids
ids的genCode逻辑是指将此ids的值放到堆栈顶,此值或许是个int、double,也可能是个对象的句柄。
递推式:
ids->
* id| type 1
id.ids| type 2
id[expr] type 3
this| type=4
代码:
public void genCode() {
ArrayList<Code> al=BackendClassManager.tFunc.codes;
if(type==1)
{
Integer slot=BackendClassManager.nameSlot.get(ID.toString());
if(slot!=null)
{
Code code=new Code(0x03);
code.Operands.add(String.valueOf(slot));
al.add(code);
}
else
{
//error
}
}
if(type==2)
{
int slot=BackendClassManager.nameSlot.get(ID.toString());
Code code=new Code(0x03);
code.Operands.add(String.valueOf(slot));
al.add(code);
IDS.genCode2nd();
}
if(type==3)
{
EXPR.genCode();//压入expr值
int slot=BackendClassManager.nameSlot.get(ID.toString());
Code code=new Code(0x03);
code.Operands.add(String.valueOf(slot));
al.add(code);//压入array
code=new Code(0x04);
al.add(code);//读取数组元素
}
if(type==4)
{
int slot=BackendClassManager.nameSlot.get("this");
Code code=new Code(0x01);
code.Operands.add(String.valueOf(slot));
}
}
public void genCode2nd() {
ArrayList<Code> al=BackendClassManager.tFunc.codes;
if(type==1)
{
Code code=new Code(0x20);
code.Operands.add(ID.toString());
al.add(code);
}
if(type==2)
{
Code code=new Code(0x20);
code.Operands.add(ID.toString());
al.add(code);
IDS.genCode2nd();
}
if(type==3)
{
EXPR.genCode();//压入expr值
Code code=new Code(0x20);
code.Operands.add(ID.toString());
al.add(code);//压入数组
code=new Code(0x04);
al.add(code);//读取数组元素
}
if(type==4)
{
//error
}
}
由于ids的结构不对称,所以不能使用递归的方式来获得其值。举例:id.id,第一个id的处理方式是通过局部变量表中拿到其句柄,第二个id的处理方式要通过getfield来拿到其值。因此这两种处理方式对应的是genCode和genCode2nd。
只有当第一次调用的时候(也就是其它节点调用)调用genCode,ids节点调用均调用genCode2nd。
(未完待续)