定義ワードで定義されたワードを、 標準の定義ワードで定義されたワードとは異なる振る舞いにしたい場合は、 以下のように定義ワードを記述できます:
: 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 をあなた独自に実装するための出発点として使って見ましょう。 もし、 あなたが行き詰まった場合は、
' と ['] の振る舞いを調べてください。