Forthワードの機能を知る上で知っておくと便利な抽象的な用語をここで整理しておこうと思う。

セマンティクス


Forthでセマンティクスというと、ワードの動作の内容を意味する。一般にはセマンティクスというと意味論と訳され、形式的な式とか文の内容構造を表す形式モデルのことを指したり、そのモデルの割り付け操作のことを指したり、文が表している意味そのものを指したりする。かなり多義的に使われる言葉であるが、forth規格で多用されるセマンティクスは、そのうちの、オペレーショナルセマンティクスといわれるものといえるだろう。それはプログラムのエレメント(Forthではワード)に対応する、演算とか、あるいは、もっと広く、コンピュータ上の処理のことを言っている。

セマンティクスと状態

セマンティクスの種類

Forthでは、セマンティクスは通常3つに分類されるが、細かくいうと4つある
  1. インタープリテーション・セマンティクス(Interpretation Semantics):解釈意味論
  2. コンピレーション・セマンティクス(Compilation Semantics):コンパイル意味論
  3. エグゼキューション・セマンティクス(Execution Semantics):実行意味論
  4. ランタイム・セマンティクス(Runtime Semantics):走行時意味論
である。上の3つは規格文書に用語解説がある正規の用語である。4つ目については、規格文書内で使用されてはいるものの解説的定義の記述はない。

コロン定義でワードを定義するとき、その定義内容というのは、通常のワードでは、上の実行意味論(Execution semantics)に対応する。ワードの定義とは、その実行意味論を定義することなのである。

状態(STATE)

しかし、上の3つないし4つの意味論がどういう意味で区分されるのか、それら自体どのような意味なのかを説明するには、状態(State)という概念を説明しなければならない。
「状態」で区別されるのは、平たくいえば、何かのワード定義の中にあるか、外にあるか、である。
状態には、
  1. コンパイル状態(Compile State)
  2. 解釈状態(Interpretation State)
の二つがある。ワード定義の中ではコンパイル状態で、それ以外では解釈状態である。これは、内部的には、STATEというフラグ変数があって、それがtrueであるときはコンパイル状態となっている。

組み合わせると

まず第一に、コンパイル意味論、解釈意味論、実行意味論、の3つの異なる動作があるのではないということをおさえなければならない。
次いで、解釈意味論とは解釈状態での動作内容であり、コンパイル意味論とはコンパイル状態での動作内容である、ということになる。

通常のワードでは、解釈意味論は実行意味論に一致する。
他方、通常ワードのコンパイル意味論は「その呼び出しコードをコンパイルすること」である。
この呼び出しのコンパイルはデフォルトのコンパイル意味論といわれる。

これに対して、IMMEDIATE指定されたワードでは、コンパイル意味論が実行意味論に一致するのである。これが、IMMEDIATEの機能である。IMMEDIATEワードの解釈意味論が定義される場合は、解釈意味論も実行意味論と一致するのが通常である。ただし、IMMEDIATEワードの中には、解釈意味論が定義されていないものがある。そのようなワードは、ワード定義外では動作は保証されていない、ということである。

要点は、
  • ワードの定義内容が実行意味論であること、
    • それがコンパイル意味論になるワード(IMMEDIATEワード)と、
    • それが解釈意味論になるワード(通常ワード)とがある、
