読者です 読者をやめる 読者になる 読者になる

grass.cyのコマンドライン実行以外の使い方

Grass interpreter for Cyan language - * *scrap*
grass.cyはGrassによる関数生成ユーティリティとしても使えます。
grass_eval_string関数は、与えられたコードの最後のapplicationの結果やabstraction定義をクロージャとして返します。

cyan> require "grass.cy"
 => true
cyan> grass_eval_string("w")
 => ^(G7){ begin({ G7 }) }

grassの仕様ではコードの最後がabstraction定義だった場合はその定義に自身を適用しますが、(ex.wWWwwww == wWWwwwwvWw) キーワードパラメータrunに[]かfalseを渡すとこの適用を省き、abstractionを返します。(最後がapplicationだった場合は普通に実行した場合と変わりません。)

cyan> f=grass_eval_string("wwWWwWWWw", :run false)
 => ^(G13){ ^(G15){ begin({ ('G17).(:=)(G13(G15)); ('G18).(:=)(G13(G17)); G18 }) } }
cyan> f(^(n){n*2})(1)
 => 4
cyan> g=grass_eval_string("wwWWwWWWw")
 => ^(G25){ begin({ ('G27).(:=)(G23(G25)); ('G28).(:=)(G23(G27)); G28 }) }
cyan> g(^(n){n*2})(1)
 => 16

fはチャーチ数の2の定義になりますが、gはf(f)=チャーチ数の4となります。
また、outputパラメータとinputパラメータで入出力のストリームを変更する事ができます。

cyan> s="".ostring(); grass_eval_string("wWWwwwwWWWw",:output s)([]); say s.to_s()
 => #<output_stream dbc29e00>
 => 119
ww
 => "ww"

DSLっぽく使えるようにするユーティリティーを定義して使ってみましょう。

require "grass.cy"
mac(_?)^(x):say"#?= "+x.to_s();`let^:r:=?x;say"#?- "+r.to_s();r
eg=^(s):grass_eval_string s.to_s()         #eval
rg=^(s):grass_eval_string s.to_s(),:run[]  #read

say _?(rg'wwWWwWWWwWWWWw)(^(n){n+1})(0) #3
say _?(eg'wwWWwWWWwWWWWw)(^(n){n+1})(0) #3**3

say _?(eg'wWWWWwwww)(1)(0) #lambda true
say _?(rg'ww)(1)(0)        #lambda false

結果

#?= rg('wwWWwWWWwWWWWw)
#?- ^(G7){ ^(G9){ begin({ ('G11).(:=)(G7(G9)); ('G12).(:=)(G7(G11)); ('G13).(:=)(G7(G12)); G13 }) } }
3
#?= eg('wwWWwWWWwWWWWw)
#?- ^(G20){ begin({ ('G22).(:=)(G18(G20)); ('G23).(:=)(G18(G22)); ('G24).(:=)(G18(G23)); G24 }) }
27
#?= eg('wWWWWwwww)
#?- ^(x){ ^(y){ x } }
1
#?= rg('ww)
#?- ^(G36){ ^(G38){ begin({ G38 }) } }
0

lambda trueはプリミティブのwにwを適用することでgrass.cy内で定義されている^(x):^(y):yを取得しています。

ここまでで普通にCyanで書くより短くなるのはww(false)くらいですが、Yコンビネータは非常に短く、、、あれ、Cyanでも結構短くなるな。

y1=^(f):h:=^(g){f^(x){g(g)x}};h h
y2=rg'wwWWwwWwwvwwWWWwWWWwvwWWwWw

fib=^(f):^(n):if(n<3){1}else{f(n-1)+f(n-2)}
say y1(fib) 10
say y2(fib) 10

しまった、Yコンビネータを短く書けるようになるというのが最大の売りだと思ってたのに全然大した事なかった\(^O^)/
CLなら効果大なんだけど\(^O^)/

どうでもいいですけど、一般的なオワタAAは小文字ですが某Hot CornerのAAは大文字なんですね。

その他の機能

機能というかgrass_eval_stringから呼び出してる関数。

cyan> l=grass_parse_string("wvwwWWWww")  # =^(x):^(y):(^(u){u})(x)
 => [[abs, 1, []], [abs, 2, [[app, 3, 2]]]]
cyan> c=grass_compile_code(['abs,0,l],[],:run false)
 => begin({ ('G18).(:=)(^(G20){ begin({ G20 }) }); ('G22).(:=)(^(G24){ ^(G26){ begin({ ('G28).(:=)(G18(G24)); G28 }) } }); G22 })
cyan> c.eval()(1)(0)
 => 1

grass_parse_stringはgrassコードをgrass.el互換の擬似コードに変換して、grass_compile_codeは擬似コードCyanのコードに変換します。
grass_compile_codeの引数は、

  1. 擬似コード
    ['abs,0,(parse結果)]として渡す
  2. 初期環境
    仕様書にあるEとかDとか。シンボルのリストを渡す。(ex.'[out, succ, w, in])
  3. runキーワードパラメータ
    grass_eval_stringと同じ

の3つです。