Compile Time Grass to D CompilerでGrassを実行可能バイナリにコンパイル

みどりの日恒例 Grass Hackathon (2年ぶり2回目、そして記事は2日遅れ)
D言語版のGrassインタプリタと、コンパイル時にGrassコードをD言語に変換し、結果をmixinするという流れでGrassを実行可能バイナリにコンパイルするモノを作ってみました。

https://github.com/youz/grasses/tree/master/D

grass.dが普通のインタプリタ、grassctc.d がコンパイル時Grass to Dコンパイラです。

grassctc.d コンパイル方法

grassのコードを"source.grass"というファイル名でgrassctc.dと同じ場所に保存して以下のコマンドでコンパイルします。

$ dmd -J. -of実行ファイル名 grassctc.d

あるいはwindowsならgrassc.bat, linux&Mac(or msys)ならgrassc(シェルスクリプト)にgrassファイルを渡せば良きに計らってくれます。
また、-pオプションを付けて呼び出すとstderrに変換結果を表示しつつコンパイルします。

$ cat w.grass
wWWwwww
$ path/to/grassc -p w.grass
(new F(delegate V(V f1) {
  return f1(f1);
}))(new F(delegate V(V f0) {
  return writer(w);
}));

$ ./w.exe
w

変換サンプル

256回'w'を出力するやつ

wwWWwWWWwvWwWwWwwwwWwwwwwww

(new F(delegate V(V f3) {
  return (new F(delegate V(V f4) {
    return (new F(delegate V(V f5) {
      return (new F(delegate V(V f6) {
        return (new F(delegate V(V f7) {
          return f7(f7);
        }))(f6(w));
      }))(f5(writer));
    }))(f4(f4));
  }))(f3(f3));
}))(new F(delegate V(V f0) {
  return new F(delegate V(V f1) {
    return (new F(delegate V(V f2) {
      return f0(f2);
    }))(f0(f1));
  });
}));

hello world

wwWWWWwWwwwWwwwWwwwvwwWWwWWWwvWwWwvwWWWWWwWWWwWwwwvwvWwwwwwWwwwWWwWWWwWWWWWWWWwWWWWWWwwwwwwwwwwwwwwWWWWWWWWwWWWWWwWWWWWwwwWWWWWWwwwWWWWWWWWWwWWWWWWWWWWwwwWWWWWWWWWwWWWWWWWWWWwwwWWWWWWWWWWwwwwWWWWWWWWWWWWWwWWWWWWWWWWWWwwWWWWWWWWWWWWWWWwWWWWWWWWWWWWWWWwWWWWWWWWWWWWWWWWWWWWWWWWWWWwvwWWWWWWWWWWWWWWWWWWWWWWWWWWWWwvWwwwwwwWWwwwwwwwwwwWWWwwwwwwwwwwwwwWWWWwWWWWWwwwwwwwwwwwwwwwwwWWWWWWwwwwwwwwwwWWWWWWWwwwwwwwwwvwWWWWWWWWWwvWwwwwwwwwwwwwwwwwwwwwwwwwWWwwwwwwwwwwwwwwwwwwwwwwWWWwwwwwwwwwwwwwwwwwwwwwwwwWWWWwwwwwwwwWWWWWwwwwwwwwwwwwwwwwwwwwWWWWWWwwwwwwwwwwwwwww

