PCASTL入門
PCASTL: by Parent and Childset Accessible Syntax Tree Language
PCASTLは今年LL未来とかで話されると良いですね!
適当に入門記事(殆ど適当に訳しただけ)を書きますが、読みながら同時に書いてるのでリアルタイムに変わります。
PCASTLってなんぞ
PCASTLはParent and Childset Accessible Syntax Tree Languageです。
Syntax Treeにアクセス可能な言語(?)です。
self-modifyingが簡単に出来る事を志向しているようです。凄いLLですよね。
自己書き換えを達成する為の3つの特徴があります。
sample1
Win向けのインタプリタのバイナリはページ下部から取れるので、適宜使用入手してください。
本家の画像を参照しながら。
> a = parent 0x330878 > info(a) Node type: mathematical operator Number of childs: 2 Operator: = infoで取得するのは、"Operator ="のアドレスです。 aで"Operator ="のノードを取得するのはちょっと変な気がしますが・・・。 > info(a.childset[0]) Node type: variable Name: "a" childsetとはまた変に長い気がしますが、取りあえずこれで子ノードの0を取得します。 これがName:"a"になっています。 a = parentの左辺値ですね。 > info(a.childset[1]) Node type: list Number of items: 1 こんどは子ノードの1を取得します。 これは要素数1のリストを保持しているノードで有る事が分かります。 > info(a.childset[1].childset[0]) Node type: "parent" reserved word そして、このリストの中を覗き込んでいます。 > a.childset[0].parent 0x330878 > a.childset[1].parent 0x330878 parentを直親を表すキーワードです。
ちょっとギモギモした所もありますが、取りあえず構文木にアクセスしている匂いがあります。
sample2
# first way # function definition: a = function() { print(1) if (value(a.childset[1].childset[0].childset[1].childset[0]) == 1) { a.childset[1].childset[0] = `print(2)' } else { a.childset[1].childset[0] = `print(1)' } {} } # function calls a() a()
aに関数呼び出しをbindして、今回は構文木を弄るということで、直接print(1)を変える事が目的です。
function()で関数を作った時には、まずa.childset[0]でこの関数の引数のリストを表すノードを返します。
今回は何も無いので、
info(a.childset[0]) Node type : list Number of items : 0
という事に成ります。
a.childset[1]では一方、関数の中のstatementが得られます。
今回の場合だと、a.childset[1].chilset[0]で"print(1)"のノードが得られます。
関数呼び出しの場合だと、childset[0]に関数名、この場合ですとprint
childset[1]に引数のリストなので、a.childset[1].childset[0].childset[1].childset[0]をvalue関数になげれば、
"現在のprint関数の引数の値"が返ってくるので、それが1の時は"print(1)"をprint(2)に書き換えてしまいます。
これを繰り返しているだけですので、aをcallする度に
a() 1 a() 2 a() 1 a() 2...
と成ります。
sample3
a = function() { print(1) if (value(parent.parent.parent.parent.parent .childset[0].childset[1].childset[0]) == 1) { a.childset[1].childset[0] = `print(2)' } else { a.childset[1].childset[0] = `print(1)' } {} } a() a()
やっている事は全く同じですが、今度はparentを使っているという点でだけ違います。
こっちは下から見ていっているので、逆に上からparentが出ているノードまで行けば分かりやすいと思います。
info(a.childset[1].childset[1](ここでif-else state).childset[0](operator ==).childset[0](call of value).childset[1](?).childset[0]
"node:call of value"のchildset[1]で何が返ってきているのやら分かりません。
sample4
今度はprint呼び出しノードをそのまんま変えるのではなくて、引数だけ変えてあげましょうという話。
a = function() { print(1) b = parent.parent.childset[0].childset[1].childset[0] if (value(b)== 1) { *b = `2' } else { *b = `1' } {} } a() a()
parentはa.childset[1].childset[1].childset[1]にあるので、そのparent.parentはa.childset[1]
よって、a.childset[1].childset[0](print呼び出しノード).childset[1](その引数リスト).childset[0]で第一引数を得る。
それをbにbindして、するとアドレスが中に入っているのでそいつを参照外しして、コードセグメントをぶち込む、という感じだそうです。