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つの特徴があります。

  1. "parent"で、構文木におけるparent(直親)のノードにアクセスします。
  2. "childset"で、構文木における現在のノードのchildにアクセスします。
  3. explicitなコードは `hoge' で埋め込む事が出来る。

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して、するとアドレスが中に入っているのでそいつを参照外しして、コードセグメントをぶち込む、という感じだそうです。