Previous: , Up: Compiling words   [Contents][Index]


6.13.2 Macros

Literal とその仲間たちは、 データ値を現在の定義にコンパイルします。 他のワードを現在の定義にコンパイルするワードを記述することもできます。 例えば:

: compile-+ ( -- ) \ compiled code: ( n1 n2 -- n )
  POSTPONE + ;

: foo ( n1 n2 -- n )
  [ compile-+ ] ;
1 2 foo .

これは : foo + ; と同等です(確認するには see foo としてください)。 この例では何が起こっているのでしょうか? Postpone は、 + のコンパイル機能(compilation semantics)を compile-+ にコンパイルします。 その後、 テキスト・インタプリタは compile-+ を実行し、 + のコンパイル機能(compilation semantics)を実行します。 これにより、+ (の実行機能(execution semantics)) が foo にコンパイルされます23

postpone ( "name" –  ) core “postpone”

name の コンパイル機能(compilation semantics)をコンパイルする。

compile-+ のようなコンパイル・ワード(compiling words)のコンパイルは通常即実行ワード(または即実行同様)であるため、 それらを実行するためにインタプリタ状態に切り替える必要はありません。 最後の例をそれに応じて変更すると、 以下のようになります:

: [compile-+] ( compilation: --; interpretation: -- )
  \ compiled code: ( n1 n2 -- n )
  POSTPONE + ; immediate

: foo ( n1 n2 -- n )
  [compile-+] ;
1 2 foo .

場合によっては、 複数のワードを POSTPONE する必要があることに気づくでしょう。 このような各ワードの前に POSTPONE を置くのは面倒なので、 Gforth ではより便利な構文 ]] ... [[ を提供しています。 これにより、 [compile-+] を以下のように記述できるようになります:

: [compile-+] ( compilation: --; interpretation: -- )
  ]] + [[ ; immediate
]] ( ) gforth-0.6 “right-bracket-bracket”

