KotlinのREPLで括弧だけのコードを動かす

KotlinにはREPL機能があります。対話型実行環境ですね。さらに、拡張関数を定義できるなど、かなり柔軟なコードが書ける言語でもあります。

今回は演算子の拡張関数を利用することで、(){}だけで任意の処理を動かすようにしてみます。Ook言語を利用しています。

コード全体はこちら

コードを読み解くために、まずは拡張関数の定義を見ていきます。

[kotlin]
typealias Brace = () -> Unit

operator fun Brace.invoke(_b: Brace) = Brace2

object Brace2 {
operator fun invoke(_b: Brace) = BracketsOok(WholeState(Memory({ Array(30000, { 0 }) }), InstructionStack(emptyArray())))
}
[/kotlin]

文に出てくる単体の{}() -> Unitという型になります。これをBraceと呼ぶこととします。
さらにBraceを、Braceを一つ引数に取る関数のように実行(invoke)させられるようにします。ここで、Kotlinでは関数に1つの関数を引数として渡すときは()を省略し、{}を使って書けるということを思い出してください。
これにより、{}{}Brace2オブジェクトを表すことができます。
オーバーロードしている関数に渡す場合、hoge()hoge({})hoge({}{})という3つのパターンができることになります。

続いて、プログラムの開始と終了を表す記述を定義します。上のコードに出てきていますが、(({}{}){})BracketsOokのインスタンスを表すようにします。ここの()を省略すると曖昧になって上手くいきません。

[kotlin]
operator fun invoke(): BracketsOok {}

operator fun invoke(_b: Brace): BracketsOok {}

operator fun invoke(_b2: Brace2): BracketsOok {}

operator fun invoke(_bf: BracketsOok) {}
[/kotlin]

BracketsOokもまた関数のように実行(invoke)できるようにし、3つの先ほどのパターンを利用して内部状態を変化させることでコードとして蓄積させます。最後に、(({}{}){})を渡すと記述終了とし、蓄積したコードを実行します。

括弧の列をそのまま単体でmain関数に書いてもいいですが、タイトルの通りREPL上にすると括弧しかないようなプログラムになります。以下がIntelliJ IDEA 2017.1で実行した例です。(実行には時間がかかります)

[caption id=”attachment_4362” align=”aligncenter” width=”958”] REPL結果[/caption]

HelloWorldのほうはコンパイルは出来ないんですが、REPLでは動く…