COINS で Brainfuck のコンパイラ (2)
とりあえず、コンパイラプログラムのドライバ部分を作ります。コマンドライン引数を読んでファイルを開いたり、オプションを設定したりほげほげほげ……というのは面倒なので、COINS で用意されている coins.driver.Driver クラスを使います。こいつを継承することで、めんどくさいところはだいたいうまいことやってくれます。
Brainfuck のフロントエンドは bffront パッケージとしてまとめて、BFDriver, BFtoHIR という二つのクラスから作ることにします。 BFDriver は coins.driver.Driver を継承した Brainfuck のドライバです。あと一つ、拡張子を設定するファイルも作る必要があります。拡張子指定ファイルは bfsuffix としました。
http://konbu.s13.xrea.com/lib/coins/bffront-070618.jar
% jar xf bffront-070618.jar % cd bffront % ls BFDriver.java BFtoHIR.java bfsuffix build.xml classes/ hello.bf % java -cp $CLASSPATH:classes bffront.BFDriver -coins:suffix=bfsuffix -coins:assembler=as -b x86 hello.bf % ./a.out % java -cp $CLASSPATH:classes bffront.BFDriver -coins:suffix=bfsuffix -coins:assembler=as -b x86 hello.bf -S % cat hello.s .ident "Coins Compiler version: coins-1.4.2.2 + BackEnd-0.8.1" /* JavaCG for target:x86 convention:standard */ .section .text .align 4 .global main main: pushl %ebp movl %esp,%ebp .L2: leave ret %
見てわかりますが、まだ BFtoHIR が空っぽなので、中身が空っぽのプログラムを吐くだけです。コンパイラの体裁だけ整えたところ。
以下コード。
BFDriver.java
package bffront; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import coins.HirRoot; import coins.IoRoot; import coins.PassException; import coins.driver.Driver; import coins.driver.Suffix; import coins.ir.hir.Program; /** * A Brainfuck driver implementation using the COINS Compiler Driver API. **/ public class BFDriver extends Driver{ /** * A main function to invoke driver instance. * * @param args a command line. **/ public static void main(String[] args){ new BFDriver().go(args); } /** * HIR tree creation from source code. * * @param hirRoot an HirRoot object * @param suffix suffix rule of the source file. * @param in an input stream from which the C source program can be read * @param io an IoRoot object * @throws IOException any IO error * @throws PassException unrecoverable error(s) found in processing **/ protected void makeHirFromSource(File sourceFile, HirRoot hirRoot, Suffix suffix, InputStream in, IoRoot io) throws IOException, PassException { if (suffix.getLanguageName().equals("Brainfuck")) { try{ BFtoHIR front = new BFtoHIR(io.symRoot, hirRoot, io); try { front.makeHirFromBF(sourceFile, hirRoot); } catch (Exception e) { io.msgError.put("in BF parser: "+e); e.printStackTrace(); throw new PassException("BFDriver", "Error(s) in parsing "+sourceFile.getName()); } } catch(Exception e){ io.msgError.put("in BFParser: "+e); e.printStackTrace(); } ((Program)hirRoot.programRoot).finishHir(); } else { io.msgError.put(sourceFile + ": Unknown programming language: " + suffix.getLanguageName()); throw new PassException(myName, "Unknown language " + suffix.getLanguageName()); } } }
coins.driver.Driver の main と makeHirFromSource をオーバーライドしています。あとは BFtoHIR.makeHirFromBF を呼んでるだけです。他はエラー処理とか拡張子チェックとか細々したこと。
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.hir.HIR0; import coins.ir.hir.Program; import coins.ir.hir.BlockStmt; import coins.ir.hir.SubpDefinition; import coins.sym.Subp; import coins.sym.Sym0; import coins.sym.SymTable; public class BFtoHIR { private SymRoot symRoot; // Root of symbol information. private Sym0 sym; // Instanceo to make Sym objects. private HirRoot hirRoot; // Root of HIR information. private HIR0 hir; // Instance to make HIR0 objects. private IoRoot ioRoot; // Root of input/output-related information. private SubpDefinition lMainDef; // コンパイル結果のmainプログラムのノード private BlockStmt lMainBlock = null; // Constructor of BFtoHIR public BFtoHIR(SymRoot sRoot, HirRoot hRoot, IoRoot iRoot) { symRoot = sRoot; sym = (Sym0)symRoot.sym; hirRoot = hRoot; hir = (HIR0)hirRoot.hir; ioRoot = iRoot; hirRoot.programRoot = hir.program(null, symRoot.symTableRoot, null, null); // プログラムのルートのノードとしてprogramノードを作る Subp lMain = sym.defineSubp("main".intern(), symRoot.typeVoid); lMain.setVisibility(Sym0.SYM_PUBLIC); lMain.closeSubpHeader(); // "main"プログラムのシンボルを作る SymTable lSymTable = symRoot.symTableRoot.pushSymTable(lMain); // "main"プログラムの記号表として新しいSymTableを作る lMainDef = hir.subpDefinition(lMain, lSymTable); // "main"プログラムの定義のノードを作る ((Program)hirRoot.programRoot).addSubpDefinition(lMainDef); lMainBlock = hir.blockStmt(null); lMainDef.setHirBody(lMainBlock); } public void makeHirFromBF(File sourceFile, HirRoot hirRoot) throws IOException{ FileInputStream fis = new FileInputStream(sourceFile); int exp; while((exp = fis.read()) != -1){ // exp に対応した HIR コードに変換する。 } } }
コンストラクタでは、HIR の最低限のヘッダとかだけ用意してます。BF から HIR への変換をするはずのメソッド makeHirFromBF はまだ空っぽですから、どんな BF をコンパイルしたところで出力するファイルは同じです。
http://www.coins-project.org/international/COINSdoc.en/infra/SimpleMain.java なんかは HIR の生成方法がよくわかると思います。
bfsuffix
#SRD, 2, Suffix rule DB file, format version 2 S, Assembler, assembly source (need preprocess), s,-,o s, Assembler, assembly source, -,-,o bf, Brainfuck, Brainfuck source, -,s,o
コンパイルは ant で以下のように。設定ファイルは Makefile じゃなくて build.xml です。
% ant Buildfile: build.xml compile: [javac] Compiling 2 source files to /home/sun/docs/bffront/classes buildall: BUILD SUCCESSFUL Total time: 4 seconds