プログラム言語 wake
Makefile と正規表現とパターンマッチを混ぜたような、トイ言語を作ってみました。
http://shinh.skr.jp/wake/wake.tgz
Hello, world!
all: "Hello, world!"
wake のプログラムは Makefile のように書きます。つまり、 : で区切って左辺にターゲットを書いて、右辺にアクションを書く感じです。上記のように、 "" で修飾された文字列があると、その文字列を出力します。
Makefile のように、右辺には複数のターゲットを書けますし、文字列以外にも、他のターゲットを action として書くこともできます。
all: "Hello, " world world: "world!"
正規表現
左辺のターゲットは、正規表現で書けます。例えば、
all: hoge hige fuga .*: "yay!\n"
とすれば yay! が 3 回出力されます。正規表現は Ruby のものに準じているので、インタプリタは waker という名前になっています。例えば JS なら wakejs とかにすればいいと思います。追記: あとで作った C++ 版は wake という名前で、 PCRE を使ってます。
右辺では、特殊変数 $&, $1, $2, $3, ... が使えます。それぞれ、 $& == マッチした文字列全体、 $N == N 番目の後方参照、です。これを使うと、
all: hoge "\n" (.*)(.): "$2" $1 :
などとして hoge を逆順に出力するプログラムが書けます。 2 行目のターゲットは 1 文字以上の文字列全てにマッチします。アクションでは、一番最後の文字を "$2" で出力して、 "" のついていない、 $1 で、残った文字列を再帰的に評価しています。
3 行目の : は、 $1 がいずれ空文字列になってマッチする文字列が無くなるので、単に何もしないルールとして記述しています。
パターンマッチ
ルールは、常に上から順にマッチするかどうかを評価していきます。 o だけを O に変換して出力するプログラムなら、
all: hoge "\n" o(.*): "O" $1 (.)(.*): "$1" $2 :
などとして書けます。
hoge を変換するだけのプログラムだとつまらないので、標準入力を処理することもできます。 $< という変数には標準入力が全て入っています。
all: $< o(.*): "O" $1 (.)(.*): "$1" $2 :
eval
$(...) となっている表現があると、 ... の部分の文字列をターゲットとして現在のプログラムのルールを実行して、その実行結果の出力に展開されます。
今一つ短くて便利そうな例を思いつかないので、 FizzBuzz を例としてはっておきます。
fizzbuzz: fb@1,1 inc_mod3@2: "0" inc_mod3@(\d): inc1@$1 inc1@9: "0" inc1@8: "9" inc1@7: "8" inc1@6: "7" inc1@5: "6" inc1@4: "5" inc1@3: "4" inc1@2: "3" inc1@1: "2" inc1@0: "1" inc_carry@(\d*)0: "$(inc@$1)0" inc_carry@(\d+): "$1" inc@: "1" inc@(\d*)(\d): inc_carry@$1$(inc1@$2) output@\d*[50],0: "FizzBuzz" output@\d*[50],\d: "Buzz" output@\d+,0: "Fizz" output@(\d+),\d: "$1" # Done fb@101,\d: fb@(\d+),(\d): output@$1,$2 "\n" fb@$(inc@$1),$(inc_mod3@$2)
感想
インタプリタであるところの waker.rb は -v とかつけると、途中のルール適用が表示されるので、デバッグに便利かもしれません。読みにくいですが。上の @ は関数と引数の区切り、という気持ちです。読みにくいから空白とかの方が良かったかも。
こういう細かい書くべきことは色々ある気がするんですが、まぁいいことにします。
正規表現がとても好きなので、こいう感じになったんじゃないかなと思います。 sed と比較すると、 fizzbuzz 書くまでの時間とか考えるとだいぶラクだったかなぁとか思います。別にラクさを目的として作ったわけじゃないですが。
ミニマルな言語としては、こんなもんかなぁと思うのですが、なんか機能を足すとしたら、整数演算くらいは欲しいかな…とは思います。 $[...] があったらその中身を四則演算する、くらいかなぁと(追記: どっちかというと、 include をできるようにして、標準ライブラリとして提供すればいいか…)。昨日思いついただけのものなので、意見とかいただければ嬉しいです。先行研究括弧笑いとか調べてないので、似たようなものはあるかもしれませんが、それも教えていただければー、と。
今後の展望としては、 Quine くらいはすぐ書けるんじゃないかと思います。あと JS と C で書いた処理系が欲しい。それとゴルフしてみるべき。