COINS で Brainfuck のコンパイラ (3)
さて、前回までで中身からっぽの実行ファイルを出力するところまでは書いた。
いちおう実行ファイルを出力するところまで書きました。クラスファイルとかソースとかもぶちこんだアーカイブ。
http://konbu.s13.xrea.com/lib/coins/bffront-20070623.tar.bz2
% tar jxf bffront-20070623.tar.bz2 % cd bffront % ./bfc.sh example/hello.bf % ./a.out Hello World! % ./bfc.sh example/echo.bf % ./a.out sdfijasi sdfijasi %
UNIX な環境で、COINS のクラスファイルがクラスパスに入ってるならこんな感じで実行できるはず。
SimpleMain.java (COINS のアーカイブの example ディレクトリにも入ってます)、coins.ir.hir.HIR, coins.ir.hir.HIR0, coins.sym.Sym,coins.sym.Sym0 あたりの情報をもとに、 Brainfuck → HIR するコードを書きました。
まああとは Brainfuck から C言語に変換して、そっから COINS のC言語コンパイラを使って目的とする HIR を出力させたり。
そんなわけで以下コード。変更加えた BFtoHIR.java だけ。全コードは bffront-20070623.tar.bz2 に。
アーカイブの URL のっけてんのになんでコード貼るかっつーと、俺のためなんだよなー。自分コードは自分日記読みゃわかるっていうのが、少し便利。
BFtoHIR.java
package bffront; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.FileInputStream; import coins.HirRoot; import coins.IoRoot; import coins.SymRoot; import coins.ir.IrList; import coins.ir.hir.HIR; import coins.ir.hir.Program; import coins.ir.hir.BlockStmt; import coins.ir.hir.SubpDefinition; import coins.sym.Subp; import coins.sym.Sym; import coins.sym.SymTable; import java.util.Stack; import coins.ir.hir.WhileStmt; import coins.sym.Type; import coins.sym.Var; import coins.sym.VectorType; import coins.sym.Const; import coins.sym.NamedConst; import coins.sym.Param; import coins.sym.Label; import coins.ir.hir.VarNode; import coins.ir.hir.ConstNode; import coins.ir.hir.AssignStmt; import coins.ir.hir.SetDataStmt; import coins.ir.hir.Stmt; import coins.ir.hir.LabeledStmt; import coins.ir.hir.ExpStmt; import coins.ir.hir.Exp; import coins.ir.hir.SubpNode; import coins.ir.hir.FunctionExp; public class BFtoHIR { private SymRoot symRoot; // Root of symbol information. private Sym sym; // Instanceo to make Sym objects. private HirRoot hirRoot; // Root of HIR information. private HIR hir; // Instance to make HIR objects. private IoRoot ioRoot; // Root of input/output-related information. private SubpDefinition lMainDef; // コンパイル結果のmainプログラムのノード private Subp lMain = null; private Subp putcharSubp = null; private Subp getcharSubp = null; private class BFExp{ public static final int INC = '+'; public static final int DEC = '-'; public static final int NEXT = '>'; public static final int PREV = '<'; public static final int GET = ','; public static final int PUT = '.'; public static final int LOOPSTART = '['; public static final int LOOPEND = ']'; } // Constructor of BFtoHIR public BFtoHIR(SymRoot sRoot, HirRoot hRoot, IoRoot iRoot) { symRoot = sRoot; sym = (Sym)symRoot.sym; hirRoot = hRoot; hir = (HIR)hirRoot.hir; ioRoot = iRoot; hirRoot.programRoot = hir.program(null, symRoot.symTableRoot, null, null); // プログラムのルートのノードとしてprogramノードを作る lMain = sym.defineSubp("main".intern(), symRoot.typeInt); lMain.setVisibility(Sym.SYM_PUBLIC); // "main"プログラムのシンボルを作る SymTable lMainSymTable = symRoot.symTableRoot.pushSymTable(lMain); // int putchar(int) symbol putcharSubp = sym.defineSubp("putchar".intern(), symRoot.typeInt); SymTable lPutcharSymTable = symRoot.symTableRoot.pushSymTable(putcharSubp); putcharSubp.addParamType(symRoot.typeInt); putcharSubp.setVisibility(Sym.SYM_EXTERN); putcharSubp.closeSubpPrototype(); lPutcharSymTable.popSymTable(); // int getchar() symbol getcharSubp = sym.defineSubp("getchar".intern(), symRoot.typeInt); SymTable lGetcharSymTable = symRoot.symTableRoot.pushSymTable(getcharSubp); getcharSubp.setVisibility(Sym.SYM_EXTERN); getcharSubp.closeSubpPrototype(); lGetcharSymTable.popSymTable(); lMain.closeSubpHeader(); // "main"プログラムの記号表として新しいSymTableを作る lMainDef = hir.subpDefinition(lMain, lMainSymTable); // "main"プログラムの定義のノードを作る ((Program)hirRoot.programRoot).addSubpDefinition(lMainDef); // used as Brainfuck memory offset Var bfOffset = sym.defineVar("memoffset".intern(), symRoot.typeInt); bfOffset.setVisibility(Sym.SYM_PUBLIC); bfOffset.setInitialValue(hir.intConstNode(0)); // used as Brainfuck memory VectorType bfMemType = sym.vectorType(symRoot.typeChar, 30000); Var bfMem = sym.defineVar("mem".intern(), bfMemType); bfMem.setInitialValue(null); bfMem.setVisibility(Sym.SYM_PUBLIC); } private Exp makeBfMemValueExp(){ Sym bfMemSym = symRoot.symTableCurrent.search("mem"); Sym bfOffsetSym = symRoot.symTableCurrent.search("memoffset"); VarNode bfMemNode = hir.varNode((Var)bfMemSym); VarNode bfOffsetNode = hir.varNode((Var)bfOffsetSym); return hir.subscriptedExp(bfMemNode, bfOffsetNode); } private Stmt makeMoveStmt(int move){ Stmt moveStmt = null; Sym bfOffsetSym = symRoot.symTableCurrent.search("memoffset"); VarNode bfOffsetNode = hir.varNode((Var)bfOffsetSym);; Exp addExp = move == 1 ? hir.exp(HIR.OP_ADD, bfOffsetNode, hir.intConstNode(1)) : hir.exp(HIR.OP_SUB, bfOffsetNode, hir.intConstNode(1)); bfOffsetSym = symRoot.symTableCurrent.search("memoffset"); bfOffsetNode = hir.varNode((Var)bfOffsetSym);; moveStmt = hir.assignStmt(bfOffsetNode, addExp); return moveStmt; } private Stmt makeAddSubStmt(int addsubv){ Exp exp1 = makeBfMemValueExp(); exp1 = hir.convExp(symRoot.typeInt, exp1); Exp resultExp = addsubv == 1 ? hir.exp(HIR.OP_ADD, exp1, hir.intConstNode(1)) : hir.exp(HIR.OP_SUB, exp1, hir.intConstNode(1)); resultExp = hir.convExp(symRoot.typeChar, resultExp); Exp exp2 = makeBfMemValueExp(); Stmt resultStmt = hir.assignStmt(exp2, resultExp); return resultStmt; } public BlockStmt makeBlockStmt(FileInputStream fis) throws IOException{ BlockStmt blockStmt = hir.blockStmt(null); int expression; while((expression = fis.read()) != -1){ Exp bfMemValueExp = makeBfMemValueExp(); // exp に対応した HIR コードに変換する。 switch(expression){ case BFExp.INC: blockStmt.addLastStmt(makeAddSubStmt(1)); break; case BFExp.DEC: blockStmt.addLastStmt(makeAddSubStmt(-1)); break; case BFExp.NEXT: Stmt nextStmt = makeMoveStmt(1); blockStmt.addLastStmt(nextStmt); break; case BFExp.PREV: Stmt prevStmt = makeMoveStmt(-1); blockStmt.addLastStmt(prevStmt); break; case BFExp.GET: SubpNode bfGetSubpNode = hir.subpNode(getcharSubp); Exp bfGetCall = hir.functionExp(bfGetSubpNode, hir.irList()); Stmt bfGetStmt = hir.assignStmt(bfMemValueExp, bfGetCall); blockStmt.addLastStmt(bfGetStmt); break; case BFExp.PUT: SubpNode bfPutSubpNode = hir.subpNode(putcharSubp); IrList bfPutParam = hir.irList(); bfPutParam.add(bfMemValueExp); Stmt bfPutStmt = hir.callStmt(bfPutSubpNode, bfPutParam); blockStmt.addLastStmt(bfPutStmt); break; case BFExp.LOOPSTART: BlockStmt loopInnerBlock = makeBlockStmt(fis); WhileStmt loopStmt = hir.whileStmt(bfMemValueExp, loopInnerBlock); blockStmt.addLastStmt(loopStmt); break; case BFExp.LOOPEND: return blockStmt; } } return blockStmt; } public void makeHirFromBF(File sourceFile, HirRoot hirRoot) throws IOException{ FileInputStream fis = new FileInputStream(sourceFile); BlockStmt lMainBlock = makeBlockStmt(fis); lMainDef.setHirBody(lMainBlock); } }
途中かなりいきづまったのは、Exp の型をちゃんと合わせたりしてなかったりしたとこかな。C言語と同じ気分で暗黙に型キャストされるだろう的なコード書いてるとコンパイルエラー。ちゃんと hir.convExp とかを使うようにしたら動いた。