IMMEDIATE


IMMEDIATEというワードは、通常は、実行状態で利用される。このワード自体というよりも、このワードを実行したときに他のワードが被る効果が重要である。
IMMEDIATE ( -- )  \ 入出力は何も取らない
IMMEDIATEとは、英語で、「間を置かない」とか「直接の」、「即時の」というような意味である。このワードを実行すると、その直前に定義された実行可能ワード — 通常はコロン定義である — が性質を変える。そのような性質の変わったワードはIMMEDIATE(イミディエット)ワードとも呼ばれる。どのように変わるかというと、コンパイル状態でも実行されるワードへと変わるのである。
: WORD-I  [何かのコード] ;  IMMEDIATE \ WORD-Iがイミディエットワードになる。対応が分り易いように普通はセミコロンの直後に置く。

通常のワード、つまり、IMMEDIATE指定されていないワードは、コンパイル状態で、つまり、何かのワードの定義の中で、その名前を書くと、そこに呼び出しがコンパイルされることになる(原則的コンピレーション・セマンティクス)。これに対して、IMMEDIATEワードは、定義中でも呼び出しはコンパイルされることなく、その時点のコンパイル状態の中で直ちに実行されてしまうのである。やや隠語的にいえば、IMMEDIATEワードは、そのコンピレーション・セマンティクスがエグゼキューション・セマンティクスと一致するワードである、ということになる。ややいい気になれば、「今でしょ!」ワードである(いつまで記憶されるだろうか)。

何の役に立つのかといえば、これによって、その場所に特殊なコードを追加的にコンパイルできるのである。また、直接にコードをコンパイルしなくても、コードのコンパイルのために何か前処理や後処理が必要である場合には、このIMMEDIATEワードを定義して行うことができる。その動作はコンパイラのデフォルトの動作に別途追加されるものである。したがって、IMMEDIATEワードを新たに定義するのは、なにかコンパイラ変形に属するようなことをしている、ということでもある。

この仕組みを使って何か単純で意味のあるコード例を挙げるのは難しいので、既定ワードに絡めて説明することにする。

例えば、forthにはIFやBEGIN、DO LOOPなど、コンパイル状態でしか利用できないワードが相当数ある。実は、これらは全てIMMEDIATEワードである。これらのワードでは、ある有用な機能を持ったコードを生成することが動作の目的であり、それ自体が何か有用なデータ処理を達成することは目的ではないのである。つまり、条件分岐やループの実行コードを生成するのが、それらワードの動作である。そのようなワードはIMMEDIATEワードになる。

