ホーム   最近   SikiWiki   編集   新規 

[SikiLanguage] [式鬼言語航海日誌] 2007-09-02

2007/09/02_000000




まいど。夏の盛も過ぎて、すっかり涼しくなりましたね。

「前は初夏も始めだったのにねぇ」

まあ、期間がかかりましたが進捗ありましたよ。基本的なメソッド呼び出しまで意味付けできました。

忘れないうちにメモ書きしておきます。頭の中を整理するために書いていますので、判り辛いのはご容赦下さい。

「図も使うと分かり易いンだけどねぇ」

そこはこのWikiエンジンの限界ということで……

まず、全体の流れとしては RootとなるCellに文字列を与える。 Rootが文字列をパースして、命令列に変換 Rootの中のパースエンジンが文字列を命令列に変換してRootのhead列に保存 トップレベル(=Root)のスコープで空行が出て来たらパースを中断 パースを中断したタイミングで、解釈済の命令列を実行 Rootのhead列を逆さにしながらbody列に全て移動 body列の命令を実行 body列が無くなったら実行を中断してパース再開 上記を繰り返し 文字列をパースしきったら終了

といった感じです。6月のアイディアを具体化したものですね。

「ブロックごとに実行していく感じかね」

そうですね。塊ごとに逐次的に実行していく感じですね。

まだ実装はしていませんが、究極的にはRootのパースルールを変更する命令列も用意したいと考えています。

で、次が命令列の挙動について。

命令列……式鬼言語ではCellと呼んでいますけど、大きく分けると

トークン(id): 名前空間から変数を持って来て戻り値として継続のhead列に(戻り値として)渡したあと、 メソッド呼び出しをスタートする。 オブジェクト:自分自身を戻り値として継続のhead列に(戻り値として)渡す。 その他命令列:それぞれ固有の処理を行う。戻り値も色々。

といった内容になります。

「判り辛いねぇ。色々と前提をすっ飛ばしているだろ。この説明」

……そうですね。まあ、まずはメモ書きということで……オブジェクトとその他命令列はまあ、何とかわかるのではないかと思います。手続き型言語でいうと、オブジェクトはリテラルの、命令列はコマンドの、それぞれの挙動に近いですからね。

で、最後のトークン(id)は、まとめて言うと、

変数の名前解決 --> 変数に保存されているCell(or トークンそのもの)を利用してメソッド呼び出しを処理

というCellということになります。

「さっぱり判らん。例を出せ、例を」

そうですね。まあ、実際の例を出しても判らないかもしれませんが……

:test1 :test2 .. :test3 <== {!'test'} test1 test2 .. test3

というソースを例に説明しましょう。

まず、Root(というCell)は、この文字列を途中までパースします。

|Transport| test1 |Transport| test2 .. |Transport| test3 |Skewer| |Transport| <InlineExecutor> |Copier| test1 test2 .. test3

|~~|というのは命令列のCellです。ここでは

  • |Transport|:次に実行するはずの命令列を実行せずにそのまま継続に(戻り値として)渡す
  • |Skewer|:メソッド呼び出しのベースになるCellを検索するして継続に(戻り値として)渡す
  • |Copier|:継続に(戻り値として)積まれているCellを2つ取り出して、片方の内容をもう片方にコピーする

という挙動になります。

<InlineExecutor>というのも命令列なのですが、「{!~~}の中身を実行する」という命令列になります。

つまりは、「test1 test2 test3というCellをそれぞれ継承元とする引数を持つメソッドを定義する」という命令列になります。

命令列はRootのbody列に積まれていますので、順次実行していくと

Root::body |Transport| test1 |Transport| test2 .. |Transport| test3 |Skewer| |Transport| <InlineExecutor> |Copier| Root::tail

Root::body |Transport| test2 .. |Transport| test3 |Skewer| |Transport| <InlineExecutor> |Copier| Root::tail test1

Root::body .. |Transport| test3 |Skewer| |Transport| <InlineExecutor> |Copier| Root::tail test1 test2

Root::body |Transport| test3 |Skewer| |Transport| <InlineExecutor> |Copier| Root::tail test1 test2 ..

Root::body |Skewer| |Transport| <InlineExecutor> |Copier| Root::tail test1 test2 .. test3

Root::body |Transport| <InlineExecutor> |Copier| Root::tail <test1 test2 .. test3 メソッドのベースとなるCell>

Root::body |Copier| Root::tail <test1 test2 .. test3メソッドのベースとなるCell> <InlineExecutor>

Root::body Root::tail <test1 test2 .. test3メソッド>

といった感じです。

「ここで文字列のパースが再開されるわけだね」

はい。Rootのbody列を使い切りましたので、再開します。パースする前に、tailに積まれたCellはクリアされます。

パースが終了するとこんな感じですね。

Root::body test1 test2 .. test3 Root::tail

「そのまンまだね」

はい。ただ、bodyに積まれたのは命令列ではなくてトークン(id)ということに注意してください。では逐次実行してみましょう。

Root::body (|Trigger|) test2 .. test3 Root::tail test1

ここで|Trigger|という命令が新たに積まれていることに注意してください。()で括られているのは、実際にはbodyに積まれないで直接実行されるためです。

|Trigger|という命令は tailに積まれたCellを新しく積まれた順に検索してメソッドを探す 該当するメソッドが存在する場合はメソッドを実行する 該当するメソッドが存在しない場合は何もしない

という命令です。いわゆるメソッド呼び出しですね。

「ふむ……メソッドを名前で呼び出すンじゃなくてCellで呼び出す、つうことか」

はい。トークンを処理するときはその都度メソッド呼び出しが発生します。パフォーマンス的なインパクトはありますが、代わりに柔軟性が手に入ります。まあ、それは後ほど。

test1 test2 .. まではメソッドがありませんので、そのままtail列に積まれます。test3まで積むと、先ほど定義したメソッドが要求するCellとスタックに積まれたCell(の継承元)が一致しますので、ようやっとメソッドが実行されます。

Root::body (|Trigger|) Root::tail test1 test2 .. test3

Root::body (<test1 test2 .. test3メソッド>) Root::tail test1 test2 .. test3

メソッドは'test'という文字列を返します。

Root::body Root::tail test1 test2 .. test3 'test'

で、めでたく終了しました。

「長かったねぇ。結局、引数は使わなかったね」

ええ、まだ実装が追い付いていないところがありまして……

引数については

  • tailに積んである引数を、順番入替え/コピー/削除する命令列がある
  • tailから降ろす/参照するときに、Closure(Delegater)の評価を行う

という特徴があります。このあたりは実装が終わったころにもう一度ということで。





制作・著作: 野分(nowake) at fiercewinds.net (Creative Commons 表示-継承 2.1 日本)