postpone 状態に切り替え: すべてのワードと認識器(recognizers)は、 その前に postpone があるかのように処理されます。 [[ が認識されると、 postpone 状態は終了します。

角括弧の珍しい方向はその機能を示しています。 ] が即時実行(インタプリタ状態)からコンパイルに切り替えるのと同じように、 ]] はコンパイルから postpone 状態(つまり、 コンパイル機能のコンパイル)に切り替えます。 逆に、 [[ は postpone 状態からコンパイル状態に切り替えます。 これは、 コンパイル状態から即時実行(インタプリタ状態)に切り替える [ に似ています。

]] ... [[ の本当の利点は、 POSTPONE するワードがたくさんある場合に明らかになります。 たとえば、 ワード compile-map-array (see Advanced macros) は、 以下のようにさらに短く書くことができます:

: compile-map-array ( compilation: xt -- ; run-time: ... addr u -- ... )
\ at run-time, execute xt ( ... x -- ... ) for each element of the
\ array beginning at addr and containing u elements
  {: xt: xt :}
  ]] cells over + swap ?do
    i @ xt 1 cells +loop [[ ;

: sum-array ( addr u -- n )
  0 [ ' + compile-map-array ] ;

see sum-array すると、 以下のコードが表示されます:

: sum-array
  #0 over + swap ?do
    i  + #8 +LOOP
;

]]...[[ に加えて、 この例では他の機能もいくつかお披露目しています:

注意: s\" などのワードのパースは postpone 指定時にはパースされないため、 ]]...[[ 内ではパースされないことに注意してください。 s\" mystring\n" の代わりに、 文字列認識器(string recognizer)を使用して、 ]]...[[ 内で動作する "mystring\n" で記述することができます。これは ]]...[[ 内で機能します。 同様に、 パース・ワード ['] についても、 ` で始まる認識器(recognizer)表記で記述することができます。

ただし、 あなたが s\" を使用したい場合(または、 認識器(recognizer)置換がないパース・ワードがある場合)、 以下のようにコンパイル状態に切り替えることで実行できます:

]] ... [[ s\" mystring\n" ]] 2literal ... [[

標準 Forth での ]] と、 その仲間の定義は、 compat/macros.fs で提供されます。

即時コンパイル・ワード(immediate compiling words)は、 他の言語(特に Lisp)のマクロに似ています。 C言語などのマクロとの重要な違いは以下のとおりです:

マクロで数値をワードにコンパイルすることが必要な場合があります。 これを行うためのワードは literal ですが、 postpone する必要があるため、 literal のコンパイル機能(compilation semantics)はマクロがコンパイルされる時ではなくマクロの実行時に効果を発揮します:

: [compile-5] ( -- ) \ compiled code: ( -- n )
  5 POSTPONE literal ; immediate

: foo [compile-5] ;
foo .

マクロにパラメータを渡して、 マクロを現在の定義にコンパイルする必要がある場合があります。 パラメータが数値の場合は、 postpone literal を使用できます(他の値の場合も同様)。

コンパイルされるワードを渡したい場合、 通常の方法は、実行トークンと compile, を渡すことです:

: twice1 ( xt -- ) \ compiled code: ... -- ...
  dup compile, compile, ;

: 2+ ( n1 -- n2 )
  [ ' 1+ twice1 ] ;
compile, ( xt –  ) core-ext “compile-comma”

xt で表される機能(semantics)を現在の定義に追加します。 結果のコード断片が実行されると、 xtexecute されたのと同一の振る舞いをします。

Gforth で利用可能な代替方法では、 コンパイル機能をパラメータ(compilation semantics)として渡すことができる、 コンパイル・トークンを使用します(see Compilation token)。 以下は、 上記と同じ例にこの方法を使ったものです:

: twice ( ... ct -- ... ) \ compiled code: ... -- ...
  2dup 2>r execute 2r> execute ;

: 2+ ( n1 -- n2 )
  [ comp' 1+ twice ] ;

この例では、 2>r2r> により、 実行(execute)されるコンパイル機能(compilation semantics)がデータ・スタックに影響を与える場合でも、 twice が確実に機能するようにします(訳注: 2dup ( ct ct ) 2>r ( ct r:ct ) execute ( ?? r:ct ) 2r> ( ?? ct ) execute ( ??? ) 。 最初の execute でデータ・スタックのTOSがどうなろうとも、 2つ目の execute のために ct を tos に与えるため。 ct は 2セル単位なので、 2dupで複製、 2>r ... 2r> で ct を1つ退避となる)

これらのワードを使用して完全な定義を定義することもできます。 これは、 does> を使用する代わりの方法を提供します(see User-defined Defining Words)。 たとえば以下の代わりに

: curry+ ( n1 "name" -- )
    CREATE ,
DOES> ( n2 -- n1+n2 )
    @ + ;

以下のように定義することができます

: curry+ ( n1 "name" -- )
  \ name execution: ( n2 -- n1+n2 )
  >r : r> POSTPONE literal POSTPONE + POSTPONE ; ;

-3 curry+ 3-
see 3-

n1 にアクセスするために >r : r> というシーケンスが必要です。 なぜなら、 : はデータ・スタックに colon-sys をプッシュし、 それより下にある全てのモノにアクセスできなくなるためです。

ワードを定義するこの方法は、 does> を使用するよりも便利な場合もあれば、 そうでない場合もあります(see Advanced does> usage example)。 この方式の利点の 1 つは、 コンパイラは、 literal でコンパイルされた値が固定されているのに対して、 create されたワードに関連付けられたデータは変更可能なことを認識しているため、 より適切に最適化できることです。

[compile] ( compilation "name" – ; run-time ? – ?  ) core-ext “bracket-compile”

古いワード(lLegacy word)です。 代わりに postpone を使用してください。 name がデフォルト以外のコンパイル機能(compilation semantics)を持つ場合は、 postpone と同様に機能します。 name がデフォルトのコンパイル機能を持つ(つまり、 通常のワードである)場合、 [compile] name をコンパイルすることは、 name をコンパイルすることと同じです(つまり、 この場合 [compile] は冗長です)。


Footnotes

(23)

最近の RFI の回答では、 ワードのコンパイルはコンパイル状態でのみ実行する必要があるとしているため、 この例はすべての標準システムで動作することは保証されませんが、 ちゃんとした(decent)システムであれば動作します


Previous: Literals, Up: Compiling words   [Contents][Index]