定義ワードで定義されたワードを、 標準の定義ワードで定義されたワードとは異なる振る舞いにしたい場合は、 以下のように定義ワードを記述できます:
: def-word ( "name" -- ) CREATE code1 DOES> ( ... -- ... ) code2 ; def-word name
このコード断片は 定義ワード(defining word) def-word
を定義し、 そして、 それを実行します。
def-word
が実行されると、 新しいワード name
が CREATE
され、コード code1
が実行されます。 コード code2 は現時点では実行されません。 name
というワードは、 def-word
の子供(child)と呼ばれることもあります。
name
を実行すると、 name
の本体のアドレスがデータ・スタックに置かれ、 code2
が実行されます(name
の本体のアドレスは、 CREATE
の直後に HERE
が返すアドレス、 つまり、
create
されたワードがデフォルトで返すアドレスです)。
def-word
を使用して、 同様に動作する一連の子ワード達を定義できます。 つまり、 これらはすべて code2
によって決定される共通の実行時の振る舞いを持っています。 通常、 code1 シーケンスは、 子ワードの本体にデータ領域を構築します。
データの構造は def-word
のすべての子に共通ですが、 データ値は各子ワードに固有で、 そして、 プライベートです。
子ワードが実行されると、 そのプライベート・データ領域のアドレスが TOS 上のパラメーターとして渡され、 code2
によって使用および操作されます16。
定義ワードを構成する 2 つのコード断片は、 完全に別々の 2 つの時点で動作(実行)されます:
def-word
と name
の振る舞いを理解するもう 1 つの方法は、 以下のように定義することです:
: def-word1 ( "name" -- ) CREATE code1 ; : action1 ( ... -- ... ) code2 ; def-word1 name1
この場合、 name1 action1
を使用することは、 name
を使用することと同じです。
def-word
を記述するもう 1 つの方法が引用(see Quotations)です:
: def-word ( "name" -- ; name execution: ... -- ... ) create code1 [: code2 ;] set-does> ;
Gforth は実際は does>
を使用してコードを後者のコードと同等のコードにコンパイルします。 set-does>
アプローチの利点は、 その背後に他のコードを配置でき、 回避策を必要とせずに制御構造内でそれを使用できることです。 欠点は、 Gforth
固有であることです。
典型的な例は、 以下のようにして CONSTANT
を定義できることです:
: CONSTANT ( w "name" -- ) CREATE , DOES> ( -- w ) @ ;
または同等の
: CONSTANT ( w "name" -- ; name execution: -- w ) create , ['] @ set-does> ;
5 CONSTANT five
を使用して定数を作成すると、 一連の定義時アクションが実行されます。 最初に新しいワード
five
が作成され、 次に ,
を使用して five
の本体に値 5 が配置されます。
five
が実行されると、 その本体のアドレスがスタックに置かれ、 @
は値 5 を取得します。 ワード
five
にはそれ自体のコードはありません。 ワード five
には、 データ・フィールドと、 引用(quotation)
の xt または @
の xt が含まれるだけです。
このセクションの最後の例は、 CREATE
されたワードで予約されている空間はデータ空間であるため、
標準プログラムによる読み取りと書き込みの両方が可能であることを思い出していただくことを目的としています17:
: foo ( "name" -- ) CREATE -1 , DOES> ( -- ) @ . ; foo first-word foo second-word 123 ' first-word >BODY !
first-word
が CREATE
されたワードであった場合、
単純にそれを実行してデータ・フィールドのアドレスを取得できます。 ただし、 DOES>
アクションを持つように定義されているため、
その実行機能(execution semantics)は、 それらの DOES>
アクションを実行することになります。
データ・フィールドのアドレスを取得するには、 '
を使用して xt を取得し、 次に >BODY
を使用して xt
をデータ・フィールドのアドレスに変換する必要があります。 first-word
を実行すると、 123
が表示されます。
second-word
を実行すると、-1
が表示されます。
上記の例では、 DOES>
の後のスタック・コメントは、 DOES>
に続くコードのスタック効果ではなく、
定義されたワードのスタック効果を指定しています(DOES>
に続くコードは、 本体のアドレスがスタックの先頭にある事を期待しています。
これはスタック・コメントには反映されません)。 これは著者が使用し、 推奨している規則です(ただし、
スタック効果の指定にローカル変数宣言を使うのと少々衝突する)。
このデータ領域への読み取りと書き込みは両方とも正当です。
研究課題: この例は
Value
と TO
をあなた独自に実装するための出発点として使って見ましょう。 もし、 あなたが行き詰まった場合は、
'
と [']
の振る舞いを調べてください。