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

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

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

コード全体はこちら

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

1
2
3
4
5
6
7
typealias Brace = () -> Unit

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

object Brace2 {
operator fun invoke(_b: Brace) = BracketsOok(WholeState(Memory({ Array<Byte>(30000, { 0 }) }), InstructionStack(emptyArray())))
}

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

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

1
2
3
4
5
6
7
operator fun invoke(): BracketsOok {}

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

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

operator fun invoke(_bf: BracketsOok) {}

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

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

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