:(コロン)


既に見たように、forthのもっとも典型的なワード(関数)定義方法は、: を用いる方法である。これも1文字名前のワードである。: を用いる標準的な定義方法をコロン定義(:-definition)などという。まず、: を置き、続いて、定義しようとしているワードの名前を書く。続いて内容定義に入る。定義には、他のワードで実行内容を記述する。最後にセミコロン ; で閉じる。
定義が終了した後は、その名前で自由に呼び出すことができる。
: square-sum   ( n1 n2 -- n )   dup * swap dup * +   ;
30 VALUE Rect1-X
90 VALUE Rect1-Y
80 VALUE Rect2-X
50 VALUE Rect2-Y
...
: Compare-diag  ( -- b ) \ Rect1の対角線の方が長いときTrue、そうでないときFalse
  Rect1-X Rect1-Y square-sum Rect2-X Rect2-Y square-sum >   ;
: のスタック効果は次の通りである。
:  ( C: “<spaces>name” -- colon-sys )
初めにC:とついているのは、このスタック効果はコントロール フロー スタックにおけるものであることを意味する。データスタックを用いても良いとされるが、環境によっては別途スタックを持っている場合もある。二重引用符で囲まれているものはテキスト入力から取ることを意味するのは、データスタック表示の場合と同じである。: が実行された時点で直ちにforth環境はコンパイル状態に入り、ソース入力から定義ワード名を取り、コロン専用のシステムフラグをスタックに置く。: の機能的動作としては、その名のワードの定義内容を辞書にコンパイルする準備を行う。実際、ワード間のリンクやヘッダデータなどの多くのパラメターを辞書領域に記録する動作を : が担っている場合が多い。

ワード名に用いる文字は、空白文字(半角空白、タブ、改行)以外なら制限はない。また、forthでは原則として大文字小文字は区別されない。したがって、定義も大文字小文字の違いだけなら同名ワードの再定義と見なされるし、呼び出しの際に定義名と大文字小文字を間違えても問題ない。ワード名の長さは環境によって制限があるかも知れない。iMopsでは英字63文字(63バイト)までである。短く、内容が分かりやすい名前が好ましいとされる(これがなかなか難しい)。

定義されるワードのスタック効果は、コメントを付けた方が良い。ただ、入力も出力もないワードについては省くことも多い。

なお、現在定義中のワードは、その定義内では呼び出せないような仕様になっている。これは、既存ワードの内容を更新したい場合に、古い定義を呼び出すことができると便利であるから、という理由による。

; (セミコロン)


定義を終了させる; のスタック効果は次の通り
;   ( C: colon-sys -- )
: がスタックに残したシステムフラグを; が拾う。これがコロンを意味するものでないときにはエラーとなる。要するにこのフラグで、コロンとセミコロンが対になっているかどうか調べているのである。
機能上の動作としては、前の : で開始されたワード定義のコンパイルを仕上げ、forth環境の状態をコンパイル状態から解釈実行状態に切り替える。

前にも述べたように、コロンはforth環境を解釈実行状態からコンパイル状態に切り替える。コンパイル状態の中で呼び出された通常のワードは、その解釈実行の動作をその場では行わない。単に、そのワードの呼び出しが、定義内にコンパイルされるだけである。現在定義中のワードが解釈実行されたとき初めて、定義内に書き込まれたワード群もまた実行されるのである。

とすると、疑問が湧くのが ; である。これはコロンの後にあるのであるから、呼び出しがコンパイルされるだけで、実行されず、コンパイル状態を実行状態に戻すことはできないのではないか?
もちろん、; はコンパイル状態でも実行されるのである。そのようなワードをIMMEDIATEワードと呼ぶ。セミコロン ; は、その性質上、必然的にIMMEDIATEワードである。
IMMEDIATEについては別のページで説明するが、コンパイル状態でも実行されるワードを、それによって定義できるのである。
したがって、IMMEDIATEワードの作動は、コンパイラの動作の追加・変更に当たるともいえる。

:NONAME


:NONAMEは、名前のないワードを作るのに用いられる。その定義内容は、辞書内のワード名リンクに登録されず、ワードを識別するタグであるxt (execution tokenの略号)のみをもち、それを通じて特定され、実行される。
:NONAME ( C: -- colon-sys )  ( S: -- xt )
スタック効果のS:マークは、通常のデータスタックを意味し、他のスタックと混同を避ける必要がある場合に付けられるものである。
:NONAMEの後、forth環境はコンパイル状態に切り替わる。通常のコロン定義と同じように、定義内容を記述すれば良い。最後は、; で定義を閉じる。
定義終了後には、スタック上にその定義内容のxtが残される。これを保存しておかないと、この定義にアクセスすることはできなくなるので注意。保存先は通常のVARIABLEやVALUEでかまわない。
:NONAME  ( c b a x -- ax^2+bx+c )  tuck * rot + * +  ;  \ c+ x(ax+b) -- xt
ただし、xtは、一般には、その値を実行可能ファイルやデータファイルなどに保存しても、再起動をまたいで有効性を保つことは保証されていないので注意を要する(再配置可能でないアドレスを用いている実装がある、ということ)。
Mopsではxtはコンパイル時に確定し、その後の辞書の拡大や保存によっては影響を受けない。全体の再コンパイルなどがない限り数値として有効である。

DEFER IS ACTION-OF


DEFERはワード名を宣言しつつ、その内容定義を後回しにするためのワードである。
DEFER aWord
とすれば、ワードaWordが登録され、呼び出しコンパイルが可能になる。しかし、実行される前に実行内容を登録しなければならない。登録するには、定義内容のxtを取って、ワードISを用いる。
IS  ( xt “<spaces>name” -- )
つまり、
( xt )  is aWord
とすれば良いのである。ISはコンパイル状態でも呼び出すことができる。したがって、DEFERされたワードは、その内容を動的に切り替えることができるわけである(ISを用いて内容を切り替えるワードをいくつか定義して、必要に応じて実行すれば良い)。
xtとその取り方については別のページで説明するが、DEFERで宣言されたワードの内容は、:NONAMEを用いて定める場合も多いであろう。
DEFER calculate1
: someword  ... calculate1 .....  ; \ コンパイルは可能
....
:noname  ( x y -- x^2+y^2 )  dup * swap dup * + ;  is calculate1 \ 内容はsquare-sum

DEFERは実行可能ワードをデータとして扱うという考え方に基づいており、それによって宣言されたワードは、一種の変数である。単に呼び出されたときに、そのデータを用いて実行するという部分が含まれているに過ぎないのである。したがって、逆に、DEFER宣言されたワードの現在のxtを取り出すためのワードACTION-OFも定義されている。
ACTION-OF ( “<spaces>name” -- xt )
つまり、
ACTION-OF calculate1
というコードで、calculate1に格納されているxtがスタックに取り出されるのである。なお、「取り出す」といっても複製であり、中身がなくなるわけではない。これを、ISによって、他のDEFERされたワードに格納することもできるわけである。

ISとACTION-OFは、ともに、コンパイル状態でも呼び出すことができる。そのときにも、どちらも、その直後に、対象となるDEFERされたワードの名前が必要である。それを定義中に含むワードの実行時にxtの格納、取り出しが起こる。

なお、DEFERは比較的新しく標準となったワードであり、PowerMopsまでは定義されておらず、代わりにVectというワードが定義されていた。
iMopsではVectは定義されておらず、DEFERが定義されている。

xtの操作に関わるものは、別のページで述べる。



次は、ワード定義方法の拡張について説明する。


最終更新:2019年07月11日 08:03