(new F(delegate V(V f5) {
  return (new F(delegate V(V f9) {
    return (new F(delegate V(V f10) {
      return (new F(delegate V(V f11) {
        return (new F(delegate V(V f15) {
          return (new F(delegate V(V f17) {
            return (new F(delegate V(V f18) {
              return (new F(delegate V(V f19) {
                return (new F(delegate V(V f20) {
                  return (new F(delegate V(V f21) {
                    return (new F(delegate V(V f22) {
                      return (new F(delegate V(V f23) {
                        return (new F(delegate V(V f24) {
                          return (new F(delegate V(V f25) {
                            return (new F(delegate V(V f26) {
                              return (new F(delegate V(V f27) {
                                return (new F(delegate V(V f28) {
                                  return (new F(delegate V(V f29) {
                                    return (new F(delegate V(V f30) {
                                      return (new F(delegate V(V f31) {
                                        return (new F(delegate V(V f32) {
                                          return (new F(delegate V(V f33) {
                                            return (new F(delegate V(V f34) {
                                              return (new F(delegate V(V f35) {
                                                return (new F(delegate V(V f36) {
                                                  return (new F(delegate V(V f37) {
                                                    return (new F(delegate V(V f39) {
                                                      return (new F(delegate V(V f40) {
                                                        return (new F(delegate V(V f41) {
                                                          return (new F(delegate V(V f42) {
                                                            return (new F(delegate V(V f43) {
                                                              return (new F(delegate V(V f44) {
                                                                return (new F(delegate V(V f45) {
                                                                  return (new F(delegate V(V f46) {
                                                                    return (new F(delegate V(V f48) {
                                                                      return (new F(delegate V(V f49) {
                                                                        return (new F(delegate V(V f50) {
                                                                          return (new F(delegate V(V f51) {
                                                                            return (new F(delegate V(V f52) {
                                                                              return (new F(delegate V(V f53) {
                                                                                return (new F(delegate V(V f54) {
                                                                                  return f54(f54);
                                                                                }))(f48(f37));
                                                                              }))(f48(f31));
                                                                            }))(f48(f43));
                                                                          }))(f48(f25));
                                                                        }))(f48(f26));
                                                                      }))(f48(f23));
                                                                    }))(new F(delegate V(V f47) {
                                                                      return f39(f47);
                                                                    }));
                                                                  }))(f39(f36));
                                                                }))(f39(f34));
                                                              }))(f39(f26));
                                                            }))(f39(f42));
                                                          }))(f39(f28));
                                                        }))(f39(f30));
                                                      }))(f39(f33));
                                                    }))(new F(delegate V(V f38) {
                                                      return writer(f38);
                                                    }));
                                                  }))(succ(f36));
                                                }))(f21(f35));
                                              }))(f20(f34));
                                            }))(f22(f32));
                                          }))(f20(f32));
                                        }))(f22(f28));
                                      }))(f21(f28));
                                    }))(f21(f29));
                                  }))(f19(f26));
                                }))(f19(f27));
                              }))(f21(f24));
                            }))(f21(f23));
                          }))(f20(f24));
                        }))(f15(f23));
                      }))(f17(w));
                    }))(f10(f21));
                  }))(f18(f20));
                }))(f18(f19));
              }))(f18(f15));
            }))(f17(f9));
          }))(new F(delegate V(V f16) {
            return f16;
          }));
        }))(new F(delegate V(V f12) {
          return (new F(delegate V(V f13) {
            return (new F(delegate V(V f14) {
              return f14(f12);
            }))(f11(f13));
          }))(f5(f12));
        }));
      }))(f10(f10));
    }))(f9(f9));
  }))(new F(delegate V(V f6) {
    return new F(delegate V(V f7) {
      return (new F(delegate V(V f8) {
        return f6(f8);
      }))(f6(f7));
    });
  }));
}))(new F(delegate V(V f0) {
  return new F(delegate V(V f1) {
    return (new F(delegate V(V f2) {
      return (new F(delegate V(V f3) {
        return (new F(delegate V(V f4) {
          return f4(f2);
        }))(f3(f1));
      }))(f2(f0));
    }))(succ(f1));
  });
}));

ベンチマーク

wを (2^2)^(3^2) = 262144回succした結果を印字するコードでインタプリタコンパイルした奴の速度を比較してみる。

$ cat 262144.grass
wwWWwWWWwvwwWWwWWWwWWWWwvWWwwWWWwwWwwWwwwwwwwWwwwwwwwwwWWWWWWWWw

$ time grass.exe 262144.grass
w
real    0m0.520s
user    0m0.015s
sys     0m0.000s

$ grassc 262144.grass

$ time ./262144.exe
w
real    0m0.265s
user    0m0.015s
sys     0m0.046s

もうちょっと重い処理をさせてみたいけど簡単に書ける重いコードってのがなかなか難しい…

Conclusion

D言語はろくにプログラミング言語を知らない頃にABAさんのSTGでその名前を知り、超絶クールなゲームが作れるプログラミング言語というイメージを持っていましたが、実際使ってみると普通なコードは簡潔に書けて良い感じですし、またCTFE&MIXINで魔界への扉が半分以上開いてる感じもあってとても良いですね。
コンパイルコンパイラコンパイラのライブラリとかいくつもあるみたいですし、escodegen的なライブラリも出てくれば(すでにある?)楽しいだろうなーと思いました。