ここで、 前のセクションの add-two
の定義をもう一度見てみましょう。 テキスト・インタープリターの動作方法に関する知識から、
add-two
を定義しようとしたとき、 私達は以下の結果を予期したかもしれません:
: add-two 2 + . ;RET *the terminal*:4: Undefined word : >>>add-two<<< 2 + . ;
しかし、 これが起こらなかった理由は、 :
の動作方法に関係しています。 :
というワードは 2 つの特別な働きをします。
1つ目の特別な機能は、 テキスト・インタープリターが add-two
という文字を認識できないようにすることです。
テキスト・インタープリターは、 >IN
(to-in;トゥーイン)という変数を使用して、 入力行のどこを追跡するかを保持し、
:
というワードに遭遇すると、 他のワードの場合とまったく同じように動作します。 名前ディクショナリーでそれを検索し、 その xt
を見つけて実行します。 実行される :
は、 入力バッファーを調べてワード add-two
を見つけ、
>IN
の値をその後ろを指すように進めておきます。 次に、 新しい定義の作成に関連するその他の処理を実行します(名前ディクショナリーに
add-two
のエントリを作成する等)。 :
の実行が完了すると、 制御はテキスト・インタープリターに戻ります。
テキスト・インタープリターは、 このトリックにより入力行の一部をスキップしていることに気づきません。
:
のようなワード(>IN
の値を進めて、 テキスト・インタープリターが入力行全体に作用するのを妨げるワード)は、
「構文解析ワード」(parsing words)と呼ばれます。
:
が行う 2 つ目の特別な処理は、 state
と呼ばれる変数の値を変更することです。 これは、
テキスト・インタープリターの振る舞いに影響します。 Gforth が起動するとき、 state
の値は 0 であり、
テキスト・インタープリターはインタープリター状態(interpreting)であると言われます。 (:
で始まる)コロン定義中
、 state
は -1 に設定され、テキスト・インタープリターはコンパイル状態(compiling)と言われます。
この例では、 テキスト・インタープリターは文字列 “2 +
. ;
”。引き続き同じ方法で文字列を文字シーケンスに分割します。ただし、数値 2
をスタックにプッシュする代わりに、 数値
2
を取得する魔法を add-two
の定義に組み込み(コンパイル)、 add-two
が「実行」されたときスタックにプッシュされます。 同様に、 +
と .
の振る舞いも定義にコンパイルされます。
特定の種類のワードはコンパイルされません。 これらのいわゆる「即実行ワード」(immediate word)は、
テキスト・インタープリターがインタープリター状態であるかコンパイル状態であるかに関係なく、 実行されます(今、 直ちに実行されます)。 ;
というワードは即実行ワードです。 定義にコンパイルされるのではなく、 実行されます。 その効果は、 state
の値を 0
に戻すことを含む、 現在の定義を終了することです。
あなたが add-two
を実行すると、 その定義の外で 2 + . RET
と入力した場合とまったく同一の「実行時効果」(run-time effect) が生じます。
Forth では、 すべてのワードまたは数値は以下の 2 つの性質を持ちます:
数値は常に決まった方法で処理されます:
ワードは常にこのような通常の振る舞いをするとは限りませんが、 ほとんどのワードにはデフォルトの機能(default semantics)があり、 以下のように振る舞うことを意味します:
特定のワードの実際の振る舞いは、 ワードの定義時に immediate
や compile-only
というワードを使用することで制御できます。 これらのワードは、 最後に定義されたワードの名前ディクショナリー・エントリにフラグを設定します。
これらのフラグは、 名前ディクショナリーでワードが見つかったときにテキスト・インタープリターによって取得されます。
immediate としてマークされたワードは、 そのインタープリター機能(interpretation semantics)と同じコンパイル機能(compilation semantics)を持ちます。 つまり、 以下のように振る舞います:
ワードを compile-only としてマークすると、 インタープリター状態(interpretation
state)でこのワードを検出したときにテキスト・インタープリターが警告を生成することを意味します。 ('
または [']
を使用して) ワードをティックすると、 警告が生成されます。
compile-only
を使用する必要はありません(多くの実装によって提供されてはいますが、 標準 Forth
の一部でもありません)が、 インタープリター態(interpret state)で正しく振る舞わないワードに compile-only
を適用するのは良いエチケットです(そして予期しない副作用が発生する可能性があります)。 たとえば、 定義内で条件ワード IF
を使用することのみが正当です。 これを忘れて別の場所で使用しようとすると、 (Gforth では) compile-only
としてマークされているため、 テキスト・インタープリターが有用な警告を生成できます。
以下の例は、 即実行ワードと非即実行ワードの違いを示しています:
: show-state state @ . ; : show-state-now show-state ; immediate : word1 show-state ; : word2 show-state-now ;
show-state-now
の定義の後にあるワード immediate
は、そのワードを即実行ワードにします。
これらの定義では、 @
(「フェッチ」と発音します) という新しいワードが導入されています。
このワードは変数の値を取り出し(フェッチし)、 それをスタックに残します。 したがって、 show-state
の振る舞いは、
state
の現在の値を表す数値を出力することです。
word1
を実行すると、 システムがインタープリター状態であることを示す数値 0 が出力されます。 テキスト・インタープリターが
word1
の定義をコンパイルしたときに、 コンパイル機能が現在の定義に実行時コードを追加する show-state
に遭遇しました。 word1
を実行すると、 show-state
のインタプリタ機能(interpretation
semantics)が実行されます。 word1
(つまり show-state
) が実行される時点で、
システムはインタープリター状態です。
word2
の定義を入力した後に RET を押すと、 数値 -1 が出力され、 その後に ok
が表示されるはずです。 テキスト・インタープリターが word2
の定義をコンパイルすると、 即実行ワードである
show-state-now
が検出されたため、 そのコンパイル機能(compilation
semantics)はインタプリタ機能(interpretation semantics)を実行します。
これは直ちにに実行されます(テキスト・インタープリターが次の文字グループ(この例では ;
)の処理に移る前に)。 これを実行すると、
word2
の定義途中の state
の値が表示されます。 -1 を出力するので、
システムがその時点でコンパイル状態であることがわかります。 もし あなたが word2
を「実行」しても何も行いません。
即実行ワードの話題を離れる前に、 前のセクションの greet
の定義における ."
の振る舞いについて検討してみましょう。
このワードは構文解析ワード(parsing word)でもあり、 かつ、 即実行ワードでもあります。 ."
とそのテキストの先頭
Hello and welcome
の間にはスペースがありますが、 welcome
の最後の文字と "
文字の間にはスペースがありません。 これは、."
が Forth ワードであるということです。 テキスト・インタープリターがその
Forth ワード ."
を識別できるように、 ."
の後ろにスペースが必要です。 "
は Forth
ワードではなく、 区切り文字(delimiter)です。 先の例では、 文字列が表示されるときに、 H
の前にも e
の後ろにもスペースがないことを示しています。 ."
は即実行ワードなので、 greet
の定義中に実行されます。
実行されると、 入力行内を前方に走査して区切り文字を探します。 区切り文字が見つかると、 区切り文字の後ろを指すように >IN
を更新します。 また、 いくつかのマジック・コード、 つまりテキスト文字列を出力する実行時コード xtを greet
の定義にコンパイルします。 文字列 Hello and welcome
をメモリーにコンパイルして、 後で出力できるようにします。
その後、 テキスト・インタープリターが制御を取得すると、 入力ストリーム内で次に検出されるワードは ;
であるため、
greet
の定義を終了します。