I've released Brainjack at GutHub. It's a Java7 implementation of the Brainf*ck programming language.
Brainf*ckのJava7実装をGitHubで公開しました。以前の記事で触れた、バイトコードを吐くコンパイラが含まれています。
Sadly, there are no Java7 features except multi-catch. I want to use invokedynamic
with ObjectWeb ASM 4.0, but there is no chance because Brainf*ck is too simple. I used only invokespecial
, invokevirtual
and other 15 classic opcodes.
残念ながらマルチキャッチくらいしかJava7っぽいコードになっていません……。できればinvokedynamic
を使いたいと思っていたのですが、Brainf*ckがシンプルすぎて使いどころがありませんでした。invokespecial
やinvokevirtual
を始めとした古典的なオペコードばかりです。
how about Brainjack works
Brainjack has 2 modes to run, INTERPRET
mode and COMPILE
mode.
今のところ、実行モードとしてインタプリタとコンパイラを用意しています。
What INTERPRET
mode does is just executing commands. You can give commands as -c
option.
インタプリタは単純に渡されたコマンドを実行します。-c
オプションでコマンドを渡すだけです。
$ java -jar Brainjack.jar interpret -c "+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-.------------.<++++++++.--------.+++.------.--------.>+." Hello, world! $ echo 'Hello, world!' | java -jar Brainjack.jar interpret -c ">,[>,]<[.<]" !dlrow ,olleH
COMPILE
mode creates .class file. This .class file has main method to execute given commands, so you can execute your commands by java
command.
コンパイラは渡されたコマンドを元にクラスファイルを生成します。作られたクラスファイルはmainメソッドを持っていますので、java
コマンドでそのまま実行できます。
$ java -jar Brainjack.jar compile -c "+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-.------------.<++++++++.--------.+++.------.--------.>+." -d . -n HelloWorld $ ls *.class HelloWorld.class $ java HelloWorld Hello, world! $ java -jar Brainjack.jar compile -c ">,[>,]<[.<]" -d . -n Reverse $ echo 'Hello, world!' | java Reverse !dlrow ,olleH
Codes to create class file is simple, methods for each commands are less than 20 lines. I don't use private methods to extract... Brainf*ck specification may be enough simple for JVM opcodes.
今回クラスファイルの生成はシンプルになっており、各コマンドの生成は長くても20行に収まっています。privateメソッドに切り出してごまかしたわけではありません。Brainf*ckの低レベルさがJVMに合ったのかもしれません。
I use args4j to parse command line options. It's very useful because it can handle command line options as String, Number, File and enum etc.
コマンドオプションのパースにはargs4jを使用しています。オプションを文字列や数値だけでなくFileやenumとしても扱えるのがとても便利です。
introspection
What I want to fix is logics shared between interpreter and compiler. It's very dirty because I switched it to Visitor pattern from capsuled logics in entity. Refactoring it will make Main class more simple I think.
最大の反省点はインタプリタとコンパイラのロジック共有でしょうか。途中からVisitorパターンに切り替えたこともあり、うまいことリファクタリングできていません。これができればMainクラスをもっと単純化できそうです。
And there is another strange code -- an abstract class knows its sub classes very well. I wrote it as enum at first, and I changed it to class. I don't have switched my idea yet.
またCommand抽象クラスがそのサブクラスを熟知しているという構造も、あまり普通ではなく気持ち悪さを感じます。もともとenumだったものをVisitorパターンを適用するためにクラス化したという経緯に引きずられすぎているかもしれません。
about next iteration
I have a schedule to develop next version. I'll try to use CloudBees next week, so Brainjack will have a Maven repository and Jenkins job on cloud. I'll try to use plugins especially for code coverages.
まだやりたいことがいくつか残っているので、次のバージョンを開発しようと思っています。来週CloudBeesのアカウント有効化を完成させ、MavenリポジトリとJenkinsジョブを作成するつもりです。Jenkinsではいくつかプラグインが使えるようなので、カバレッジを中心にいくつか試してみます。
And I want to use maven-release-plugin, I never used, to release jars to Maven repository and create tags on git. I've checked how to use it, but I cannot test it because there is no Maven repository in my private development environment.
また、実は今まで使って来なかったmaven-release-pluginを使ったMavenリポジトリへのデプロイとgitへのタグ作成も試してみます。各ゴールの使い方までは分かったつもりなのですが、Mavenリポジトリを用意できていないので動かせていません。
$ mvn release:prepare $ mvn release:perform -DpushChanges=false