ということである。
そして、また、そのようなワード定義としての意味論特性を変更するのがPOSTPONE、というわけである。(参照:POSTPONE IMMEDIATE

これらに対して、第四の、走行時意味論とは、それらのワードが直接インタープリタ/コンパイラに読み込まれたときの動作ではなく、当該ワードを呼び出したワード定義が実行されたときに、その該当部分はどのような動作をするか、ということである。

通常ワードでは、走行時意味論は実行意味論に一致する。つまり、他のワードから呼び出されて、その実行意味論を実現するのである。

他方、IMMEDIATEワードについては、若干複雑である。
当然ながら、IMMEDIATEワードの走行時意味論は、その実行意味論とは一致しない。解釈意味論ともコンパイル意味論とも同じではない、特有の走行時意味論があるのである。
IMMEDIATEワードが特定のコードをコンパイルするという場合に、そのワードの目的となっているのが、まさに走行時意味論なのである。例えば、IFやループの場合を考えればよい。これらの走行時意味論は、条件分岐すること、あるいは、繰り返しループを実行すること、である。IFには、解釈意味論は定義されていないし、コンパイル意味論も、決して「スタックの値に呼応して分岐実行すること」(IFの走行時意味論)ではないことは分るであろう。というのは、IFのコンパイル時の動作は、条件分岐すること自体ではなくて、分岐を実行するためのコードをコンパイルすることだからである。

しかし、IMMEDIATEワードの中には、走行時の動作を何も設定しないものもある。つまり、IMMEDIATEワードは、その走行時意味論が null (noop) であるものもあり得るのである(コメントの場合など)。その種のIMMEDIATEワードの目的は、コンパイル時にソーステキストを準備ないし後始末処理することである場合が多い。

このように見ると、IMMEDIATEワードには、コンパイル時にも実行意味論が実現されるという点を共通点としつつも、少なくとも二種類の異質なものがある、ということがわかる。一つは、コンパイル時の動作そのものが目的で走行時に何かを実現する意図はないもの、もう一つは、走行時に実現されることが正に重要な目的であるもの、である。もちろん、二つの側面を組み合わせ、両側面を併せ持つワードも可能であろう(例えばforthとは異なる構文のソーステキストを読み取り解析して対応するコードをコンパイルするワード、など。)。

ともあれ、IMMEDIATEワードの中には、走行時意味論をいわなければ、どのような機能を果たすのか説明できないものがあるのである。
むしろ、逆に、各ワードについて走行時意味論を特定してさえもらえれば、普通のプログラミングには困らないのであるが。

インタープリター コンパイラー


インタープリターといったりコンパイラーといったり(あるいは最後の伸ばし棒を略したり)するが、forthには、インタープリターとコンパイラーの二つの機構が備わっているというわけではない。厳密にいえば、インタープリターしかない。ソーステキストインタープリターである。このインタープリターが、コンパイル状態においてはコードをコンパイルするのである。したがって、forthのテキストインタープリターは、コンパイラー機能を備えたインタープリターなわけである。
インタープリター、コンパイラーに、解釈器、コード生成器、という訳をあてれば少し日本語的に見やすくなるような気もするが、
なんとなく、しっくりこないので、カタカナのままにする。

Forthの話題には、よく「内部インタープリター」というのがでてくる。これは、スレッディング方式の場合にしか存在しないもので、ネイティブコード方式には関係がないが、forth特有の用語としては興味深いものである。
どの部分をさすのかというと、ワードのxt、これは普通は間接ポインターだったわけだが、そのポインターを解いて、ポインターリストを逐次実行していく機構の部分である。インタープリターの実行部分、ともいえるだろう。ソーステキストを解析して辞書を検索する部分とコンパイラー部分を取り除いた、インタープリターの核である。これが、アセンブリ言語で4~5行しかなかった、というのが伝説のforthの単純性の話である。実際、その程度で実現可能なのである。もう少し具体的な中身についてはスレッディング技術 (Threading Technology)のページ参照。

関連してコンパイルという言葉が、forthではやや独特な意味で用いられることがある。つまり、必ずしもマシン語に変換するのではなくて、順次実行されるべきワードの間接ポインタの系列を書き込むことをコンパイルというのである。これはforthバーチャルマシンのマシン語に変換するのだと考えれば、例えば、Javaでいうコンパイルと同じ用法として、正当なものということができる。

ワード(語)とディクショナリ(辞書)


Forthではワード(word:語)を用いてプログラミングするのだが、そのワードはディクショナリ(dictionary:辞書)に格納されている。もっともなネーミングである。Forthで普通にワードというと、何らかの計算や処理を実施する関数(サブルーチン)を指すことが多いが、実際は、変数も数字も、概念上はすべてワードである。ifとかloopも、普通の言語では関数でもプロシージャーでもサブルーチンでさえもないが、forthでは、これらは実際に動作内容を持ったサブルーチンであるので、他の関数と同じようにワードと呼ぶことに抵抗がない。最も広い意味では、ワードは、forthのソースコード内の、空白文字(半角空白、タブ、改行)で区切られた非空白文字の列、と規定される。数字(数値リテラル)もワードである。ワードには、既定義ワードと未定義ワードがある。辞書内に登録されているのが既定義ワードである。ソースコード内で、広義のワードが検出されたとき、まずそれが既定義ワードであるかどうか、辞書内を検索して調べる。なければ未定義ワードであるが、続いて数値リテラルとして解釈できないかどうか調べる。数値としても解釈できない場合には、「未定義」エラーになって、コンパイル等は中止される。

辞書には上にも触れたように、既定義ワードに関するデータが格納されている。サブルーチンに当たるワードについては、その内容となる実行コードも辞書内に記録されている。ネイティブコンパイラ方式であれば、そのコードは、機械命令の系列となる。また、データ型ワード、つまり、変数については、そのデータ格納用フィールドは辞書内にある。最近の実行可能ファイル形式では、機械命令実行の高速化のために、数値データを格納するための領域と、機械で直接実行される機械命令の系列を含む部分を分離して、別々の領域に配置する。このような場合に対応して、forthの辞書、特にネイティブコード方式のforthの多くは、機械命令コードを格納する辞書と、データ格納域に当たる辞書、二つに区分された辞書領域をもつ。

実行コード領域に保存された各ワードはポインターでリンクされており、ソースコードとしてワードが読み込まれた時には、そのリンクを辿って、該当ワードが検索される。

データ格納用の辞書領域の空き領域は、一時使用のbuffer memoryとして利用することがある。その際に、空き領域の先頭のアドレスを取るワードがHEREとして定義されている。





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