レガシーコード生産ガイド

私に教えられることなら

Scheme(Gauche)でS式→JSトランスレータ書いた

github.com

S式でJavaScript(とGLSL)を書きたいという欲求が前々からあって、なんか難しそうだから勉強してからだな……と思ってたんだけど、「あっこれ一生とりかからないパターンだ」と昨日の夜中突然思い立ったので、浅い知識でも書き始めることにした。そしたらできてきた。

なんのために作ったのか、サンプル、どう取り組んだかをメモしておく。

なんのためか

おもに楽しみのため、またこういうの書けるようになっておきたかった、というのがあった。

あと、世の中には大量にこういうのあるんだろうし、実際ParenScriptなどを使ってたんだけど、

  • JavaScript結構好きだし、WebGL触ると速度をちょっと気にするので、できれば本当に単にS式で書けるだけ+マクロでやりたい。
  • (obj.fn x y)obj.fn(x, y)になったりとか、ドットノーテーションを気軽に書きたい。
  • 暗黙の処理や特殊な変換もできるだけ抑えて、動きを把握しておきたい。
  • caseSensitiveで、シンボルに記号などを使いたい。

などの要求を満たしてくれるものを探すのがちょっと面倒、既にあるものを改造してもいいんだけどそれなら最初から作ってみたい、ということで書いた。

サンプル

明解WebGLという本を読みながらいじってて、そのときに書いたコードをそのまま移植した

jasj/app.scm at master · phaendal/jasj · GitHub

とりあえずparedit使えるのがとてもありがたかった。

その中から一例、クロージャを作って返す関数の宣言は次のようになる。

 (defn buffer-creator [type ctor]
      (fn [data]
          (let [(buff (gl.createBuffer))]
            (gl.bindBuffer type buff)
            (gl.bufferData type (new (ctor data)) gl.STATIC_DRAW))))

とにかくクロージャを作っていき、関数を組み立てるスタイルでコード量を減らそうとしているので、やっぱりfunction})})みたいな構造は少し面倒だな、と感じる。なのでこういう風に書けるようにした。

また、オブジェクトのプロパティを呼びだすのは次のように書く。

        (let [(src-vs (@ (document.getElementById "vs") textContent))
              (src-fs (@ (document.getElementById "fs") textContent))

単純なfoo.barはそのまま書いて、呼び出しなどが入るとParenScriptを参考に@at記法などで呼び出す。

(@ a b (c x))a.b.c(x)に、(at (a x) b "c" (d y))a(x)[b]["c"][d(y)]と変換される。

あとはまあ、いろいろ。。

取り組み

最初は、S式なんだからそれがASTヤンケ!(よくわかってない)などと直接文字列に変換してたんだけど、次のようなことをやるときにとてもむずかしくなった

  • 関数本体で最後のS式がJSの式の場合はreturnを追加する
  • 関数本体がlet式1つの場合は、letのボディにvarでの変数宣言をそのまま追加したものに展開する。それ以外の場合は、関数の即時実行によってローカル変数用の空間を作る

なので一度全部消して、解析→変換の2フェイズによって書き出すことにした。

そのS式がJSの式になるのか文になるのか、などを解析して、引数や本体などそれぞれのデータを分解して格納することでとても扱いやすくなった。

多分これ当たり前なんだろな、まあ勉強になった。楽しかった。

今後

  • マクロつけたい。
  • GLSL版、CSS版も作ってS式ニンゲンになりたい。
  • 自分で使いながら改善していくとノリノリになった。ドッグフーディングとか言うんだっけ。
  • 面白いと思ってやったコミットメッセージをどのタイミングでやめるか真剣に検討したい。
広告を非表示にする