素朴な自作言語のコンパイラをPHPに移植した


移植一覧に戻る


やっつけなので汚いです。ライフゲームコンパイルが通ったのでヨシ、というレベルのものです。

github.com


移植元

memo88.hatenablog.com

ベースになっているバージョン: tag:50 のあたり

ただ、実質的には Perl版 からの移植です。

メモ

  • PHP を書くのは今回が初めて
  • さらっと文法を見た感じ Perl に近そうだったので、 Perl版をコピーして書き換えていった
    • 割と機械的に置き換え
      • my を消して
      • sub => function
      • elsif => elseif
      • 正規表現はダブルクォートで囲んで preg_match にして
      • ……などなど
    • PerlPHP という流れで進んだのは良かった
  • Emacsphp-mode を追加するのがめんどくさかったので perl-mode で書いてた
  • クラスも普通に使えるし、 int と string の型の判別もできるし、 Perl より楽ちんという印象。 正直なところあまり書くことがないです……。

今回の実験コーナー。

これまで Dart への移植 のときに set を不要にし、 Java への移植 のときに call を不要にし、 Perl への移植 のときに call_set を不要にしてきました。

これらを全部適用するとどうなるか trial ブランチ でやってみました。

とりあえず parse_stmt() の分岐のとこだけ貼ってみます。

<?php
# ...

    # if     ($t->str_eq("set"     )) { return parse_set();        }
    # if     ($t->str_eq("call"    )) { return parse_call();       }
    # elseif ($t->str_eq("call_set")) { return parse_call_set();   }
    elseif ($t->str_eq("return"  )) { return parse_return();     }
    elseif ($t->str_eq("while"   )) { return parse_while();      }
    elseif ($t->str_eq("case"    )) { return parse_case();       }
    elseif ($t->str_eq("_cmt"    )) { return parse_vm_comment(); }
    else {
        if (
               $t->kind_eq("ident")
            && peek(1)->is("sym", "=")
        ) {
            if (
                   peek(2)->kind_eq("ident")
                && peek(3)->is("sym", "(")
            ) {
                return parse_call_set();
            } else {
                return parse_set();
            }
        } elseif (
               $t->kind_eq("ident")
            && peek(1)->is("sym", "(")
        ) {
            return parse_call();
        } else {
            throw not_yet_impl($t);
        }
    }

個別に試していたときはそれぞれの個別のことだけ考えてやればよかったのが、 3つを同時に満たそうとして else に押し込めてとりあえずこうなった、というものです。 ちょっと野暮ったいですが peek(n) で先読みしてやればなんとかなるようです。

funcall を expr に含めるとかするともうちょっとすっきりする気がします。

vgコードはこんな感じ。 ここまでやるとかなり普通の言語(というか JavaScript)っぽい見た目になりますね。

func main() {
  var w = 5; // 盤面の幅
  var h = 5; // 盤面の高さ

  // 初期状態の設定
  vram_set(w, 1, 0, 1);
  vram_set(w, 2, 1, 1);
  vram_set(w, 0, 2, 1);
  vram_set(w, 1, 2, 1);
  vram_set(w, 2, 2, 1);

  var gen_limit = 0;
  var gen = 1;
  while (gen != gen_limit) {
    make_next_gen(w, h);
    replace_with_buf();
    gen = gen + 1;
  }
}