[SikiLanguage] [式鬼言語航海日誌] 2007-09-23
2007/09/23_000000今回はブロックの呼び出しを実装しました。メソッド呼び出しとか関数呼び出しとか呼ばれるものですね。
「あれ? もう実装しているンじゃなかったっけ?」
ええ、そうなんですけど、ちょっと厳密に見直しました。以前にも書いていますけど内容が変更になっています。
例えばこんな感じですね。
<?:String :String :..test> <::{2 1 0| ..append}. #メソッドの定義 'test1' 'test2' ..test #メソッドの呼び出し。結果は 'test2test1'
それぞれの意味は
- <?:[引数のmaster] [引数のmaster]…… [メッセージ]>
[引数のmaster]をmasterとして持つCellがスタックに積まれている状態で[メッセージ]を飛ばした時に実行するCellをスタックに積む。 - master: 移譲先のCell。親クラスみたいなもの
- 頭に"."の付いた単語: スタックに積まれたCellに関連付けられたCellを取り出して実行するメッセージを実行するCell。"."と":"の数によって引数となるCellの数が変わる。
- [コピー先ブロック] <: [コピー元ブロック]
[コピー元ブロック]を実行した結果のCellを[コピー先ブロック]を実行した結果のCellにコピーする - :[Cell]
Cellを実行しないでそのままスタックに積む - {[引数の数] [引数1] [引数2]……| [実行するCell]……}
Executorを作る。Executorが実行されると スタックの上から[引数の数]分のCellを[引数1] [引数2]……の順番に並び換える [実行するCell]……を次に実行する命令列に押し込む という動作をする
- .
スタックの最後のCellをPopする - (空行)
パースを一時中断し、今までパースした結果を実行する
となります。
「細かいねえ。構文として処理しないで一つ一つの単語に意味を持たせた訳かい?」
はい。せっかくForthもどきにしているのですから、ここらへんも作り込んだほうが良いかと思いまして。ただし、タイプミスで全然違う挙動をするリスクがありますので危険でもありますが……まあ、ここはピーキーな俺言語ということで……
結局のところ、この文によって『スタックに文字列が2つある場合に、スタックの上の文字列にその下の文字列を加える』メソッドが定義されました。また、最後にpopしていますので、スタックには何も残りません。
「最後の“.”を忘れるとどうなるンだい?」
<:は最後に[コピー先ブロック]を残しますので、最後のpopがなければそのまま残ります。場合によっては次のパース実行で変なイタズラをする可能性がありますので、忘れずに処理するようにしてください。大抵は「メソッドを呼んでもいないのに実行される」という挙動になるかと思います。
「うわ……極悪……」
まあ、ここは(空行)でスタックの中身を空にするようにしようか悩み中なんですけどね。さすがにパーサーも絡んだ挙動は判り辛いですし。そのあたりはもうちょっと練り込むことにしましょう。
メソッド呼び出しは今まで使ってきたのと同じですね。
'test1' 'test2' ..test #メソッドの呼び出し。結果は 'test2test1'
文字列はそのままスタックに積まれ、..testメッセージによって先ほど定義したメソッドが呼ばれます。メソッドは前述の通り「スタックのCellを積みなおす」「メソッドの中身を次に実行する命令列に押し込む」という2つの動作を行うExecutorになっています。結果として、スタックに積まれた'test1''test2'の順番を入れ替えて、test2'に'test1'の文字を追加して、追加した文字列のみスタックに残します。
「Executorというのはある意味Forth Wordみたいな感じかね」
Forthの関数定義みたいな感じですかね?
いやあ、Forthって凄いですね。スタック&辞書(Forth Word)で、基本的な動作が破綻なくできてしまいます。
「でも、これじゃインラインで展開されるから名前の汚染とかフロー制御とか面倒そうだね。新しくスコープを作るのはないンかい?」
そのようなケースはFiberを活用します。こっちの説明はまた明日ということで。
制作・著作: 野分(nowake) at fiercewinds.net (Creative Commons 表示-継承 2.1 日本)