しかし、IMMEDIATEワードはすべてコンパイル状態でしか利用できないのか、というと、そうではない。例えば、コメントの開始を記述する \ や、Mopsの (* は、性質上、IMMEDIATEワードである。ワード定義中にもコメントを挿入したいからである。しかし、もちろん、定義の外でも同じように利用でき、機能する。だから、ワード定義中にも何か動作してほしいという要請がある場合、というのがIMMEDIATEワード定義の公約数である。その動作の内容は、コード生成であったり、コメントの読み飛ばしであったり、と様々あり得るのである。他にも例えば、形式的な理由でIMMEDIATEでなければならないものとして、セミコロンがある。というのは、; が実行されるのは、いつもコンパイル状態であって、それを終結させなければならないが、これが普通のワードのように呼び出しがコンパイルされるだけというのでは、いつまでもコンパイルが終結しないからである。加えてセミコロンは、実行コードからのリターンをコンパイルするワードでもある。

もっとも、IMMEDIATEワードで、例えば、マシンコードを直接にコンパイルするようなワードを作って利用しても、Mopsではうまく動作しないであろう。これは、多くの他のネイティブコード方式のforthでもそうなのではないかと思われる。というのは、マシン語へのコンパイルは、一語一語行われるとは限らず、かたまり毎に行うのが自然だからである。その結果、IMMEDIATEワードでマシン語がコンパイルされる段階では、それより前のコードが、まだコンパイルされていないという事態も十分に考えられる。ワード定義の冒頭であれば可能ということもあるであろう(iMopsでは可)。低レベルの操作は環境依存になり、あまり自由なことはできない。

むしろ、既存のIMMEDIATEワードの変形に利用できる、という感じである。
他には、ソーステキスト処理をして構文変更に使ったり、評価可能な部分を先に評価して効率化する、とかいう使い道もないではない。

POSTPONE


IMMEDIATEに対して、ある種、逆変換ともいえる動作をするのがPOSTPONEである。厳密には逆ではない。POSTPONEが作用するのは、それがおかれたその場のみであって、IMMEDIATEのようにワードの属性を半永久的に変えてしまうものではない。

POSTPONEは、英語で「延期する」とか「後回しにする」というような意味である。このワードは実行環境では利用しない。専らコンパイル環境で用いられる。POSTPONEそれ自体はIMMEDIATEワードであり、その後にワード名を入力として予定する。
POSTPONE compilation: ( "<spaces>name" -- )
POSTPONEされたワードは、1段階だけ実行を延期される。「1段階だけ」という妙な言い方は、次のようなことを意味する。

まず、POSTPONEの後にあるワードがIMMEDIATEワードであった場合、そのワードはIMMEDIATEであるにもかかわらず、その場で直ちには実行されず、そこにはそのワードの呼び出しがコンパイルされることになる。つまり、IMMEDIATEワードはPOSTPONEによって、コンパイラで通常ワードのように扱われることになる。そのIMMEDIATEワードが実行されるのは、そのPOSTPONEを含むワードが実行されるとき、ということになる。したがって、例えば、
: END-IF  POSTPONE THEN ; IMMEDIATE
とすれば、IF構文のときに、THENの代わりにEND-IFが利用できるのである。ロジックを説明すると、POSTPONEによって、THENは、その場での実行が抑制されて、そこで呼び出されるコードがコンパイルされる。他方、END-IFをIMMEDIATE指定することによって、END-IFは他のワードの定義中でも実行され、そこにTHENを呼び出すことで、そのコード生成効果をもたらすわけである。もしも、END-IFをIMMEDIATE指定しなければ、他のワードの定義中ではEND-IFの呼び出しがコンパイルされるに止まり、THENの実行は行われずじまいである。逆にTHENの前にPOSTPONEを置かなかったら、IMMEDIATEワードであるTHENはEND-IFの定義のコンパイル中に実行されてしまい、対応するIFが無いためにエラーとなる。

次に、POSTPONEされたワードが通常ワードである場合には、そのワードを呼び出すコードをコンパイルするコードがコンパイルされることになる。ややこしい。メタ呼び出しとでもいうべきか。これは、しかし、例えば、宣言的方法で、特定のワードの呼び出しを定義に付け加えたりするのに、極めて便利である。したがって、実質的には、これもコンパイラ変形の方法を提供するのである。

どちらの場合も、結局、POSTPONEを用いた定義をIMMEDIATE指定すると、POSTPONEされたワードの名前を書くのと同値になる点は、通常ワードでも変わらない。つまり、
: WORD2  POSTPONE WORD1 ; IMMEDIATE
とすれば、
: WORD-A   WORD2  ;
: WORD-B   WORD1  ;
は全く同じ内容を持つのである。ただし、これは、コンパイル環境においての話であって、実行環境におけるWORD1の実行とWORD2の実行は必ずしも同じになるとは限らないことには注意しなければならない。

これは、極めて論理的なのだが、状況はかなりややこしい。POSTPONEはIMMEDIATEワードだがIMMEDIATEはIMMEDIATEワードではない、などと余計な情報を追加するとますます混乱したりする。
そして特に問題となるのは、POSTPONEされるワードがIMMEDIATEワードかそうでないかによって、起こることが違うように感じられることである。そして、そのワードがIMMEDIATEワードであるかどうかは、ワード名からは普通は分らないことが、更に問題を難しくする。
Forthには、ワードがIMMEDIATEであるかどうかを環境中で調べる一般的な方法は無いかのようである。FINDが利用できるはずであるが、
ワード名をFINDの入力に適した形式に変えるのに、少し手間がかかる。
Mopsでは簡単な方法があって、defined?というワードの後に、調べたいワード名を書いて実行すればよい。
二つの値を返すが、下の値は無視してよい。トップの値が1ならIMMEDIATEワードである。-1なら通常ワードである。0なら定義されていない。

「呼び出しをコンパイルする」というのがPOSTPONEの特徴的機能だが、この機能を果たす別のワードも規定されている。ただし、当然のことだが、POSTPONEの動作とは一部重なるものの、同じではない。それがCOMPILE,と[COMPILE]である。

COMPILE, [COMPILE]


COMPILE,は、最後のコンマまでがワード名である。
このワードは実行時に何かのワードのxtをスタックから受け取りその呼び出しをコンパイルする。
COMPILE, ( xt -- )
xtが普通のワードのものでもIMMEDIATEワードのものでも、呼び出しがコンパイルされるのである。典型的には次のような使い方になる。
: WORD1 [内容コード] ;
...
: WORD2  ['] WORD1 COMPILE,  ; IMMEDIATE
: WORD3  .... WORD2 ...  ;
このような形では、WORD3の定義の中のWORD2と書いた所には、WORD1の呼び出しがコンパイルされる。つまり、そこにWORD1と書くのと同値になる。しかし、もし仮にWORD1がIMMEDIATEワードであった場合には、WORD3の中のWORD2は、WORD1と書くのと同値ではない。つまり、WORD1がIMMEDIATEであろうがなかろうが、上のWORD2は呼び出しをコンパイルするのである。WORD2をIMMEDIATEのWORD1に置き換えたなら、[内容コード]がその場で実行されてしまうことになるから、同値ではないのである。

要するに、COMPILE,が実行されたときには、いつも上の[内容コード]の呼び出しがコンパイルされるのである。

これは、IMMEDIATEでない普通のワードについては、POSTPONEと同値で、特別な効用はないと見えるかも知れない。しかし、COMPILE,には、柔軟性の向上という効用がある。
すなわち、POSTPONEは既に定義されたワード名を必要とするが、COMPILE,はコンパイルされるワードのxtを実行時にスタックから取る。そのxtは実行時にまでに準備できれば十分である。結果として、変数を用いてコンパイルされるワードを状況に応じて変えることもできるのである。実行コードをデータとして扱うことによって抽象化する例のひとつである。
歴史的には、xtを単に辞書に格納する ,  (コンマ:1文字ワード)が用いられていた。現在でも用いられることがあるが、しかし、
これが呼び出しをコンパイルする意味になるためには、実装上多くの前提が必要である。そこで、呼び出しをコンパイルすることに限定した
特別なワードが導入された、ということのようである。

[COMPILE]は、COMPILE,のIMMEDIATE版である。つまり、ワード定義内で使用されることが前提とされ、それに引き続くワードの呼び出しをその場にコンパイルする。
[COMPILE]  compilation: ( "<spaces>name" -- )
例えば、
: WORD1  [内容コード] ; IMMEDIATE
...
: WORD  [COMPILE] WORD1 ;
とすれば、WORD1はIMMEDIATEワードであるにもかかわらず、WORDの定義中で実行されることはなく、その[内容コード]の呼び出しがコンパイルされることになる。この局面ではPOSTPONEと同値である。しかし、WORD1がIMMEDIATEワードではないときには、POSTPONEと[COMPILE]は同値ではない。その場合には、POSTPONEでは、前に述べたように、WORD1の呼び出しをコンパイルするコードがコンパイルされることになるが、[COMPILE]では、そこにWORD1の呼び出しがコンパイルされるのである。つまり、WORD1が通常ワードならば、"[COMPILE] WORD1"はただWORD1と書くのと同値である。
要するに、[COMPILE]においては、WORD1がIMMEDIATEワードであるか否かにかかわらず、その場所に[内容コード]の呼び出しがコンパイルされるのである。

したがって、上を統合して述べれば、COMPILE,は、その実行時にスタックから受け取ったxtの内容コードの呼び出しをコンパイルするのであり、他方、[COMPILE]はコンパイル状態中で実行されて、その入力ストリームから受け取ったワードのxtの内容コードの呼び出しをコンパイルするのである。

表にしてみる。
1:実行される
2:実行を呼び出すコードがコンパイルされる
3:実行を呼び出すコードをコンパイルするコードがコンパイルされる

ノーマル POSTPONE COMPILE, [COMPILE]
ノーマルワード Interpretation (3) (2) (2)
IMMEDIATEワード Interpretation (2) (2) (2)
ノーマルワード compilation
IMMEDIATEワード compilation
ただし、括弧付きは形式推論であって実用上の意味はない。規格上も、POSTPONE、COMPILE,、[COMPILE]のすべてについて、ワード定義外での動作は定義されていない。


次:



最終更新:2019年07月17日 23:49