Gforth

このマニュアルは、 標準 Forth 言語の高速で移植可能な実装である Gforth (バージョン 0.7.9_20240418, April 18, 2024)用です。 これはリファレンス・マニュアルとして機能しますが、 Forth の概要と Forth チュートリアルも含まれています。

Authors: Bernd Paysan, Anton Ertl, Gerald Wodni Copyright © 1995, 1996, 1997, 1998, 2000, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014,2015,2016,2017,2018,2019,2020,2021,2022,2023 Free Software Foundation, Inc.

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, with the Front-Cover texts being “A GNU Manual,” and with the Back-Cover Texts as in (a) below. A copy of the license is included in the section entitled “GNU Free Documentation License.”

(a) The FSF’s Back-Cover Text is: “You have freedom to copy and modify this GNU Manual, like GNU software. Copies published by the Free Software Foundation raise funds for GNU development.”

Table of Contents


1 Goals of Gforth

Gforth プロジェクトの目標(goal)は 標準Forth(Standard Forth)のための標準の模範(model)を開発することです。 これは以下のように、 幾つかの副目標に落とし込めます:

上記の目標を達成するには Gforth は以下のようにするべきです

私達はこれらの目標を達成できているでしょうか? Gforth は、 Forth-94 (ANS Forth) と Forth-2012 標準に準拠しています。 内部データ構造の一部 (特にヘッダー) は時間の経過とともに変更されているため、 Gforth は安定しているモデルとは見なさていません。 確かにまだ事実上​​の標準にはなっていませんが、 かなり人気があるようです。 以前のモデルとの類似点と相違点がいくつかあります。 いくつかの強力な機能がありますが、 まだ私たちが想像していたもの全てを備えているわけではありません。 私たちは確かに実行速度の目標を達成しました(see Performance)1。 Gforth は無料で、多くのマシンで利用できます。


1.1 Stability Goals

(Gforthの安定性)Gforth の以前のバージョンで動作するプログラムは、 新しいバージョンでも動作するはずです。 ただし、 以下のようないくつかの注意点があります:

Gforth の内部データ構造 (コンパイル済みコードの表現を含む) は、 文書化されていない限り、 バージョン間で変更される可能性があります。

さらに、 私達は、 標準のワード (つまり、 標準のワードセットにある名前のワード)と、 永続的な Gforth 拡張機能として文書化されたワード(ワードセット名が gforth または gforth-<version> であるワード)(see Notation 参照)は、 保持する義務があると考えています。 他のワードは新しいリリースでは削除される可能性があります。

とりわけ、 あなたは locate を使用するか、 または、 Gforth のソース・コードを調べることでワードを見つけることができ、 スタック効果コメントの直後のコメントでワードセットを確認できます。 そのワードにワードセットがない場合、 それは内部実装であり、 将来のバージョンでは削除される可能性があります。 ワードセットが gforth-experimental または gforth-internal または gforth-obsolete の場合、 そのワードは将来のバージョンで削除される可能性があります。

あなたが永続的としてマークされていない特定のワードを使用したい場合、 私達にご連絡ください。 そのワードを永続的ワードとして追加することを検討します(または、このワードに代わる代替案を提案する場合もあります)。


2 Gforth Environment

メモ: Gforth のマニュアル・ページ(man page)は、 結局のところ、 この章の内容から自動生成されています。

イメージの作成に関する関連情報については Image Files を参照してください。


2.1 Invoking Gforth

Gforth は 2 つの部分から成ります。 実行可能「エンジン」(gforth または gforth-fast という名前)と、イメージ・ファイルです。 Gforth を開始するには、 通常は gforth とするだけです。 これにより、 デフォルトのイメージ・ファイル gforth.fi が自動的にロードされます。 他の多くの場合、 デフォルトの Gforth イメージは以下のように呼び出されます:

gforth [file | -e forth-code] ...

これにより、 ファイルの内容や、 (訳注:コマンドラインに記述された、) Forth コードが、 指定の順序で通訳(interpret)されます。

gforth エンジンに加えて、 gforth-fast というエンジンもあります。 これは高速ですが、 表示されるエラー・メッセージ(see Error messages)の情報が少なく、 そして、 一部のエラーのキャッチ(特に、 スタック・アンダーフローや整数除算エラーなど)が遅れて発生するか、 あるいはまったく発生しません。 デバッグ済みの、 パフォーマンスが重要なプログラムに対して使用するべきです。

さらに、 gforth-itc というエンジンがあり、 下位互換性が必要な状況(see Direct or Indirect Threaded?)で役に立ちます。

一般に、 コマンドラインは以下のようになります:

gforth[-fast] [engine options] [image options]

エンジン・オプションは、 コマンド・ラインの他の部分より前に指定する必要があります。 それらは以下のとおりです:

--image-file file
-i file

デフォルトの gforth.fi の代わりに、 指定の Forth イメージ・ファイルをロードします(see Image Files)。

--appl-image file

指定のイメージ・ファイルをロードし、 これ以降のコマンドライン引数は(エンジンのオプションとして処理するのではなく)、 すべてそのイメージが処理する為に残します。 これは、 gforthmi --application ... で構築された、 Unix 上で実行可能なアプリケーション・イメージを構築する場合に便利です。

--path path
-p path

デフォルトである環境変数 GFORTHPATH 、 またはインストール時に指定されたパス(例: /usr/local/share/gforth/0.2.0:.)と作業ディレクトリ . の代わりに、 指定のパスを使用してイメージ・ファイルと Forth ソース・コード・ファイルを検索します。 パスは ‘:’ で区切られたディレクトリのリストとして指定されます(以前のバージョンには他の OS 用に ‘;’ がありましたが、 Cygwin は現在は /cygdrive/<letter> のみを受け入れ、 かつ、 私達は OS/2 や MS-DOS のサポートは終了してしまったため、 どこでも全部 ‘:’ になりました)。

--dictionary-size size
-m size

イメージで指定されているデフォルト(通常は 256K)を使用する代わりに、 Forth ディクショナリー用のスペースに指定サイズ(size)のスペースを割り当てます。 このオプションと、下記のオプションのサイズ(size)指定は整数と単位で構成されます(例: 4M)。 単位は b (バイト)、 e (elementの略。セル単位)、 k (キロバイト)、 M (メガバイト)、 G (ギガバイト)、 T (テラバイト)のいずれかになります。 単位が指定されていない場合 e が使用されます。

--data-stack-size size
-d size

イメージで指定されているデフォルト(通常は 16K) を使用する代わりに、 データ・スタックに指定のサイズ(size)のスペースを割り当てます。サイズ(size)指定は整数と単位で構成されます(例: 4M)。 単位は b (バイト)、 e (elementの略。セル単位)、 k (キロバイト)、 M (メガバイト)、 G (ギガバイト)、 T (テラバイト)のいずれかになります。 単位が指定されていない場合 e が使用されます。

--return-stack-size size
-r size

イメージで指定されたデフォルト(通常は 15K)を使用する代わりに、 リターン・スタックに指定のサイズ(size)のスペースを割り当てます。 サイズ(size)指定は整数と単位で構成されます(例: 4M)。 単位は b (バイト)、 e (elementの略。セル単位)、 k (キロバイト)、 M (メガバイト)、 G (ギガバイト)、 T (テラバイト)のいずれかになります。 単位が指定されていない場合 e が使用されます。

--fp-stack-size size
-f size

イメージで指定されているデフォルト(通常は 15.5K)を使用する代わりに、 浮動小数点スタックに指定のサイズ(size)のスペースを割り当てます。この場合、単位指定子 e は浮動小数点数を参照します。 サイズ(size)指定は整数と単位で構成されます(例: 4M)。 単位は b (バイト)、 e (elementの略。セル単位)、 k (キロバイト)、 M (メガバイト)、 G (ギガバイト)、 T (テラバイト)のいずれかになります。 単位が指定されていない場合 e が使用されます。

--locals-stack-size size
-l size

イメージで指定されているデフォルト(通常は 14.5K)を使用する代わりに、 ローカル・スタック(locals stack)に指定サイズ(size)のスペースを割り当てます。 サイズ(size)指定は整数と単位で構成されます(例: 4M)。 単位は b (バイト)、 e (elementの略。セル単位)、 k (キロバイト)、 M (メガバイト)、 G (ギガバイト)、 T (テラバイト)のいずれかになります。 単位が指定されていない場合 e が使用されます。

--vm-commit

通常 Gforth は、 ディクショナリーとスタックに十分な仮想メモリー(virtual memory)がない場合でも起動しようとします(OS がサポートしていれば MAP_NORESERVE を使用します)。 したがって、 あなたは非常に大きなディクショナリーやスタックを要求することができ、 利用可能なのを超える仮想メモリーを使用しない限り、 すべて問題ありません(ただし、それを超えて使用すると、 プロセスが強制終了(kill)されます)。 このオプションを使用すると、 OS のデフォルトの割り当てポリシーを使用するようになります。 とりわけ、 オーバーコミットしない OS (Solaris など) の場合、 これは、 大きなディクショナリーやスタックを要求することはできませんし、 要求すべきではないことを意味しますが、 しかし、 Gforth が正常に起動する事に成功したならば、 メモリー不足によって強制終了(kill)させられることはありません。

--help
-h

コマンドライン・オプションに関するメッセージを出力します

--version
-v

バージョンを出力して終了(exit)

--debug

起動時のデバッグに役立ついくつかの情報を出力します。

--offset-image

それ以外の場合に使用される位置とはわずかに異なる位置でディクショナリーを開始します (データ再配置可能イメージ(data-relocatable images)の作成に役立ちます see Data-Relocatable Image Files)。

--no-offset-im

ディクショナリーを通常の位置で開始します。

--clear-dictionary

イメージをロードする前に、 ディクショナリー内の全てのバイトを 0 に初期化します(see Data-Relocatable Image Files)。

--die-on-signal

通常、 Gforth はほとんどのシグナル(例えば、 ユーザー割り込みの SIGINT や、 セグメンテーション違反 SIGSEGV)を Forth の THROW に変換することで処理します。 このオプションを使用すると、 Gforth はそのようなシグナルを受信すると終了(exit)します。 このオプションは、 (最初のシグナルから回復する前に別のシグナルが発生するなど、)エンジンやイメージがひどく壊れている可能性がある場合に役立ちます。 このオプションは、 そのような場合の無限ループを回避します。

--no-dynamic
--dynamic

レプリケーション(replication)を伴う動的スーパー命令(dynamic superinstructions)を無効または有効にします(see Dynamic Superinstructions)。

--no-super

動的スーパー命令(dynamic superinstructions)を無効にし、 動的レプリケーション(dynamic replication)のみを使用します。 これは、 スレッド化コード(threaded code)にパッチを適用する場合に便利です(see Dynamic Superinstructions)。

--ss-number=N

エンジンにコンパイル済みの最初の N 個の静的スーパー命令(static superinstructions)のみを使用します(デフォルトでは全てを使用します。 注意: gforth-fast のみのオプションです)。 このオプションは、 静的スーパー命令(static superinstructions)のパフォーマンスへの影響を測定するのに役立ちます。

--ss-min-codesize
--ss-min-ls
--ss-min-lsu
--ss-min-nexts

指定のメトリックを使用して、 静的スーパー命令の選択(static superinstruction selection)をするためにプリミティブまたは静的スーパー命令のコストを決定します。 Codesize はプリミティブまたは静的スーパー命令のネイティブ・コード・サイズ、 そして、 ls はロードとストアの数、 そして、 lsu はロードとストアと更新の数、 そして、 nexts は(動的スーパー命令を考慮しない)ディスパッチの数です。 ここで、 すべてのプリミティブまたは静的スーパー命令のコストは 1 です。 デフォルトでは、 動的コード生成を使用する場合は codesize、 それ以外の場合は nexts です。

--ss-greedy

このオプションは、 静的スーパー命令のパフォーマンスへの影響を測定するのに役立ちます。 デフォルトでは、 静的スーパー命令の選択には最適な最短パス・アルゴリズム(shortest-path algorithm)が使用されます。 --ss-greedy を使用すると、 そのアルゴリズムは、 現在検討中の静的スーパー命令以降は静的スーパー命令に結合されないと想定するように変更されます。 --ss-min-nexts を使用すると、 その時点で利用可能な最長のスーパー命令を常に選択する貪欲なアルゴリズムと同じ結果が生成されます。 たとえば、スーパー命令 AB と BCD がある場合、 シーケンス A B C D に対して、 最適アルゴリズムは A BCD を選択し、 貪欲アルゴリズムは AB C D を選択します。

--print-metrics

静的スーパー命令の選択中に使用されるいくつかのメトリックを出力します。 code size は、 動的に生成されたコードの実際のサイズです。 Metric codesize は、静的スーパー命令の選択によって確認できるコードサイズ・メトリック(codesize metrics)の合計で、 code size とは異なります。 これは、 すべてのプリミティブと静的スーパー命令が動的に生成されるコードにコンパイルされるわけではないことと、 マーカーがあるためです。 他のメトリクスは ss-min-... オプションに対応します。 このオプションは、--ss-... オプションの効果を評価するのに役立ちます。

上記にて説明したように、 デフォルト・イメージである gforth.fi のイメージ固有のコマンドライン引数は、 一連のファイル名と、 指定した順序で通訳(interpret)される -e forth-code オプションで構成されます。 -e forth-code または --evaluate forth-code オプションは Forth コードを評価(evaluate)します。 このオプションは引数を 1 つだけ取ります。 あなたが、もし、 さらに多くの Forth ワードを評価したい場合は、 ワードを引用符で囲むか、 -e を複数回使用する必要があります。 コマンド・ラインの処理後に(対話モードに入るのではなく、)終了(exit)するには、 コマンド・ラインに -e bye を追加します。 Forth プログラムではコマンド・ライン引数を処理することもできます(see OS command line arguments)。

複数のバージョンの Gforth がインストールされている場合、 gforth は最後にインストールされたバージョンを呼び出します。 gforth-<version> は特定のバージョンを呼び出します。 あなたの環境に環境変数 GFORTHPATH がある場合、 --path オプションを使用してこの環境変数をオーバーライドできます。

起動時、 イメージ・オプションを処理する前に、 環境変数 GFORTH_ENV で指定されたユーザー初期化ファイル、 またはその環境変数が設定されていない場合は(存在する場合、) ~/.config/gforthrc0 がインクルードされます。 GFORTH_ENV が「off」の場合は何もインクルードしません。 すべてのイメージ・オプションを処理した後、 ブート・メッセージを出力する直前に、 オプション --no-rc が指定されていない限り、 ホーム・ディレクトリのユーザー初期化ファイル ~/.config/gforthrc がインクルードされます。

警告レベルは以下のように設定できます

-W

警告(warnings)をオフにする

-Won

警告(warnings)をオンにする(レベル 1)

-Wall

初心者向け警告をオンにする(レベル 2)

-Wpedantic

細かい構文的な警告(pedantic warnings)をオンにする(レベル 3)

-Werror

警告をエラーとして出す(レベル 4)


2.2 Leaving Gforth

bye または (行の先頭で、) Ctrl-d または (--die-on-signal オプションを指定して Gforth を起動した場合、) Ctrl+C を入力すると、 Gforth を終了できます。 Gforth を終了すると、 あなたの定義とデータはすべて破棄されます。 Gforth を終了する前にシステムの状態を保存する方法については、 Image Files を参照してください。

bye () \ gforth を正常に終了する。


2.3 Help on Gforth

Gforth には、 シンプルなテキストベースのオンライン・ヘルプ・システムがあります。

help ( "rest-of-line" –  ) gforth-1.0 “help”

名前が指定されていない場合は、 基礎的なヘルプが表示されます。 ドキュメント・ノード名に ‘::‘ が続く場合、 ノードの最初を表示します。 ワードの名前が指定されている場合、 そのワードのドキュメントが存在する場合はそのドキュメントを、 存在しない場合はソース・コードを表示します。 g を使用して、 help で表示された場所でエディタに入ります。 Help は現在の位置をマークするため、 nb を使用してテキストの詳細を表示したり、 g を使用してエディターでドキュメントにアクセスしたりできます(see Locating source code definitions)。

authors ( ) gforth-1.0 “authors”

著者のリストを表示

license ( ) gforth-0.2 “license”

ライセンス声明を出力します


2.4 Command-line editing

Gforth は、 テキスト・インタープリターに入力したすべての行を記録するヒストリ・ファイルを維持しています。 このファイルはセッションをまたいで保存され、 コマンドラインの再呼び出し機能を提供するために使用されます。 Ctrl-P を繰り返し入力すると、このセッション(または前のセッション)から古いコマンドを連続して呼び出すことができます。 コマンドライン編集機能の完全なリストは以下のとおりです:

  • Ctrl-p (“previous”) (または「↑」上矢印)を使用して、 ヒストリ・バッファーから古い行を順に呼び出します。
  • Ctrl-n (“next”)(または「↓」下矢印)を押すと、 ヒストリ・バッファーからより新しい行を連続して呼び出します。 以前に古い行に移動し、 それをテキスト解釈(text-interpretation)のために Gforth に与えた場合、 最初の編集コマンドとして「nextコマンド」を要求すると、 最後に選択した行の次の行が表示されます。
  • Ctrl-f (または「→」右矢印)を使用して、 非破壊的にカーソルを右に移動します。
  • Ctrl-b (または「←」左矢印)を使用して、 非破壊的にカーソルを左に移動します。
  • Ctrl-h (backspace) カーソルの左側の文字を削除し、 桁を詰めます。
  • Ctrl-k カーソルから行末までを削除 (“kill”) します。
  • Ctrl-a カーソルを行頭に移動します。
  • Ctrl-e カーソルを行末に移動します。
  • RET (Ctrl-m) または LFD (Ctrl-j) 現在行を gforth へ送ります
  • TAB 現在入力中のワードの、 可能なすべての全ワード補完をステップ実行します。
  • Ctrl-d 空行で押すと Gforth を終了(terminate)します。 (bye 使用すると正常に終了します)。
  • Ctrl-x (空行以外では Ctrl-d も使えます) カーソル位置の文字を削除します。

編集中、 表示可能な文字はカーソル位置の左側に挿入されます。 行は常に(「上書きモード」(overstrike)ではなく、)「挿入モード」(insert)です。

Unix システムでは、 ヒストリ・ファイルは デフォルトでは $HOME/.local/share/gforth/historyです2。 以下のコマンドを使用して、 ヒストリ・ファイルの名前と場所を確認できます:

history-file type \ Unix-class systems

history-file type \ Other systems
history-dir  type

あなたが長い定義を手入力した場合、 テキスト・エディターを使用してヒストリ・ファイルから Forth ソース・ファイルに貼り付け、 後で再利用できます。

Gforth はヒストリ・ファイルのサイズを決して削減しないため、 あなたは必要に応じて定期的にヒストリ・ファイルのサイズを削減する必要があります。


2.5 Environment variables

Gforth は以下の環境変数を使用します:

  • GFORTHHIST – (Unix系のみ) ヒストリ・ファイル .gforth-history のパスを指定。 デフォルト: $HOME/.local/share/gforth/history.
  • GFORTHPATH – gforth イメージ・ファイルと Forth ソースコード・ファイル を検索するときに使用するパスを指定(通常 ‘.’ 、現在の作業ディレクトリ)。 パス区切り文字は ‘:’ です。 /usr/local/share/gforth/1.0:. みたいなのが典型です。
  • LANGLC_CTYPE 参照
  • LC_ALLLC_CTYPE 参照
  • LC_CTYPE – Gforth の起動時にこの環境変数が “UTF-8” を含んでいる場合、 Gforth は内部で文字列に UTF-8 エンコーディングを使用し、 UTF-8 エンコーディングでの入力を期待し、 UTF-8 エンコーディングで出力を生成します。 それ以外の場合、 エンコーディングは 8 ビットです(see Xchars and Unicode)。 この環境変数が設定されていない場合、 Gforth は LC_ALL を調べ、 それも設定されていない場合は LANG を調べます。
  • GFORTHSYSTEMPREFIX – C言語 の system() に渡す前に system の引数に何を付加するかを指定します。 デフォルト: Windows では "./$COMSPEC /c "で、他の OS では "" です。 このプレフィックスとコマンドは直接連結されるため、 間にスペースが必要な場合はプレフィックスに追加してください。
  • GFORTHgforthmi によって使用されます(See gforthmi)。
  • GFORTHDgforthmi によって使用されます(See gforthmi)。
  • TMP, TEMP - (Unix系以外) ヒストリ・ファイルの場所として暗黙に使用されます。

すべての Gforth 環境変数は、 設定されていない場合、 デフォルトで適切な値になります。


2.6 Gforth files

Gforth を Unix系にインストールすると、 デフォルトでは以下の場所にファイルがインストールされます:

  • /usr/local/bin/gforth
  • /usr/local/bin/gforthmi
  • /usr/local/man/man1/gforth.1 - man page.
  • /usr/local/info - the Info version of this manual.
  • /usr/local/lib/gforth/<version>/... - Gforth .fi files.
  • /usr/local/share/gforth/<version>/TAGS - Emacs TAGS file.
  • /usr/local/share/gforth/<version>/... - Gforth source files.
  • .../emacs/site-lisp/gforth.el - Emacs gforth mode.

configure のオプションを使用すると、 インストール先に違う場所を選択できます(どんなオプションがあるかは configure --help してください)。


2.7 Gforth in pipes

Gforth は、他の場所で作成されたパイプラインを使用できます(以下で説明します)。 独自にパイプラインを作成することもできます(see Pipes)。

Gforth にパイプライン入力する場合、 プログラムは stdin から read-file または read-line を使用して読み取る必要があります(see General files)。 Key は入力の終わりを認識しません。 accept のようなワードは入力をエコーするため、 通常はパイプラインからの読み取りには役に立ちません。 あなたは、 (Forthのテキスト・インタープリターはパイプライン入力を通訳(interpret)しようとしてしまうため、)Forth内のコマンド・ラインを使用する機会はなく、 OSのコマンド・ライン・オプションを使用して Forth プログラムを呼び出す必要があります。

あなたは typeemitcr などでパイプラインへ出力できます。

もう一方の端で既に閉じられているパイプラインに書き込むと、 Gforth は SIGPIPE シグナル(「パイプが壊れた」シグナル)を受け取ります。 Gforth はこれを例外 broken-pipe-error に変換します。 あなたのアプリケーションがその例外をキャッチしない場合、 システムはその例外をキャッチして、 通常は黙って終了(exit)します(Forth コマンド・ラインで作業している場合を除き、 エラー・メッセージを出力して終了します)。 これは通常、 望ましい動作です。

この振る舞いが気に入らない場合は、 自分で例外をキャッチし、 対応する必要があります。

ここで、 パイプライン内で使用できる Gforth の呼び出しの例を以下に示します:

gforth -e ": foo begin pad dup 10 stdin read-file throw dup while \
 type repeat ; foo bye"

この例では、 入力をそのまま出力にコピーするだけです。 この例を含む非常に単純なパイプラインは以下のようになります:

cat startup.fs |
gforth -e ": foo begin pad dup 80 stdin read-file throw dup while \
 type repeat ; foo bye"|
head

Gforth の stderr 出力に関連するパイプラインは機能しません。


2.8 Startup speed

Gforth を CGI スクリプトやシェルスクリプトで使用する場合、 起動速度が問題になる場合があります。 libc-2.7 を搭載した 64 ビット Linux 2.6.27.8 上の 3GHz Core 2 Duo E8400 では、 gforth-fast -e bye でユーザー時間は 13.1 ミリ秒、システム時間は 1.2 ミリ秒かかります(gforth -e bye は、 下記で説明するオプションの一部を組み込んでいるため、 ユーザー時間は約 3.4 ミリ秒、 システム時間は 1.2 ミリ秒で起動がより高速になります)。

起動速度が問題になる場合は、 以下の改善方法を検討してください。 または、 起動の数を減らす方法(Fast-CGI の使用など)を検討することもできます。 以下の最初の手順では、 (コンパイル時間を含む)実行時間を犠牲にして起動時間を短縮するため、 それで利益が得られるかどうかはあなたのアプリケーションでのこれらの時間のバランスによって決まることに注意してください。

Gforth の起動速度に影響を及ぼす簡単な手順は、 実行時間(run-time)を増加させながらイメージの読み込み時間(image-loading time)を短縮する多数のオプションを使用してみることです。

最初に試す必要があるのは --ss-number=0 --ss-states=1 です。 このオプションは実行時の高速化が比較的わずかなのに、 起動時にかなりの時間がかかるためです。 gforth-fast --ss-number=0 --ss-states=1 -e bye には、 ユーザー時間が約 2.8 ミリ秒、 システム時間が約 1.5 ミリ秒かかります。

次のオプションは --no-dynamic です。 これは実行時間に大きな影響を及ぼします(いくつかのプラットフォームでは約 2 倍です)が、 それでも起動速度が若干速くなります。 gforth-fast -- ss-number=0 --ss-states=1 --no-dynamic -e bye は、 約 2.6 ミリ秒のユーザー時間と 1.2 ミリ秒のシステム時間を消費します。

起動速度を向上させるための次のステップは、 データ再配置可能イメージ(data-relocatable image)を使用することです(see Data-Relocatable Image Files)。 これにより、 イメージ内のコードの再配置コストが回避されます(ただし、 データの再配置コストは回避されません)。 イメージは、 使用している特定のバイナリ(つまり、 gforthgforth-fast、さらには特定のビルド)に固有であることに注意してください。 ./gforth-fast で動作するデータ再配置可能イメージを作成するには、 GFORTHD="./gforth-fast --no-dynamic" gforthmi gforthdr.fi とします(これには --no-dynamic が必要です。そうしないとこのイメージは機能しません)。 そして、 gforth-fast -i gforthdr.fi ... -e bye で実行します(上記で説明したフラグは、 再配置可能なコードでのみ機能するため、ここでは重要ではありません)。 gforth-fast -i gforthdr.fi -e bye では、 ユーザー時間は約 1.1 ミリ秒、 システム時間は約 1.2 ミリ秒かかります。

さらにもう 1 つのステップは、 再配置不可イメージ(non-relocatable image)を使用することで、 すべての再配置コストと書き換え時コピー(copy-on-write;COW)コストの一部を回避することです(see Non-Relocatable Image Files)。ただし、 アドレス空間のランダム化が行われているオペレーティング・システム(最近の Linux 等ではこれがデフォルト)や、 その他の理由でディクショナリーが移動した場合(OS カーネルの変更や、 ライブラリー更新などの場合)には機能しないという欠点があるため、 あまりお勧めできません。再配置不可イメージ(non-relocatable image)を作成するために gforth-fast --no-dynamic -e "savesystem gforthnr.fi bye" とします(ここでも --no-dynamic が必要です)。 そして、 gforth-fast -i gforthnr.fi ... -e bye として実行します(ここでも、 上記で説明したフラグは重要ではありません)。 gforth-fast -i gforthdr.fi -e bye はユーザー時間は約 0.9 ミリ秒、 システム時間は約 0.9 ミリ秒かかります。

実行するスクリプトに大量のコードが含まれている場合は、 起動時のコンパイルのコストを避けるために、 それをイメージにコンパイルすると有益な場合があります。


3 Forth Tutorial

この章と Introduction(see An Introduction to Standard Forth) の違いは、 このチュートリアルの方がハイペースで、 かつ、 あなたがコンピューターをさわれるときに読む必要があり、 かつ、 より多くの内容をカバーしていますが、 しかし、 Forth システムがどのように機能するかについては説明してい無いことです。

このチュートリアルは、 標準に準拠した任意の Forth でご利用できます。 Gforth 固有の機能はすべてその旨の目印が付けられており、 別の Forth を使用する場合はその部分はスキップできます。 このチュートリアルでは、 Forth のすべての機能について説明するわけではありませんが、 あなたが Forth を使い始めて、 Forth で使用できる機能についていくつかのアイデアを得るには十分です。 このチュートリアルが終わったら、 マニュアルの残りの部分をお読みください。

このチュートリアルの使用目的は、 あなたがコンソールの前に座って作業を進め、 例を見てその結果を予測してから、 自分で試してみるというものです。 結果が期待どおりでない場合は、 何が起こっているのかを理解できるように(類似の例を試すなどして)理由を調べます。 いくつか課題も出題してあります。

このチュートリアルでは、 あなたが以前にプログラミングをしたことがあり、 例えば、 ループとは何か、とかを理解していることを前提としています。


3.1 Starting Gforth

あなたが Gforth を開始するには、その名前をタイプします:

gforth

これにより、 対話モードに入ります。 bye と入力すると Gforth を終了できます。 Gforth では、 bash と同様に、 コマンド・ラインを編集し、 カーソル・キーを使用してコマンド・ライン・ヒストリ(履歴)にアクセスできます。


3.2 Syntax

word は、任意の文字のシーケンスです(空白(white space)を除く)。 ワードは空白(white space)で区切られます。 たとえば、 以下の各行には正確にただ 1 つのワードが含まれています:

word
!@#$%^&*()
1234567890
5!a

初心者によくある間違いは、 必要な空白を省略することです。 その結果、 ‘Undefined word’ のようなエラーが発生します。そのため、 このようなエラーが表示された場合は、 必要な場所に空白を入れてあるかどうかを確認してください。

." hello, world" \ correct
."hello, world"  \ gives an "Undefined word" error

Gforth および他のほとんどの Forth システムは、 大文字と小文字の違いを無視します(大文字と小文字は区別されません)。 つまり、 ‘word’ は ‘Word’ と同一です。 あなたのシステムで大文字と小文字が区別される場合は、 ここに示されているすべての例を大文字で入力する必要がある場合があります。


3.3 Crash Course

Forth は、 自分の足を撃つような馬鹿げた事を妨げたりはしません。 以下のように Gforth をクラッシュさせるいくつかの方法を試してみましょう:

0 0 !
here execute
' catch >body 20 erase abort
' (quit1) >body 20 erase

最後の 2 つの例は、Gforth (および他のほとんどのシステム) の重要な部分を破壊することが保証されているため、 (Gforth が自動的に終了していない場合、) この後は Gforth を終了させたほうがよいでしょう。 一部のシステムでは、 外部から gforth を強制終了する必要がある場合があります(例: Unix系 では kill を使用します)。

これらの行が何を行うのか、 なぜクラッシュが発生するのかは後ほど分かります。

クラッシュを発生させる方法がわかったので(そして、 それは大したことがない事がわかったので)、 今度は意味のあるプログラムを作成する方法を学ぶとしましょう。


3.4 Stack

Forth の最も明々白々な機能はスタックです。 数値を入力すると、 その数値がスタックにプッシュされます。 .s を使用してスタックの内容を表示できます。

1 2 .s
3 .s

.s はスタックの最上位(top-of-stack)が一番右になるように表示します。 つまり、 数値は入力時に表記されたとおりに .s 出力に表れます。

. を使用してスタックの最上位要素を出力できます。

1 2 3 . . .

一般に、 ワードはスタック引数を消費します(.s は例外です)。

研究課題(assignment): 5 6 7 . の後、 スタックには何が含まれていますか?


3.5 Arithmetics

+-*/mod というワードは、 常に頂上から見て 2 つのスタック項目に作用します:

2 2 .s
+ .s
.
2 1 - .
7 3 mod .

-/mod のオペランドは、 対応する中置式と同じ順序になります(これが Forth における一般的なケースです)。

ワードの順序によって評価の順序とオペランドが明確に決定されるため、 括弧は不要です(そして、 括弧は使用不可です):

3 4 + 5 * .
3 4 5 * + .

研究課題(assignment): 上記の Forth コードに対応する中置式はどうなるでしょうか? また、 6-7*8+9 を Forth 表記で記述してください3

符号を変更するには、 以下のように negate を使用します:

2 negate .

研究課題(assignment): ‘-(-3)*4-5‘ を Forth に変換してみましょう。

/mod/mod の両方を実行します。

7 3 /mod . .

詳しくはこちらを参照ください: Arithmetic


3.6 Stack Manipulation

スタック操作ワードはスタックのデータを並べ替えます。

1 .s drop .s
1 .s dup .s drop drop .s
1 2 .s over .s drop drop drop
1 2 .s swap .s drop drop
1 2 3 .s rot .s drop drop drop

上記は最も重要なスタック操作ワードです。 以下のように2つペアでスタック項目を操作する亜種もあります:

1 2 3 4 .s 2swap .s 2drop 2drop

さらに 2 つ、 以下のスタック操作ワードがあります:

1 2 .s nip .s drop
1 2 .s tuck .s 2drop drop

研究課題(assignment): niptuck を他のスタック操作ワードの組み合わせに置き換えてみましょう。

以下の結果になるスタック操作を考えてみましょう
Given:          How do you get:
1 2 3           3 2 1
1 2 3           1 2 3 2
1 2 3           1 2 3 3
1 2 3           1 3 3
1 2 3           2 1 3
1 2 3 4         4 3 2 1
1 2 3           1 2 3 1 2 3
1 2 3 4         1 2 3 4 1 2
1 2 3
1 2 3           1 2 3 4
1 2 3           1 3
5 dup * . \ 5^2

研究課題(assignment): 17 を複数回書かずに、 Forth で 17^3 と 17^4 を書いてみましょう。 また、 スタック上の 2 つの数値 (ab、 スタック頂上を b とする) を想定し、(a-b)(a+1) を計算する Forth コードを作成してみましょう。

こちらも参照してください: Stack Manipulation


3.7 Using files for Forth code

Forth コマンド・ラインでの作業は、 1 行の例や短い 1 回限りのコードの場合には便利ですが、 あなたは編集や永続化に便利なように、 ソース・コードをファイルに保存したいと思うかもしれません。 お気に入りのエディター(なお、 Gforth には Emacs サポートがあります see Emacs and Gforth)を使用して file.fs を作成し以下のようにします

s" file.fs" included

こうすると、 file.fs ファイルを Forth システムにロードします。 私達は Forth ファイルの拡張子には ‘.fs’ を使っています。

以下のようにいくつかのファイルをロードして、 Gforth を簡単に開始できます:

gforth file1.fs file2.fs

これらのファイルのロード中にエラーが発生した場合、 Gforth は終了(terminate)しますが、 Gforth 内で INCLUDED 中にエラーが発生すると、 通常は Gforth コマンド・ラインになります。 Forth システムを毎回開始させれば、 あなたの以前の試行の結果に影響されることなく、 毎回クリーンな開始が可能になります。

私達は多くの場合、 すべてのテストをファイルに入れて、 それからそのコードをロードし以下のようにしてテストを実行します

gforth code.fs tests.fs -e bye

(多くの場合、 Emacs内 で C-x C-e を使用してこのコマンドを実行します。) -e bye により、 テスト後に Gforth が確実に終了(terminate)するので、 面倒なくこのコマンドを再度開始できます。

このアプローチの利点は、 プログラムが変更されるたびにテストを簡単に繰り返すことができ、 変更によって生じたバグを簡単に発見できることです。

こちらも参照してください: Forth source files


3.8 Comments

\ これはコメントです。これは行末で終わります
( もう一つのコメント。 これはこのように閉じ丸括弧で終わります: )  .s

\( は通常の Forth ワードであるため、 その後ろのテキストと空白(white space)で区切る必要があります。

\This gives an "Undefined word" error

最初にあらわれる )( で始まるコメントを終了するため、 「( 型のコメント」を入れ子(nest)にすることはできません。 そして、 ( ... ) を使用して ) を含むテキストをコメントアウトすることはできません4

私達は、 説明テキストや、 1 行以上のコードのコメントアウトに \ コメントを使用します。 ( コメントは、 スタック効果やスタックの内容を説明したり、 コードの下位部分をコメントアウトしたりするために使用します。

Emacs モード gforth.el (see Emacs and Gforth) は、 C-x \ でリージョンをコメントアウトし、 C-u C-x \ でリージョンのコメントを解除し、 \ でコメントされた領域を M-q でフィル(fill)します。

こちらも参照してください: Comments


3.9 Colon Definitions

コロン定義(Colon Definitions)は、 他のプログラミング言語のプロシージャや関数に似ています。

: squared ( n -- n^2 )
   dup * ;
5 squared .
7 squared .

: はコロン定義を開始します。 その名前は squared です。 それに続くコメントは、 そのスタック効果について説明しています。 dup * というワードは実行はされませんが、 定義にコンパイルされます(compiled into the definition)。 ; はコロン定義を終了します。

新しく定義されたワードは、 他の定義での使用を含め、 他のワードと同様に使用できます:

: cubed ( n -- n^3 )
   dup squared * ;
-5 cubed .
: fourth-power ( n -- n^4 )
   squared squared ;
3 fourth-power .

研究課題(assignment): niptucknegate/mod のコロン定義を他の Forth ワードで記述し、 それらが機能するかどうかを確認してみましょう (ヒント: 最初にオリジナルであなたの作成したテストコードをテストして結果を確認してから、 その後あなたが定義して、 再度テストして結果を比較しましょう)。 ‘redefine’ メッセージに驚かないでください。 これらは単なる警告です。 (訳注: redefine; 同じ名前のワードを定義したという警告。 置き換えではなく追加となる。 これ以降の通訳(interpret)・コンパイルは新しい方のワードを参照するが、 通常は新しい方のワード定義後も、 既にコンパイル済みのワードは古い方のワードを参照しつづける。 詳しくはディクショナリー等の項目を参照)

こちらも参照してください: Colon Definitions


3.10 Decompilation

see: を使用してコロン定義を逆コンパイルできます:

see squared
see cubed

Gforth では、 see は実行可能コードからソース・コードを再構築したものを示します。 ソースには存在するが、 実行可能コードには存在しない情報(コメントなど)は失われます。

最初から義済みのワードを逆コンパイルすることもできます:

see .
see +

3.11 Stack-Effect Comments

慣例により、 定義名の後のコメントはスタック効果を説明します。 ‘--’ の前の部分は、 定義の実行前のスタックの状態、 つまりコロン定義に渡されるパラメータを説明します。 ‘--’ の後ろの部分は、 定義の実行後のスタックの状態、 つまり定義の実行結果です。 スタック・コメントには、 定義がアクセス、 または、変更する、 (訳注: 定義の外部から見える、 )スタック項目のみを記述します。

たとえ スタック効果が ( -- ) (訳注: スタックに何の効果も及ぼさない)であっても、 あなたは、 すべての定義に正しいスタック効果を記述するべきです。 また、 より複雑なワードには説明的なコメントを追加する必要があります(通常、 これは : の後ろに続けます)。 これを行わないと、 あなたコードの行動は解読不能になります(なぜなら、 とある定義を理解するためには、 全ての定義を辿るハメになるから)。

研究課題(assignment): 例えば swap のスタック効果は、 x1 x2 -- x2 x1 のように記述できます。 同様に、 -dropdupoverrotniptuck のスタック効果を書いてみましょう。 ヒント: 書けたら、 このマニュアルに書いてあるスタック効果と合っているかどうかチェックしましょう(see Word Index)。

プログラマーは、 コロン定義内のさまざまな場所に、 その場所のスタックの内容を説明するコメント(スタック・コメント)を挿入することがあります。 つまり、 スタック効果コメントの最初の部分のようなものです。 例えば以下のようなのです

: cubed ( n -- n^3 )
   dup squared  ( n n^2 ) * ;

この場合、 ワードが十分に単純であるため、 スタック・コメントは割と余計です。 あなたが、 読みやすさを高めるためにそのようなコメントを追加することが良いと思った場合、 ワードをいくつかの単純なワードにファクタリング(因数分解)することも検討する必要があります(see Factoring)。 ファクタリングにより、 通常はスタック・コメントが不要になります。 ただし、 あなたが結果としてリファクタリングしないことに決めた場合は、 そのようなコメントがある方が、 無いよりも良いです。

標準や、 このマニュアルや、 多くのプログラムの、 スタック効果およびスタック・コメント内のスタック項目の名前は、 Fortran やハンガリアン記法と同様に、 型プレフィックスによって型を指定します。 最も頻繁に使用されるプレフィックスは以下のとおりです:

n

符号付き整数

u

符号なし整数

c

文字(character)

f

二値フラグ(Boolean flags)。 つまり false または true

a-addr,a-

セル・アライメント・アドレス

c-addr,c-

文字(char)アライメント・アドレス(注意: Windows NT では文字(char)は 2 バイトになる場合があることに注意)

xt

実行トークン(Execution token)。 セルと同一サイズ

w,x

セル(cell)。 セルには整数またはアドレスを含めることができます。 通常は 32ビットまたは 64 ビットまたは 16 ビットを必要とします(プラットフォームと Forth システムによって異なります)。 cell は一般には machine word として知られていますが、 Forth では word という用語はすでに別の意味を持っています。

d

符号付き2倍長整数(signed double-cell integer)

ud

符号無し2倍長整数(unsigned double-cell integer)

r

浮動小数点数(Float)(FP スタック上に置かれる)

より完全なリストは Notation にあります。

研究課題(assignment): あなたがここまでで記述したすべての定義に対してスタック効果コメントを記述してみましょう。


3.12 Types

Forth では、 演算子の名前はオーバーロードされません。 したがって、 異なる型に対する同様の演算には異なる名前が必要です。 たとえば、 + は整数の加算をしますが、 浮動小数点数を加算するには f+ を使用する必要があります。 以下のプレフィックスは、 さまざまな型の関連する演算によく使用されます:

(none)

符号付き整数

u

符号なし整数

c

文字(character)

d

符号付き2倍長整数(signed double-cell integer)

ud, du

符号無し2倍長整数(unsigned double-cell integer)

2

2 つのセル(必ずしも 2倍長整数(double-cell numbers)とは限りません)

m, um

単一セルとダブル・セルの混合操作

f

浮動小数点(注意: スタック・コメントでは、 ‘f’ はフラグを表し、 ‘r’ は FP 数値を表すことに注意してください。 また、 リテラル FP 数値には指数部分を含める必要があります see Floating Point)。

符号付きのバリエーションと符号なしのバリエーションに違いがない場合(例えば + の場合)、 プレフィックスのないバリエーションのみが存在します。

Forth は、 コンパイル・モード時もインタープリター・モード時も型チェックを実行しません。 以下のように間違った操作を行うと、 データが正しく通訳(interpret)されません:

-1 u.

あなたが、 これまで型チェック言語しか使用したことがなく、 型チェックがいかに重要であるかを聞いたことがある場合でも、 パニックに陥る必要はありません。 著者の経験(および他の Forth 利用者(Forthers)の経験)では、 Forth コードの型エラーは通常(慣れてしまえば)簡単に見つかります。 プログラマの警戒心が高まると、 ほとんどの型エラーに加えて、 より困難なエラーも発見される傾向があります。 型システムを回避する必要がまったくないため、 ほとんどの状況では型チェックがないことが利点であるようです(Forth に型チェックを追加するプロジェクトは普及していません)。


3.13 Factoring

あなたが長ったらしい定義を書こうとすると、 スタックの内容を追跡するのがすぐに困難になることがわかります。 したがって、優れた Forth プログラマは短い定義 (たとえば 3 行)のみを記述する傾向があります。 意味のある短い定義を見つける技術は、 (多項式の因数分解(factoring polynomials)と同様に)ファクタリング(factoring)として知られています。

よくファクタリングされたプログラムには、 追加の利点もあります。 つまり、小さくて一般的なワードは、 大きくて特殊なワードよりもテストとデバッグが容易で、より再利用性に富みます。

したがって、 あなたが、 スタック管理に問題を抱えてるなら、 コードを記述するときにワードに意味のある要素を定義し、 それらの観点からワードを定義するようにしてください。 たった 2 つのワードしかしか含まれないような定義でも、 多くの場合役に立ちます。

上手なファクタリングは簡単ではなく、 コツを掴むにはある程度の練習が必要です。 しかし、経験豊富な Forth プログラマーであっても、 すぐには適切な解決策を見つけられないことが多く、 プログラムの書き直し時に見つかるのです。 したがって、 すぐに良い解決策が思い浮かばなくても、 絶望しないで試し続けてください。(訳注: 参考: 拙訳 Thinking Forth 第6章 ファクタリング https://thinking-forth-ja.readthedocs.io/ja/latest/chapter6.html)


3.14 Designing the stack effect

他の言語では、 関数(function)のパラメーターに任意の順序を使用できます。 また、 結果は 1 つだけなので、 結果の順序を扱う必要もありません。

Forth (および他のスタック・ベースの言語、 たとえば PostScript)では、 定義のパラメーターの順序と結果の順序が重要であり、 適切に設計する必要があります。 一般的なガイドラインは、 ワードの実装が複雑になる場合でも、 ワードがほとんどの場合に簡単に使用できるようにスタック効果を設計することです。 いくつかの具体的なルールは以下のとおりです:

  • ワードがパラメーターの全てを消費する(例: .)。
  • パラメーターの順序に関する慣習がある場合(数学や他のプログラミング言語など)、 それに従ってください(例: -)。
  • 1 つのパラメーターが通常短い計算のみを必要とする場合(たとえば、それが定数である場合)、 それをスタック頂上(top of stack;TOS)に渡します。 逆に、 通常、 計算に長いコード シーケンスが必要なパラメーターは、 スタック底(bottom) (つまり、最初の) パラメーターとして渡す必要があります。 これにより、 リーダーはコードの長いシーケンス(またはスタック操作)を通じてスタック底の項目(bottom item)を追跡する必要がなくなるため、 コードが読みやすくなります。 たとえば、 !(「ストア」 see Memory)は、 通常、 格納されている値よりも計算が簡単であるため、 スタック頂上にアドレスがあるのを期待します(多くの場合、 アドレスは単なる変数です)。
  • 同様に、 通常すぐに消費される結果はスタック頂上(top of stack)に返されるべきですが、 長い計算でよく使用される結果はスタック底(bottom)の結果として渡される必要があります。 たとえば、 open-file のようなファイル・ワードは、スタック頂上にエラー・コードを返します。 これは、 通常、 throw によってすぐに消費されるためです。 さらに言えば、 他の結果に対して何かを行う前に、 エラー・コードをチェックする必要があります。

これらのルールは一般的なガイドラインに過ぎません。 ワードを使いやすくするという全体的な目標を見失わないでください。 例えば、 慣習のルールが計算長のルールと衝突する場合、 ワードがあまり使われない場合は慣習を優先するかもしれません。 一方、 ワードが頻繁に使われる場合は、 計算長のルールを優先するかもしれません(だからといって頻繁に使うと、 計算長のルールを破るコストが非常に高くなりますし、 頻繁に使うことで非慣習的な順序を覚えてしまいがちになります)。


3.15 Local Variables

コロン定義内でローカル変数(local)を定義できます:

: swap { a b -- b a }
  b a ;
1 2 swap .s 2drop

(あなたの Forth システムがこの構文をサポートしていない場合は、 最初に compat/anslocal.fs をインクルードしてください)

この例では、 { a b -- b a } がローカル変数定義です。 スタックから 2 つのセルを取得し、 スタック頂上を b に入れ、 その次のスタック要素を a に入れます(訳注: a b はスタック上から取り除かれる)。 --} で終わるコメントを開始します。 ローカル変数の定義後、 ローカル変数の名前を使用すると、 その値がスタックに積まれます。 コメント部分(-- b a)は省略できます:

: swap ( x1 x2 -- x2 x1 )
  { a b } b a ;

Gforth では、 コロン定義内の任意の場所に複数のローカル変数定義を含めることができます。 対照的に、 標準Forthのプログラムでは、 コロン定義ごとにローカル変数定義を 1 つだけしか持つことができず、 そのローカル変数定義は制御構造の外側にある必要があります。

ローカル変数を使用すると、 スタックの問題に遭遇することなく、 少し長い定義を書くことができます。 ただし、重要な、ファクタリング技能を身につけるための演習として、 ローカル変数を使用せずにコロン定義を記述してみることをお勧めします。

研究課題(assignment): ここまでのあなたの定義をローカル変数を使って書き換えてみましょう。

こちらも参照下さい: Locals


3.16 Conditional execution

Forth では、 コロン定義内でのみ制御構造を使用できます。 if 構造は以下のようになります:

: abs ( n1 -- +n2 )
    dup 0 < if
        negate
    endif ;
5 abs .
-5 abs .

if はスタックからフラグを取得します。 フラグがゼロ以外 (true) の場合は、 その次のコードが実行されます。 そうでない場合は、 endif (または else) の後から実行が続けられます。 < は、 頂上から 2 つのスタック要素を比較し、 フラグを生成します。

1 2 < .
2 1 < .
1 1 < .

実は、 endif の標準Forthでの名前は then です。 このチュートリアルでは、 endif を使用した例を示します。 なぜなら、 then が異なる意味を持つ他のプログラミング言語に慣れている人々にとっては、 endif を使用する方が混乱が少ないからです。 システムに endif がない場合は、 以下のように定義します

: endif postpone then ; immediate

あなたはオプションで else 部分を使用できます:

: min ( n1 n2 -- n )
  2dup < if
    drop
  else
    nip
  endif ;
2 3 min .
3 2 min .

研究課題(assignment): else 部分を付けずに min を記述してみましょう。 (ヒント: nip の定義は何ですか?)。

こちらも参照下さい: Selection


3.17 Flags and Comparisons

false フラグはすべてのビットがクリアです(整数として通訳(interpret)される場合は 0)。 正規化された true フラグは、 すべてのビットがセットされています(2の補数の符号付き整数として -1)。 多くの文脈(例: if)では、ゼロ以外の値はすべて true フラグとして扱われます。

false .
true .
true hex u. decimal

比較ワードは正規化フラグを生成します:

1 1 = .
1 0= .
0 1 < .
0 0 < .
-1 1 u< . \ 型エラー。 u< は -1 を 大きな符号なし数として扱ってしまいます
-1 1 < .

Gforth は、接頭辞 0 u d d0 du f f0 (または「 接頭辞なし」)との比較 = <> < > <= >= のすべての組み合わせをサポートします。 これらの組み合わせの一部のみが標準Forthです (詳細については、標準Forth または Numeric comparison または Floating Point または Word Index を参照してください)。

and or xor invert を正規化フラグ用の演算子として使用できます。 実際には、 これらはビット単位演算です:

1 2 and .
1 2 or .
1 3 xor .
1 invert .

0<> を使用して ゼロ/非ゼロ フラグを正規化フラグに変換できます(そして、 その途中で 0= を使用して ゼロ/非ゼロ フラグの余数(complement)を取ります。 実際、 正規化フラグでは invert の代わりに 0= を使用するのが一般的です)。

1 0= .
1 0<> .

0<> 無しでも if で ゼロ/非ゼロ をテストすることはできますが、 ゼロ/非ゼロ 値を and or xor と組み合わせる場合、 and or xor はビット単位での操作なので、 0<> を使用する必要がある場合があります。 最も単純で、 エラーが少なく、 おそらく最も明確な方法は、 これらすべての場合に 0<> を使用することですが、 場合によっては、 使用する 0<> の数を減らすこともできます。 以下にいくつかのスタック効果を示します。 fc は正規化フラグ(canonical flag)を表し、 fz は ゼロ/非ゼロ を表します(すべての fcfz としても機能します):

or  ( fz1 fz2 -- fz3 )
and ( fz1 fc  -- fz2 )
and ( fc  fz1 -- fz2 )

したがって、 以下のようなコードの場合:

( n1 n2 ) 0<> and if

これは、 n1 and n2 (n1 かつ n2)がゼロ以外であるかどうかをテストし、 イエスの場合は if の後のコードを実行します。 n1 を ゼロ/非ゼロ として扱い、 0<> を使用して n2 を正規化フラグに変換します。 and は fz を生成し、 それは if によって消費されます。

正規化フラグの全ビットセット機能とブール演算のビット単位の演算を使用して、 if を回避することもできます:

: foo ( n1 -- n2 )
  0= if
    14
  else
    0
  endif ;
0 foo .
1 foo .

: foo ( n1 -- n2 )
  0= 14 and ;
0 foo .
1 foo .

研究課題(assignment): minif 無しで書いてみましょう。

こちらも参照下さい: Boolean Flags, Numeric comparison, Bitwise operations


3.18 General Loops

無限ループは非常に単純です:

: endless ( -- )
  0 begin
    dup . 1+
  again ;
endless

(Gforth の場合、) Ctrl-C を押してこのループを終了します。 begin は実行時に何も行わず、 againbegin にジャンプします。

任意の場所に 1 つの出口があるループは以下のようになります:

: log2 ( +n1 -- n2 )
\ logarithmus dualis of n1>0, rounded down to the next integer
  assert( dup 0> )
  2/ 0 begin
    over 0> while
      1+ swap 2/ swap
  repeat
  nip ;
7 log2 .
8 log2 .

実行時には while はフラグを1つ消費します。 フラグが 0 の場合、 repeat の後ろへ飛んで実行が継続されます。 フラグがゼロ以外の場合、 実行は while の後ろから継続されます。 Repeat は、 again と全く同じように、 begin に戻ります。

Forth には、 1+ など、多数の 組み合わせ/省略形 があります。 しかし、 2/ は 組み合わせ/省略形 ではありません。 これは引数を 1 ビット右にシフトし、 Gforth の (Gforth 0.7 以降の) / と同様、 常に負の無限大方向に向かって小数点以下を丸める除算(フロア除算)と見なされますが、 他の多くの Forth システムの / とは異なります。

-5 2 / . \ -2 or -3
-5 2/ .  \ -3

assert( は標準Forthのワードではありませんが、 Gforth 以外のシステムでも compat/assert.fs を含めることで取得できます。 それが何をするかは、 以下のように試してみることで確認できます。

0 log2 .

以下は、 最後に出口があるループです:

: log2 ( +n1 -- n2 )
\ logarithmus dualis of n1>0, rounded down to the next integer
  assert( dup 0 > )
  -1 begin
    1+ swap 2/ swap
    over 0 <=
  until
  nip ;

Until はフラグを消費します。 フラグがゼロの場合は begin から実行が継続され、 それ以外の場合は until の後から実行が継続されます。

研究課題(assignment): 最大公約数を計算する定義を書いてみましょう。

こちらも参照ください: Simple Loops


3.19 Counted loops

: ^ ( n1 u -- n )
\ n = the uth power of n1
  1 swap 0 u+do
    over *
  loop
  nip ;
3 2 ^ .
4 3 ^ .

U+do (あなたの Forth システムにない場合は compat/loops.fs をインクルードしてください)は、 ( u3 u4 -- ) 、 つまり、 スタックから 2 つの数値を取得し、 u+doloop の間のコードを u3 - u4 回実行します( u3 - u4 < 0 の場合はまったく実行しません)。

ループ開始ワードのスタック効果を見れば、スタック効果の設計ルールが機能していることがわかります。 ループの開始値は終了値と比べて定数であることが多いため、 開始値はスタック頂上(top-of-stack)に渡されます。

i を使用して、 ループ・カウンターにアクセスできます:

: fac ( u -- u! )
  1 swap 1+ 1 u+do
    i *
  loop ;
5 fac .
7 fac .

+do もあります。 これは符号付きの数値を期待します(それはループに入るかどうかを決定するために重要です)。

研究課題(assignment): n 番目のフィボナッチ数を計算するための定義を記述してみましょう。

増分として 1 以外も使用できます:

: up2 ( n1 n2 -- )
  +do
    i .
  2 +loop ;
10 0 up2

: down2 ( n1 n2 -- )
  -do
    i .
  2 -loop ;
0 10 down2

こちらも参照ください: Counted Loops


3.20 Recursion

通常、 定義の名前はその定義内に表れません。ただし、 それ以前の(同名の)定義は普通に表れます(訳注: 下記例は再帰呼出しではなくて、 ’/’ の古いバージョンを呼び出しているに過ぎない事に注意。同じ名前の古いバージョンが存在しなければエラーになるが、存在すればエラーにならないので注意。):

1 0 / . \ "Floating-point unidentified fault" in Gforth on some platforms
: / ( n1 n2 -- n )
  dup 0= if
    -10 throw \ report division by zero
  endif
  /           \ old version
;
1 0 /

再帰定義の場合は、 recursive (非標準) または recurse を使用できます:

: fac1 ( n -- n! ) recursive
 dup 0> if
   dup 1- fac1 *
 else
   drop 1
 endif ;
7 fac1 .

: fac2 ( n -- n! )
 dup 0> if
   dup 1- recurse *
 else
   drop 1
 endif ;
8 fac2 .

研究課題(assignment): n 番目のフィボナッチ数を計算するための再帰定義を記述してみましょう。

こちらも参照ください (間接再帰に関してもコチラ): See Calls and returns


3.21 Leaving definitions or loops

EXIT は、 現在の定義をすぐに終了(exit)します。 EXIT の実行前に、 (残ったループ・カウンタ達を全て取り除くために、)ネストしたカウンタ付きループの数だけ UNLOOP を実行する必要があります(訳注: 下記例は1重のループなので unloop が1つ。 2重ループになったら unloop unloop と2つ必要):

: ...
 ... u+do
   ... if
     ... unloop exit
   endif
   ...
 loop
 ... ;

LEAVE は、 (LEAVE から見て)最も内側にあるカウンタ付きループを直ちに終了します:

: ...
 ... u+do
   ... if
     ... leave
   endif
   ...
 loop
 ... ;

こちらも参照ください: Calls and returns, Counted Loops


3.22 Return Stack

データ・スタックに加えて、 Forth には 2 番目のスタックであるリターン・スタックもあります。 ほとんどの Forth システムは、プロシージャ呼び出しの戻りアドレスをそこに保存します(したがって、 リターン・スタックという名前が付けられています)。 プログラマもリターン・スタックを利用することができます。

: foo ( n1 n2 -- )
 .s
 >r .s
 r@ .
 >r .s
 r@ .
 r> .
 r@ .
 r> . ;
1 2 foo

>r はデータ・スタックから1つの要素を取得し、 それをリターン・スタックにプッシュします。 逆に、 r> は1つの要素をリターンからデータ・スタックに移動します。 r@ は、リターン・スタック頂上のコピーをデータ・スタックにプッシュします。

Forth プログラマーは通常、 データ・スタックのみを使用すると複雑すぎて、 かつ、 ファクタリングやローカル変数が選択肢に無い場合、 データを一時的に保存するためにリターン・スタックを使用します。

: 2swap ( x1 x2 x3 x4 -- x3 x4 x1 x2 )
 rot >r rot r> ;

定義のリターン・アドレスとカウント付きループのループ制御パラメーターは通常、 リターン・スタック上に存在するため、 コロン定義またはカウント付きループでリターン スタックにプッシュしたすべての項目を、定義の終了やループの終了の前にリターン・スタックから取得する必要があります。 ある定義の外側やループの外側でリターン・スタックにプッシュしたアイテムに、 ループの定義内からアクセスすることはできません。

リターン・スタック項目の数を間違えると、 通常はクラッシュします:

: crash ( n -- )
  >r ;
5 crash

ローカル変数の使用とリターン・スタックの使用を混在させることはできません(標準Forthの場合。 Gforth では問題ありません)。 ただし、 これらは同一の問題の解決なので、 問題にはなりません。

研究課題(assignment): リターン・スタックを使用して、 ここまでにあなたが書いた定義をより良い方法で書き直すことができるでしょうか?

こちらも参照ください: Return stack


3.23 Memory

以下使用してグローバル変数 v を作成できます:

variable v ( -- addr )

v は、スタックにメモリー内の、とあるセルのアドレスをプッシュします。 このセルは variable によって予約されています。 !(ストア)を使用してスタックからこのセルに値を保存し、 @(フェッチ)を使用して値をメモリーからスタックにロードできます:

v .
5 v ! .s
v @ .

dump を使用すると、 メモリーの生のダンプ(メモリー・ダンプ)を確認できます:

v 1 cells .s dump

Cells ( n1 -- n2 ) は、 n1 個のセル が占めるバイト数(より一般的にはアドレス単位(address units)(aus))を与えます。 そして、 あなたは、 さらに多くのメモリーを予約することもできます:

create v2 20 cells allot
v2 20 cells dump

変数のようなワード v2 を作成し、 20 個の初期化されていないセルを予約します。 v2 によってプッシュされたアドレスは、 これら 20 個のセルの先頭を指します(see CREATE)。 あなたは、 アドレス演算を使用して、 これらのセルにアクセスできます:

3 v2 5 cells + !
v2 20 cells dump

, を使用してメモリーを予約 かつ 初期化できます:

create v3
  5 , 4 , 3 , 2 , 1 ,
v3 @ .
v3 cell+ @ .
v3 2 cells + @ .
v3 5 cells dump

研究課題(assignment): 最初のセルは addr 、 次のセルは addr cell+ などとして、 u 個のセルの値の合計を計算する定義 vsum ( addr u -- n ) を記述してみましょう。

variablecreate の違いは、variable がセルを割り当てる(allots)ことと、 標準 Forth では変数(variable)に追加のメモリーを割り当てることができないことです。

新しいワードを作成せずにメモリーを予約することもできます:

here 10 cells allot .
here .

最初の here は(最初の here 時点の)ディクショナリー領域の後のアドレスをプッシュし、 メモリー領域の開始アドレスとして使い、 2 番目の here は(2 番目の here 時点の)ディクショナリー領域の後のアドレスをプッシュします。 この開始アドレスはどこかに保存する必要があります。 そうしないと、 あなたはこのメモリー領域を再度見つけるのが困難になります。

Allot はディクショナリー・メモリーを管理します。 ディクショナリー・メモリーには、 Gforth や他のほとんどの Forth システムの、 ワードなどのシステムのデータ構造が含まれています。 これはスタックのように管理されます。 あなたは以下のようにして割り当て(allot)したメモリーを解放できます:

-10 cells allot
here .

注意: その合間に新しいワードを作成した場合、 それを実行できないことに注意してください(allot で作成されたメモリーが、 もはやディクショナリー「スタック」の頂上ではなくなるため)。

その代わりに、 allocatefree を使用すると、 任意の順序でメモリを解放できます:

10 cells allocate throw .s
20 cells allocate throw .s
swap
free throw
free throw

throw はエラー(メモリー不足など)を処理します。

また、 ガベージ・コレクター なら、 メモリーを明示的に解放(free)する必要がなくなります。

こちらも参照ください: Memory


3.24 Characters and Strings

スタック上では、 数と同様に文字がセルを占めます。 メモリー内では文字は独自のサイズ(ほとんどのシステムでは 8 ビットのバイト値)であるため、 メモリー・アクセスには文字独自のワードが必要です:

create v4 
  104 c, 97 c, 108 c, 108 c, 111 c,
v4 4 chars + c@ .
v4 5 chars dump

スタック上の文字列の推奨される表現は addr u-count です。ここで、addr は文字列の最初の文字のアドレスで、 u-count は文字列の文字数です。

v4 5 type

以下を使用すると文字列定数を取得できます

s" hello, world" .s
type

s" と文字列の間にスペースがあることを確かめてください。 s" は通常の Forth ワードであり、 空白(white space)で区切る必要があります(スペースを削除するとどうなるかを試してみましょう)。

ただし、 この s" のインタープリターでの使用(interpretive use)は非常に制限されています。 文字列は、 s" が次に呼び出されるまでの間だけ存在します(一部の Forth システムはこれらの文字列を複数保持しますが、 普通は未だ限られた寿命です)。

s" hello," s" world" .s
type
type

あなたは定義内で s" を使用することもでき、 (定義が続く限り、)その結果の文字列は永久に存続します:

: foo s" hello," s" world" ;
foo .s
type
type

研究課題(assignment): Emit ( c -- )c を(数値ではなく)文字として出力します。 これを使って type ( addr u -- ) を実装してみましょう。

こちらも参照ください: Memory Blocks


3.25 Alignment

多くのプロセッサでは、 @! を使用してセルにアクセスする場合、 メモリー内でセ​​ルをアライメントする必要があります (プロセッサがアライメントを必要としない場合でも、 アライメントされたセルへのアクセスは高速です)。

Createhere(つまり、 次の割り当てが行われる場所、 そしてcreateされたワードが指す場所)でアライメントします。 同様に、 allocateによって生成されたメモリーはアライメントしたアドレスから始まります。 アライメントしたアドレスに cells の数値 を足すと、 次のアライメントされたアドレスが生成されます。

ただし、 char+ および chars を含むアドレス演算では、セルにアライメントしていないアドレスが作成される可能性があります。 Aligned ( addr -- a-addr ) は、 その次のアライメントせされたアドレスを生成します:

v3 char+ aligned .s @ .
v3 char+ .s @ .

同様に、 alignhere を次のアライメントされたアドレスに進めます:

create v5 97 c,
here .
align here .
1000 ,

注意: プログラムを移植可能にしたい場合は、 プロセッサがそれらを必要としない場合でも、 アライメントれたアドレスを使用する必要があることに注意してください。

こちらも参照ください: Address arithmetic


3.26 Floating Point

Forth の浮動小数点(floating-point)(FP)の、 数値と算術演算は、 ほぼ期待された通りに機能しますが、 特筆すべき点がいくつかあります:

最初の点は Forth に固有のものではありませんが、 非常に重要で、 まだ広く知られていないため、 ここで言及します。 浮動小数点数は実数(real)ではありません。 実数(real)が持ち、 あらゆる種類の数値に期待される多くの性質(算術法則など)は、 浮動小数点数には当てはまりません。 浮動小数点演算したい場合、 浮動小数点演算の問題とその回避方法について学ぶ必要があります。 良い出発点は David Goldberg, What Every Computer Scientist Should Know About Floating-Point Arithmetic, ACM Computing Surveys 23(1):5−48, March 1991 です(訳注: https://docs.oracle.com/cd/E19957-01/806-4847/ncg_goldberg.html これが合ってるかどうか不明。一部文字化けあり2024/06現在)。

Forth ソース・コードでは、リテラル浮動小数点数には指数が必要です(例: 1e0)。これは、 1e のように短く書くことも、 +1.0e+0 のように長く書くこともでき、 その間にはさまざまなバリエーションがあります。 その理由は、 歴史的な理由により、 Forth は小数点のみ(例: 1.) の数を 2 セル整数を示すものとして通訳(interpret)するためです。 例:

2e 2e f+ f.

リテラル浮動小数点数のもう 1 つの要件は、 現在の基数が 10 進数であることです。 16 進数の 1e は整数として通訳(interpret)されます:

Forth には、Forth-2012 に準拠した浮動小数点数用の別個のスタックがあります。 このモデルの利点の 1 つは、 浮動小数点数にアクセスするときにセルが邪魔にならないこと、 またその逆も同様であることです。 Forth には、 浮動小数点スタック(FP スタック)を操作するためのワードのセットがあります: fdup fswap fdrop fover frot や (非標準の、) fnip ftuck fpick

FP 算術ワードには F という接頭辞が付きます。 通常の f+ f- f* f/ f** fnegate のほか、 他の、 関数用の多数のワード (例: fsqrt fsin fln fmin) があります。 期待されるワードの 1 つが f= ですが、 f= は標準にはありません。 浮動小数点数の計算結果は通常不正確であるため、 正確な比較は通常間違いであり、 近似的な比較を使用する必要があります。 残念ながら、この目的のための標準のワードである f~ は適切に設計されていないため、 Gforth では f~absf~rel も提供しています。

そしてもちろん、 メモリー内の浮動小数点数にアクセスするためのワード(f@ f!)や、 アドレス演算用のワード(floats float+ faligned)もあります。 メモリ内の IEEE 書式の単精度および倍精度数にアクセスするために、 sf および df プレフィックスを付けた、 これらのワードのバリエーションもあります。 その主な目的は、 外部浮動小数点数データ(ファイルから読み取られた、 またはファイルに書き込まれるデータなど)にアクセスすることです。

以下は、 ドット出力ワード(dot-product word)とその使用例です:

: v* ( f_addr1 nstride1 f_addr2 nstride2 ucount -- r )
  >r swap 2swap swap 0e r> 0 ?DO
    dup f@ over + 2swap dup f@ f* f+ over + 2swap
  LOOP
  2drop 2drop ;

create v 1.23e f, 4.56e f, 7.89e f,

v 1 floats  v 1 floats  3  v* f.

研究課題(assignment): 二次方程式を解くプログラムを作成してみましょう。 次に、 Henry G. Baker, You Could Learn a Lot from a Quadratic, ACM SIGPLAN Notices, 33(1):30−39, January 1998 を読んで、 そのプログラムを改善できるかどうか確認してください。 最後に、 元のバージョンと改良されたバージョンで異なる結果が生成されるテスト・ケースを探しましょう。

こちらも参照ください: Floating Point; Floating point stack; Number Conversion; Memory Access; Address arithmetic.


3.27 Files

このセクションでは、 Forth 内でファイルを使用する方法について簡単に説明します。 それは 5 つの簡単なステップに分かれています:

  1. 入力用に ASCII テキスト・ファイルを開きます
  2. 出力用にファイルを開く
  3. 文字列が一致する (または他の条件が満たされる) まで入力ファイルを読み取ります
  4. 入力 (が変更されたかどうかに関係なく、) から数行を出力に書き込みます
  5. ファイルを閉じます。

こちらも参照ください: General files

3.27.1 Open file for input

s" foo.in"  r/o open-file throw Value fd-in

3.27.2 Create file for output

s" foo.out" w/o create-file throw Value fd-out

使用可能なファイル・モードは、 読み取り専用アクセスの場合は r/o 、 読み取り/書き込み アクセスの場合は r/w 、書き込み専用アクセスの場合は w/o です。 必要に応じて、 読み取りと書き込みの両方のファイルを r/w で開くこともできます。 すべてのファイル用ワードはエラーコードを返します。 ほとんどのアプリケーションでは、 throw を使用してエラー・コードを外部のエラー・ハンドラーに渡すのが最善(best)です。

開いたり(opening)割り当てたり(assigning)するためのワードが必要な場合は、 以下のように定義します:

0 Value fd-in
0 Value fd-out
: open-input ( addr u -- )  r/o open-file throw to fd-in ;
: open-output ( addr u -- )  w/o create-file throw to fd-out ;

使用例:

s" foo.in" open-input
s" foo.out" open-output

3.27.3 ファイルの特定の行をスキャン

256 Constant max-line
Create line-buffer  max-line 2 + allot

: scan-file ( addr u -- )
  begin
      line-buffer max-line fd-in read-line throw
  while
         >r 2dup line-buffer r> compare 0=
     until
  else
     drop
  then
  2drop ;

read-line ( addr u1 fd -- u2 flag ior ) は、 addr からのバッファーに最大 u1 バイトを読み取り、 読み取ったバイト数と、 ファイルの終わりに達した場合に false になるフラグと、 エラーコードを返します。

compare ( addr1 u1 addr2 u2 -- n ) は 2 つの文字列を比較し、 両方の文字列が等しい場合は 0 を返します。 最初の文字列の方が字句的に大きい(lexically greater)場合は正の数値を返し、 2 番目の文字列の方が字句的に大きい(lexically greater)場合は負の数値を返します。

このループは、 まだ見たことがないですよね。 このループは出口が2つあります。 while はスタック上にある読み取ったバイト数で終了するため、 それを個別にクリーンアップする必要があります。 そのクリーンアップする部分は else の後にあります。

使用例:

s" The text I search is here" scan-file

3.27.4 Copy input to output

: copy-file ( -- )
  begin
      line-buffer max-line fd-in read-line throw
  while
      line-buffer swap fd-out write-line throw
  repeat 
  drop ;

3.27.5 Close files

fd-in close-file throw
fd-out close-file throw

これも、同様に、 定義に組み込むことができます:

: close-input ( -- )  fd-in close-file throw ;
: close-output ( -- )  fd-out close-file throw ;

研究課題(assignment): copy-file を変更して、 2 行目が一致するまでコピーするようにするにはどうすればよいでしょうか? セクションの開始行と終了行を指定して、 テキスト・ファイルのセクションを抽出するプログラムを作成できますか?


3.28 Interpretation and Compilation Semantics and Immediacy

ワードがコンパイルされる時と、 通訳(interpret)される時では異なる振る舞いをします。 たとえば、 + について考えてみましょう:

1 2 + .
: foo + ;

これらの 2 つの振る舞いは、 コンパイル機能(compilation semantics)とインタプリタ機能(interpretation semantics)として知られています。 通常のワード(例: +)の場合、 コンパイル機能は、 現在定義中のワード(上記の例では foo)にインタープリター機能を追加します。 つまり、 後で foo が実行されると、 + のインタープリター機能(interpretation semantics)(つまり、2 つの数値の加算)が実行されます。

ただし、 if のような制御フロー・ワードなど、 デフォルト以外のコンパイル機能を持つワードが存在します。 immediate を使用すると、 最後に定義されたワードのコンパイル機能をインタープリター機能と等しくなるように変更できます:

: [FOO] ( -- )
 5 . ; immediate

[FOO]
: bar ( -- )
  [FOO] ;
bar
see bar

デフォルト以外のコンパイル機能をもつワードだと知らしめる 2 つの慣習は、 名前を括弧で囲む(より頻繁に使用される)ことと、 名前をすべて大文字で記述する(あまり使用されない)ことです。

if などの一部のワードについては、 インタープリター機能を使用するのは通常間違いであるため、 それらを compile-only としてマークし、 インタープリター機能を使用すると警告が表示されます。

: flip ( -- )
 6 . ; compile-only \ but not immediate
flip

: flop ( -- )
 flip ;
flop

この例では、 最初に flip のインタープリター機能を使用します(警告が表示されます)。 flip の 2 番目の使用では、 コンパイル機能を使用します(警告は表示されません)。 この例では、 compile-only が実行時(run-time)ではなくテキスト・インタープリター時に評価される属性であることもわかります。

テキスト・インタープリターには 2 つの状態があります。 インタープリター・モード(interpret)は、 遭遇したワードのインタープリター機能(interpretation semantics)を実行します。 コンパイル・モードでは、 これらのワードのコンパイル機能(compilation semantics)が実行されます。

特に、 : はコンパイル状態に切り替え、 ; はインタープリター状態に戻します。 これらには、 状態を切り替えるだけの効果である ] (コンパイル状態に切り替える) と [ (インタープリター状態に切り替える) が含まれています。

: xxx ( -- )
  [ 5 . ]
;

xxx
see xxx

これらの角括弧(brackets)は、 上記の命名慣習の源でもあります。

こちらも参照ください: Interpretation and Compilation Semantics


3.29 Execution Tokens

' word は、 ワードの実行トークン(execution token)(XT)を提供します。 XT は、 ワードのインタープリター機能(interpretation semantics)表すセルです。 これは execute で実行できます:

' + .s
1 2 rot execute .

XT は C の関数ポインターに似ています。 ただし、 パラメーターがスタックで渡されるため、もう少し柔軟になります:

: map-array ( ... addr u xt -- ... )
\ addr で始まり u 個の要素を含む配列のすべての要素に対して
\ xt ( ... x -- ... ) を実行します
  { xt }
  cells over + swap ?do
    i @ xt execute
  1 cells +loop ;

create a 3 , 4 , 2 , -1 , 4 ,
a 5 ' . map-array .s
0 a 5 ' + map-array .

s" max-n" environment? drop .s \ 下記比較初期値用に整数最大値を得る
a 5 ' min map-array . \ 最初の要素は、 environment? で取得した整数最大値と比較

生成するより消費する要素が 1 つ多いワードの XT に対して map-array を使用できます。 理論的には、他の XT でも使用できますが、 スタック効果は配列のサイズに依存するため、理解するのが困難です。

XT はセルサイズであるため、 メモリーに保存し、 他のセルと同様にスタック上で操作できます。 compile, を使用して XT をワードにコンパイルすることもできます:

: foo1 ( n1 n2 -- n )
   [ ' + compile, ] ;
see foo1

compile, は標準ではコンパイル機能(compilation semantics)がないため、 上記は標準ではないけれども、 良い Forth システムでは動作します。 うまくいかなかったモノついては、 以下を使用してください

: [compile,] compile, ; immediate

: foo1 ( n1 n2 -- n )
   [ ' + ] [compile,] ;
see foo1

' は、 デフォルトでコンパイル機能(compilation semantics)を持つワードです。 そのワードのインタープリター機能(interpretation semantics)を実行すると、 その次のワードを構文解析(parse)します。

: foo ( -- xt )
  ' ;
see foo
: bar ( ... "word" -- ... )
  ' execute ;
see bar
1 2 bar + .

コンパイル中にワードを解析(parse)し、 その XT をコンパイルして、 実行時にスタックにプッシュされるようにしたいことがよくあります。 ['] はこれを行います:

: xt-+ ( -- xt )
  ['] + ;
see xt-+
1 2 xt-+ execute .

多くのプログラマーは、 ' とそれが解析(parse)するワードを 1 つの単位として認識し、 コンパイル時に ['] のように動作することを期待し、 実際の動作に混乱する傾向があります。 あなたがもしそうなら、 Forth システムは ' を 1 つの単位として捉えているだけであり、 それが解析(parse)ワードであるとはまったく考えていないことを覚えておいてください(この問題でプログラマーの便宜を図る試みは、 通常、 さらにひどい落とし穴につながります。 State-smartness—Why it is evil and How to Exorcise it)。

XT の作成および実行は、 インタープリターの状態には影響を受けないことに注意してください。 つまり、 コンパイル状態で ' を実行した場合でも、 インタープリター機能(interpretation semantics)が得られます。 そして、 そこでの状態が何であれ、 execute は XT によって表されるコード(つまり、' で生成された XT の場合はインタープリター機能(interpretation semantics))を実行します。

こちらも参照ください: Tokens for Words


3.30 Exceptions

throw ( n -- ) は、 n がゼロでない限り例外を引き起こします。

100 throw .s
0 throw .s

catch ( ... xt -- ... n )execute と同様に動作しますが、 例外をキャッチし、 スタック上に例外の数値(または XT が例外なしで実行された場合は 0)をプッシュします。 例外があった場合、 スタックの深さは catch の実行直前と同一です。

.s
3 0 ' / catch .s
3 2 ' / catch .s

研究課題(assignment): catch の代わりに execute を使用して同じことを試してみましょう。

throw は、常に動的に直近にこの throw を囲んでいる(定義の) catch にジャンプします、 たとえそのジャンプを達成するために複数の呼び出しレベルを飛び越す必要がある場合でもです:

: foo 100 throw ;
: foo1 foo ." after foo" ;
: bar ['] foo1 catch ;
bar .

多くの場合、 定義が例外によって終了した場合でも、 定義を終了したときに値を復元することが重要です。 以下のようにしてみるのはどうでしょうか:

: ...
   save-x
   ['] word-changing-x catch ( ... n )
   restore-x
   ( ... n ) throw ;

しかし、 これでも、 たとえば、catchrestore-x の間を実行中にユーザーが Ctrl-C を押すなどしたら安全ではありません。

Gforth は、そのような場合に対して安全な代替例外処理構文(alternative exception handling syntax) try ...restore ... endtry を提供します。 tryendtry の間のコードに例外があった場合、 スタックの深さが復元され、 例外数値がスタックにプッシュされ、 restore の直後から実行が続行されます。

以下は、 上記のコードと同等の、 より安全なコードです

: ...
  save-x
  try
    word-changing-x 0
  restore
    restore-x
  endtry
  throw ;

こちらも参照ください: Exception Handling


3.31 Defining Words

これまでに出てきた :createvariable は定義ワードです。 これらは他のワードを定義します。 Constant はもう一つの定義ワードです:

5 constant foo
foo .

variableconstant でも接頭辞 2 (2倍長セル) や f (浮動小数点) を使用することができます。

あなた独自の定義ワードを定義することもできます。 例:

: variable ( "name" -- )
  create 0 , ;

また、 単にアドレスを生成する以外のことを行うワードを作成する定義ワードを定義することもできます:

: constant ( n "name" -- )
  create ,
does> ( -- n )
  ( addr ) @ ;

5 constant foo
foo .

上記の constant の定義は does> で終了します。 つまり、 does>; を置き換えるのですが、 他のことも行います。 最後に定義されたワードを変更して、 ワード本体(body)のアドレスをプッシュし、 呼び出されるたびに does> の後のコードを実行します。

上の例では、 constant, を使用して foo の本体に 5 を格納します。 foo が実行されると、 本体のアドレスがスタックにプッシュされ、 (does> の後のコードにより、)そこから 5 がフェッチされます。

does> の脇のスタック・コメントは、 does> の後のコードのスタック効果ではなく、 定義されたワードのスタック効果です(違いは、 does> の後のコードは does> の脇のスタック・コメントには書いてない、 ワード本体(body)のアドレスを期待している点です)。

これらの定義ワードを使用すると、 (他の)定義ワードが関係する場合にファクタリングを行うことができます。 たとえば、 フィールド・オフセットは常にアドレスに加算するものですが、 その代わりに以下を定義します

2 cells constant offset-field1

この offset-field1 は以下のように使います

( addr ) offset-field1 +

ここで、 あなたは以下のような定義ワードをを定義できます

: simple-field ( n "name" -- )
  create ,
does> ( n1 -- n1+n )
  ( addr ) @ + ;

フィールド・オフセットの定義と使用は以下のようになります:

2 cells simple-field field1
create mystruct 4 cells allot
mystruct .s field1 .s drop

does> の後のコードを実行せずに、 そのワードで何かをしたい場合は、 >body ( xt -- addr ) を使用すれば create したワードの本体(body)にアクセスできます:

: value ( n "name" -- )
  create ,
does> ( -- n1 )
  @ ;
: to ( n "name" -- )
  ' >body ! ;

5 value foo
foo .
7 to foo
foo .

研究課題(assignment): (abort の XT の先頭に、) XT を格納するワードを作成する、 defer ( "name" -- ) を定義し、 実行時に XT を execute で実行するようにしてみましょう。 間接再帰は defer の応用の 1 つです。

こちらも参照ください: User-defined Defining Words


3.32 Arrays and Records

Forth には配列を定義するための標準ワードはありませんが、 アドレス演算に基づいて自分で配列を構築できます。 配列とレコードを定義するためのワードを定義することもできます(see Defining Words)。

Forth の初心者がワードの定義について学ぶときに最初に着手するプロジェクトの 1 つが配列定義ワード(ことによっては n 次元配列)です。 さぁあなたもやってみましょう。 あなたはそこから何かを学ぶでしょう。 ただし、 後からこれらのワードをほとんど使用しないことがわかってもがっかりしないでください(不適切に使用するとさらに悪いことになります)。 著者はまだ有用な配列ワードのセットを見つけられていません。 ニーズがあまりにも多様で、 (定義ワードを単純に使用した結果である、)名前付きのグローバル配列は、 柔軟性が十分ではないことがよくあります(たとえば、 どのようにしてパラメーターを渡すか、など)。 同様のプロジェクトのもう 1 つは、 文字列の処理に役立つワードのセットです。

その一方、 レコード・ワードには便利なセットがあり、 compat/struct.fs で定義されています。 これらのワードは Gforth で事前定義されています。 これらについては、 このマニュアルの他の場所で詳しく説明されています(see Structures 参照)。 上記の simple-field の例は、 このパッケージのフィールドの簡略化されたバリエーションです。


3.33 POSTPONE

POSTPONE (訳注: (期限が定まってる)延期)を使用すると、 (そのワードのインタプリタ機能(interpretation semantics)をコンパイルする代わりに、) そのワードのコンパイル機能(compilation semantics)をコンパイルできます:

: MY-+ ( Compilation: -- ; Run-time of compiled code: n1 n2 -- n )
 POSTPONE + ; immediate
: foo ( n1 n2 -- n )
 MY-+ ;
1 2 foo .
see foo

foo の定義中に、 テキスト・インタープリターは MY-+ のコンパイル機能(compilation semantics)を実行し、 そのコンパイル機能が + のコンパイル機能を実行します。 つまり、 +foo 内にコンパイルします。

この例では、 コンパイル機能(compilation semantics)とコンパイルされたコードのスタック効果について個別のスタック・コメントも表示します。 デフォルトのコンパイル機能を持つワードの場合、 通常、 これらのスタック効果は表示されません。 これらのワードのコンパイル機能のスタック効果は常に ( -- ) であり、 コンパイルされたコードのスタック効果はインタープリター機能(interpretation semantics)のスタック効果です。

注意: この方法でコンパイル機能(compilation semantics)を実行する場合、 インタプリタの状態には影響を受けないことに注意してください。 あなたはそれを対話的(interpretively)に実行することもできます。 例:

: foo2 ( n1 n2 -- n )
 [ MY-+ ] ;
1 2 foo2 .
see foo2

ただし、 これが常に機能するとは限らない、良くない Forth システムがいくつかあるため、 この方法は 1999 年に非標準となりました。

POSTPONE を使用する別の例を以下に示します:

: MY-- ( Compilation: -- ; Run-time of compiled code: n1 n2 -- n )
 POSTPONE negate POSTPONE + ; immediate compile-only
: bar ( n1 n2 -- n )
  MY-- ;
2 1 bar .
see bar

ENDIF は以下の方法で定義できます:

: ENDIF ( Compilation: orig -- )
  POSTPONE then ; immediate

研究課題(assignment): 2dup と同等のコンパイル機能(compilation semantics)を持つ MY-2DUP を作成しますが、 コンパイルされるのは over over になるようにしてみましょう。


3.34 Literal

数値を POSTPONE することはできません:

: [FOO] POSTPONE 500 ; immediate

代わりに LITERAL (compilation: n --; run-time: -- n ) を使用します:

: [FOO] ( compilation: --; run-time: -- n )
  500 POSTPONE literal ; immediate

: flip [FOO] ;
flip .
see flip

LITERAL は、 コンパイル時(コンパイル機能(compilation semantics)が実行される時)に数値を消費し、 実行時(コンパイルされたコードが実行されるとき)にそれをプッシュします。 LITERAL のよくある使用法は、 コンパイル時に計算された数値を現在のワードにコンパイルすることです:

: bar ( -- n )
  [ 2 2 + ] literal ;
see bar

研究課題(assignment): 上記の例を : bar ( -- n ) [ 2 2 + ]L ; と記述できるような ]L を定義してみましょう。


3.35 Advanced macros

Execution Tokensmap-array について再検討してみましょう。 map-arrayexecute を頻繁に実行しますが、 これは一部の Forth 実装では比較的高価な操作です。 compile,POSTPONE を使用すると、 これらの execute を削除し、 直接実行されるワードを含むワードを生成できます:

: 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 }
  POSTPONE cells POSTPONE over POSTPONE + POSTPONE swap POSTPONE ?do
    POSTPONE i POSTPONE @ xt compile,
  1 cells POSTPONE literal POSTPONE +loop ;

: sum-array ( addr u -- n )
 0 rot rot [ ' + compile-map-array ] ;
see sum-array
a 5 sum-array .

コードの生成には Forth の機能を最大限に活用できます。 以下に、 コードがループ内で生成される例を示します:

: compile-vmul-step ( compilation: n --; run-time: n1 addr1 -- n2 addr2 )
\ n2=n1+(addr1)*n, addr2=addr1+cell
  POSTPONE tuck POSTPONE @
  POSTPONE literal POSTPONE * POSTPONE +
  POSTPONE swap POSTPONE cell+ ;

: compile-vmul ( compilation: addr1 u -- ; run-time: addr2 -- n )
\ n=v1*v2 (inner product), where the v_i are represented as addr_i u
  0 postpone literal postpone swap
  [ ' compile-vmul-step compile-map-array ]
  postpone drop ;
see compile-vmul

: a-vmul ( addr -- n )
\ n=a*v, where v is a vector that's as long as a and starts at addr
 [ a 5 compile-vmul ] ;
see a-vmul
a a-vmul .

この例では compile-map-array を使用していますが、 代わりに map-array を使用することもできます(是非試してみてください)。

この手法を使用すると、 巨大な行列を効率的に乗算できます。 行列の乗算では、 一方の行列のすべての行ともう一方の行列のすべての列を乗算します。 1 行のコードを 1 回生成し、 それをすべての列に使用できます。 この手法の唯一の欠点は、 生成されたコードによって消費されたメモリーを完了時に開放するのが面倒なことです(さらに複雑な場合は移植可能ではありません)。


3.36 Compilation Tokens

このセクションは Gforth 固有です。 スキップしても構いません。

' word compile, はインタープリター機能(interpretation semantics)をコンパイルします。 デフォルトのコンパイル機能(compilation semantics)を持つワードの場合、 これはコンパイル機能を実行するのと同じです。 他のワードのコンパイル機能(インタープリター機能を持たない if などのワード)を表すために、 Gforth にはコンパイル・トークン(CTと略します。 2つのセルで構成)と、 ワード comp' と、 ワード [comp'] の概念があります。 execute を使用して、 CT によって表されるコンパイル機能を実行できます:

: foo2 ( n1 n2 -- n )
   [ comp' + execute ] ;
see foo2

postpone, を使用して、 CT によって表されるコンパイル機能をコンパイルできます:

: foo3 ( -- )
  [ comp' + postpone, ] ;
see foo3

[ comp' word postpone, ] POSTPONE word と同等です。 comp' は、 インタープリター・モード用のコードを持たないワードに対して特に役立ちます:

' if
comp' if .s 2drop

こちらも参照ください: Tokens for Words


3.37 Wordlists and Search Order

ディクショナリー(辞書)は、 allot でメモリーを割り当てることができる、 単なるメモリー領域ではなく、 複数のワード・リスト(wordlist)上にある Forth ワード達も含まれています。 ワード・リスト内のワードを検索するとき、 概念的には、 最も新しいワードから検索を開始し、 古いワードに向かって進みます(実際には、 最近のほとんどのシステムはハッシュ・テーブルを使用します)。 つまり、 古いワードと同一の名前のワードを定義すると、 新しいワードが古いワードを隠します。

どのワードリストがどの順序で検索されるかは、 検索順序スタック(the search order)によって決まります。 order で検索順序を表示できます。 最初に検索されるワードリストから順に検索順序を表示し、 その次に、 新しく定義されるワードを含むワードリストを表示します。

wordlist ( -- wid ) を使用して、 新しい空のワード・リスト(wordlist)を作成できます:

wordlist constant mywords

Set-current ( wid -- ) は、 新しく定義されたワードを入れるワード・リストを設定します(the current wordlist):

mywords set-current
order

このワード・リストは wordlist を使用して匿名で作成されたため、 Gforth は mywords のワード・リスト名を表示しません。

get-current ( -- wid) で現在のワード・リストを取得できます。 現在のワード・リストに影響を与えずに、 指定のワード・リストに何かを入れたい場合、 通常は以下のようにします:

get-current mywords set-current ( wid )
create someword
( wid ) set-current

検索順序スタック(the search order)は set-order ( wid1 .. widn n -- ) で記述し、 get-order ( -- wid1 .. widn n ) で読み取ることができます。 ( n を除いて) もっとも TOS 側のワード・リストが最初に検索されます。

get-order mywords swap 1+ set-order
order

ええ、 order の出力内のワードリストの順序は、 スタック・コメントや .s の出力とは逆になっているため、 直感的ではありません。

研究課題(assignment): >order ( wid -- ) を定義して、 最初に検索されるワード・リスト(wordlist)として wid を検索順序スタック(the search order)に追加してみましょう。 previous ( -- ) を定義してみましょう。 これは、 最初に検索されたワードリスト(wordlist)を検索順序スタックから削除するものです。 定義したら、 境界条件を試してみましょう(クラッシュや、 そこから抜け出すことが困難または不可能な状況がいくつか見られる事でしょう)。

検索順序スタック(the search order)は、 Modula-2 モジュールや、 C++ の名前空間と同様の機能を提供するための強力な基盤です。 ただし、 この方法でプログラムをモジュール化しようとすると、 デバッグや 再利用/ファクタリング に関しては、 (大規模なプロジェクトの経験はありませんけれども、)著者の経験では利点を上回る欠点があります。 他の言語/プログラミング環境では、 デバッグや再利用がそれほど強力ではないため、 これらの欠点はそれほど目立ちません。

こしらも参照ください: Word Lists


4 An Introduction to Standard Forth

この章とチュートリアル(see Forth Tutorial)との違いは、 急ぎ足のチュートリアルと違ってじっくり腰を据えて、 チュートリアルではカバーしきれていない、 Forth の内部を詳しく説明していることです。 それはさておき、 この章で取り上げる内容はチュートリアルに比べはるかに少ないので、 パソコンを使わずに読むのに適しています。

このマニュアルの主な目的は、 Gforth を文書化することです。 ただし、Forth は広く知られている言語ではなく、 最新の教材が不足しているため、 入門用の教材を提供する価値があると思われます。 巷の Forth 関連情報その他の情報源については Other Forth-related information を参照下さい。

このセクションの例は、 どの標準 Forth でも動作するはずです。 示されている出力は Gforth を使用して生成されました。 各例では、 Gforth が生成する正確な出力を再現しようとしています。 あなたが例を試してみれば(そして、あなたはそうするべきです)、 入力すべき内容は like this と示され、 Gforth の応答は like this で示されます。 唯一の例外は、 例で RET が示されている場合、 これは「Enter」キー(機種によりReturnキー)を押す必要があることを意味します。 残念ながら、 このマニュアルの一部の出力形式では thisthis の違いを表示できないため、 例を試してみるのが困難かもしれません(ただし、 不可能ではありません)。

Forth は珍しい言語です。 インタープリターとコンパイラーの両方を含む対話型開発環境を提供します。 Forth のプログラミング・スタイルでは、 問題をいくつかの要素に分割することが推奨されます 小さな断片を作成し(ファクタリング)、 各断片を対話的に開発・テストします。 Forth の支持者は、 従来のプログラミング言語で使用されている編集・コンパイル・テストのサイクルを打ち破ることで、 生産性の大幅な向上につながる可能性があると主張しています。


4.1 Introducing the Text Interpreter

Forth イメージを呼び出すと、 起動バナーが出力されますが、 他には何も表示されません (システムに Gforth がインストールされている場合は、 gforthRET と入力して今すぐ呼び出してみてください)。 今、 Forth は、 テキスト・インタープリター(Text Interpreter)と呼ばれるコマンド・ライン・インタプリタを実行しています(外部インタープリターとも呼ばれます;訳注: 別途存在する 内部インタープリター(inner interpreter)に対してこう呼ばれる)。 (この章を読み進ればテキスト・インタープリターについて多くのことを学ぶことができます。 詳細については see The Text Interpreter を参照してください)。

明白ではなくて分かりにくいですが、 今や Forth はユーザーの入力を待っています。 数字の 4 と 5 を入力し RET キーを押します:

45RET  ok

テキスト・インタープリターは、 次の入力を促すプロンプトを表示するのではなく、 入力行を処理した後に(改行無しで)ステータス・メッセージを出力します。 この場合のステータス・メッセージ(RET (「エンター」(リターン)キー押下)の後の ok )は、 テキスト・インタープリターがすべての入力を正常に処理できたことを示します。 それでは次に、 不正な文字列を入力してみましょう:

qwer341RET
*the terminal*:2: Undefined word
>>>qwer341<<<
Backtrace:
$2A95B42A20 throw 
$2A95B57FB8 no.extensions 

‘Undefined word‘ 以外の文章はシステムによって若干異なる場合がありますが、 意味は同じです。 テキスト・インタープリターがエラーを検出すると、 行に残っているテキストを破棄し、 特定の内部状態をリセットして、 エラー・メッセージを出力します。 エラー・メッセージの詳細な説明については、 Error messages を参照してください。

テキスト・インタープリターは、エンター・キー(リターン・キー)が押されるのを待ち、 その後入力行を処理します。 行頭から開始して、 行をスペースで区切られた文字のグループに分割します。 文字のグループごとに、 以下の順番で、 何かするために計 2 回の試みを行います:

  • その文字のグループをコマンドとして扱おうとします。 これは、 名前ディクショナリ(name dictionary)を検索することによって行われます。 その文字のグループが名前ディクショナリーのエントリと一致する場合、 名前ディクショナリーは、 いくつかのアクションを実行できるようにする情報をテキスト・インタープリターに提供します。 Forth 流に言うと、文字のグループを「ワードの名前」といい、 ディクショナリー検索でワードの定義(definition)に対応する実行トークン(execution token;xt)が返され、 テキスト・インタープリターは xt を実行します。 多くの場合、「ワード」(word)と「定義」(definition)という用語は同じ意味で使用されます。
  • テキスト・インタープリターが名前ディクショナリー内で一致するものを見つけられなかった場合、 文字のグループを現在の基数の数値として処理しようとします(Forth を起動したときの、 現在の基数は 10 です)。 文字のグループが正当な数値を表す場合、 テキスト・インタープリターはその数値をスタックにプッシュします(これについては次のセクションで詳しく説明します)。

テキスト・インタープリターが文字グループに対して上記のいずれも実行不可能な場合、 その文字グループと行の残りの部分が破棄され、 エラー・メッセージが出力されます。 テキスト・インタープリターがエラーなく行末に到達すると、 ステータス・メッセージ ok に続いて改行を出力します。

以下は、 テキスト・インタープリターに与えることができる最もシンプルなコマンドです:

RET  ok

ここで、 テキスト・インタープリターは、 私たちが要求したことをすべて(何もせずに)エラーなしで実行したため、 すべてが ok であると表示しました。 今度は少し長いコマンドを試して見ましょう:

12 dup fred dupRET
*the terminal*:3: Undefined word
12 dup >>>fred<<< dup
Backtrace:
$2A95B42A20 throw 
$2A95B57FB8 no.extensions 

エンター・キー(リターン・キー)を押すと、 テキスト・インタープリターが行に沿って動作を開始します:

  • 2 の後のスペースに到達すると、 文字グループ 12 を取得し、 それらを名前ディクショナリーで検索します5。 名前ディクショナリにはこの文字グループに一致するものがないため、それらを数値として処理しようとし、 そして、 数値として処理するのは正常に実行できたので、 (それが何を意味するにせよ) 数値 12 を「スタック上」に置きます。
  • テキスト・インタープリターは行のスキャンを再開し、 次の文字グループ ‘dup‘ を取得します。 ‘dup‘ を名前ディクショナリーで探し、 (これは著者の言葉を信じてもらうしかありませんが) ‘dup‘ を名前ディクショナリーで見付けて、 ワード dup を実行します(それが何を意味していても)。
  • 更に再び、 テキスト・インタープリターは行のスキャンを再開し、 文字グループ fred を取得します。 名前ディクショナリーで調べますが、見つかりません。 それらを数値として扱おうとしますが、 正当な数値を表すものではありませんでした。

この時点で、 テキスト・インタープリターは諦めてエラー・メッセージを出力します。 エラー・メッセージには、 テキスト・インタープリターが行の処理でどこまで到達したかが正確に示されます。 これは、 特に、 テキスト・インタープリターが最後の文字グループ dup に対して何も行おうとしなかったことを示しています。 テキスト・インタプリタがそのワード dup を検索して、 一度はちゃんと実行されたのですから、 もう一度実行することに何の問題はないはずなのに、 です。


4.2 Stacks, postfix notation and parameter passing

手続き型プログラミング言語(C や Pascal など)では、 プログラムの構成要素は関数(function)やプロシージャ(procedure)です。 これらの関数またはプロシージャは、 明示的なパラメーターを使用して呼び出されます。 たとえば、 C では以下のように記述できます:

total = total + new_volume(length,height,depth);

ここで、 new_volume は別のコード片への関数呼び出し(function-call)であり、 total, length, height, depth はすべて変数です。 length, height, depth は関数呼び出しのパラメーターです。

Forth では、 関数またはプロシージャに相当するのは「定義」(definition)であり、 パラメーターはプログラマから見える共有スタックを使用して定義間で暗黙的に渡されます。 Forth は変数をサポートしていますが、 スタックの存在は、 他のほとんどのプログラミング言語よりも変数が使用される頻度がはるかに低いことを意味します。 テキスト・インタープリターは数値を検出すると、 それをスタックに置きます(プッシュ)。 何種類かのスタックがあり(いくつあるかは実装依存です)、 操作に使用されるスタックは、 実行される操作によって明確に示されます。 すべての整数演算に使用されるスタックは「データ・スタック」と呼ばれ、 これが最も一般的に使用されるスタックであるため、 「データ・スタック」への参照は多くの場合「スタック」と省略されます。

スタックには後入れ先出し(last-in, first-out;LIFO)構成が採用されています。 以下のように打ち込むと:

1 2 3RET  ok

これはテキスト・インタープリターに 3 つの数値を (データ)スタック に配置するように指示します。 スタックの振る舞いはトランプの扱いに例えられます。 トランプの箱から、 (スートは何でもいいですが、) エース(1)のカードと2のカードと3のカードをテーブルの上の山に配ります。 3のカードは山の最後のカード (last-in)であり、 山からカードを 1 枚取ると、 あなたがシャッフルとか何もいじってない限り、 取り出すカードは 3のカードになります(first-out)。 スタックから最初に取り出されるであろう(スタック上の)数値はスタック頂上(スタック・トップ;top of stack)と呼ばれ、 多くの場合「TOS」と省略されます。

Forth でパラメーターがどのように渡されるかを理解するために、 定義 + (「プラス」と発音します) の振る舞いを見てみましょう。 あなたは、 この定義が加算を実行することを知っても驚かないでしょう。 より正確には、 これは 2 つの数値を加算して結果を生成します。 さて、 その 2 つの数値はどこから取得されるのでしょうか? これはスタック頂上から上位 2 つの数値を取り除きます。 その結果はどこに配置されるのでしょうか? それはスタックです。 あなたは以下のように、 トランプを使って + の振る舞いを演ずることができます。

  • テーブル上の山(the steck)カードを 2 枚取り出します
  • あなたは、 それらをじっと見つめて、 「これら 2 つの数字の合計は何であるか」を自問してください。
  • 答えは 5 であると判明しました
  • 2 枚のカードをシャッフルしてトランプの箱に戻し、 トランプの箱の中から 5のカード を見つけます
  • テーブル上にある残りのエース(1)のカードの上に 5のカードを置きます。

トランプの箱はないですが、 Forth の実行中は、 定義 .s を使用して、 スタックに影響を与えることなく、 スタックの現在の状態を表示できます。 以下の様に打ち込みます:

clearstacks 1 2 3RET ok
.sRET <3> 1 2 3  ok

テキスト・インタープリターはワード clearstacks を検索して実行します。 スタック(データおよび浮動小数点スタック)を整理し、 以前の例の実行によってスタックに残された可能性のあるエントリをすべて削除します。 続けてテキスト・インタプリタは、 3 つの数値をそれぞれ順番にスタックにプッシュします。 最後に、 テキスト・インタープリターはワード .s を検索して実行します。 .s を実行すると、「<3>」 (スタック上の項目の合計数) に続いて、スタック上のすべての項目のリストが出力されます。 一番右側の項目が TOS です。

あなたは今や以下のように打ち込めます:

+ .sRET <2> 1 5  ok

現在スタックには 2 つの項目があり、 (そのうちの項目の1つである)加算の結果は 5 となっていれば正解です。

あなたが引き続きトランプで遊んでいるなら、 この結果に対して 2 回目の足し算を行ってみましょう。 2 枚のカードを手に取り、 その合計が 6 であることを計算し、手に取った2枚のカードをシャッフルして箱に入れ、 箱から 6のカード を探してテーブルに置きます。 これで、 スタックにはアイテムが 1 つだけになりました。 あなたが 3 回目の足し算を実行しようとするとどうなるでしょうか? あなたは、 1枚目のカードを手に取り、 2枚目のカードを手に取ろうとします – ああ! でも 2枚目のカードはありません。 これは「スタック・アンダーフロー」と呼ばれ、エラーとなります。 Forth で同じことを行おうとすると、 多くの場合、 エラーが報告されます(おそらく、 スタック・アンダーフロー(Stack Underflow)エラー、 または、 無効なメモリーアクセス(Invalid Memory Address)エラー)。

スタック・アンダーフローの逆の状況が「スタック・オーバーフロー」です。 これは、 あなたがたが、 スタック用に予約されている有限量のストレージ・スペースがあることを単に受け入れるだけで済みます。 トランプの例えを拡張すると、 沢山の数のトランプのカードがあり、 それらのカードをテーブルに積み上げた場合、 天井にぶつかってしまい、 最終的には別のカードを追加できなくなります。 Gforth を使用すると、 スタックの最大サイズを設定できます。 一般に、 スタック・オーバーフローが発生するのは、 定義にバグがあり、 制御不能にスタック上にデータを生成している場合のみです。

トランプの例えには、 最後にもう 1 つ適用例があります。 トランプを使用してスタックをモデル化した場合、 スタック上のアイテムの最大数は 52 になります(ジョーカーを使用しなかったと仮定して)。 スタック上のアイテムの最大値は13(キング)です。 実際は、 使用できる数値は 1 ~ 13 の正の整数のみ(つまり、 13個の数)です。 (たとえば) 0や27や3.52や-2は使用できません。 そこで、 一部のカードについて考え方を変え、 さまざまな数値に対応できるようにします。 たとえば、ジャックは 0 を表し、 クイーンは -1 を表し、 キングは -2 を表すと見なします。 すると、 表現できる数値の幅は変更されません(合計 13 個の数値のみを表すことができます)が、 -2 から 10 の数値を表す事ができるようになりました。

この例えでは、 制限は 1 つのスタック・エントリが保持できる情報の量であり、 Forth にも同様の制限があります。 Forth では、 スタック・エントリのサイズは「セル」(cell)と呼ばれます。 セルの実際のサイズは実装に依存し、 スタック・エントリが保持できる最大値に影響します。 標準 Forth は少なくとも 16 ビットのセル・サイズを提供し、 ほとんどのデスクトップ・システムは 32 ビットのセル・サイズを使用します。

Forth は型チェックを一切行わないため、 スタック項目を自由に操作したり組み合わせたりできます。 スタック項目を 2 の補数の符号付き整数として扱う便利な方法は、 + などの標準ワードもやっています。 それゆえ、 以下のように入力できます:

-5 12 + .sRET <1> 7  ok

あなたが、 Forth を電卓にするために、 数値や + のような定義を使用すると、 それが通常の電卓とはかなり異なることがわかるでしょう。 あなたは 2 + 3 = と入力するのではなく、 2 3 + と入力する必要があります(ここでは、 結果を確認するには .s を使用する必要があるという事実は無視してください)。 この違いを説明するために使用される用語は、 電卓は「中置記法」(Infix Notation;パラメーターと演算子が混合されている)を使用するのに対し、 Forth は「後置記法」(Postfix Notation;パラメーターと演算子が分かれている)、 またの名を「逆ポーランド記法」(Reverse Polish Notation)と呼ばれるものを使用します。

後置記法は最初はわかりにくいように見えるかもしれませんが、 いくつかの重要な利点があります:

  • 明白です
  • より簡潔です
  • スタックベースのシステムに自然に適合します

これらの主張をさらに詳しく調べるために、 以下の計算について見てみましょう:

6 + 5 * 4 =
4 * 5 + 6 =

あなたが、算数を勉強中の場合、 または算数が非常に苦手な場合は、 最初の答えは 44、 2 番目の答えは 26 になるでしょう。 あなたが算数に少し詳しい人なら、 乗算は加算よりも優先されるという法則を覚えているでしょう。 そして、 どちらの場合も答えは 26 になるでしょう。 答え 44 だと言った人に、 なぜ答えが 26 かを説明するには、 最初の計算を以下のように書き換えることになるでしょう:

6 + (5 * 4) =

あなたが、 もし、 乗算より優先して加算を実行したい場合は、 かっこを使用して強制的に加算させる必要があります。

上記 2 つの計算を電卓で計算する場合、 以下のキーストローク・シーケンスを使用して、 入力ミスしない限り、 おそらく正しい答えが得られるでしょう:

6 + 5 = * 4 =
4 * 5 = + 6 =

Postfix notation is unambiguous because the order that the operators are applied is always explicit; that also means that parentheses are never required後置記法では演算子が適用される順序は常に明示的です。 これは、 括弧が決して必要ないことも意味します。 演算子はアクティブです(演算子を引用する行為によって演算操作が実行されます)。 これにより、「=」が必要なくなります。

計算 6 + 5 * 4 は、 以下の 2 つの同等の方法で(後置表記で)書くことができます:

6 5 4 * +
または:
5 4 * 6 +

この表記法に関して注意すべき重要な点は、 数値の順番は変わらないことです。 10 から 2 を減算する場合は、 10 2 - と入力します。

Forth が後置表記を使用する理由は非常に簡単に説明できます。 これにより実装が非常に単純になり、 パラメーターを渡すためのメカニズムとしてスタックを使用することが自然な流れになります。 これについての別の考え方としては、 すべての Forth 定義がアクティブであると認識することです。 これらは、 テキスト・インタープリターによって検出されると実行されます。 この結果、 Forth の構文は何の努力も必要とせずシンプルになります。


4.3 Your first Forth definition

これまで私たちが見てきた例はこまごまとしたものでした。 私たちは Forth を大きめの電卓として使用してきました。 また、 これまでに示した各計算は「1 回限り」のものです – それを繰り返すには、もう一度入力する必要があります6。 このセクションでは、 Forth の語彙(vocabulary)に新しいワードを追加する方法を説明します。

新しいワードを作成する最も簡単な方法は、「コロン定義」を使用することです。 それらがどのように機能するかについて思い悩む前に、 いくつか定義して試してみましょう。 以下の例を入力してみてください。 スペースを正確に反映するように注意してください:

: add-two 2 + . ;
: greet ." Hello and welcome" ;
: demo 5 add-two ;

この例を打ち込んだら、 すぐ試してみましょう:

greetRET Hello and welcome  ok
greet greetRET Hello and welcomeHello and welcome  ok
4 add-twoRET 6  ok
demoRET 7  ok
9 greet demo add-twoRET Hello and welcome7 11  ok

ここで導入した最初の新しいモノは、 :; というワードのペアです。 これらは、それぞれ新しい定義を開始および終了するために使用されます。 : の後の最初の単語(word)は、 新しい定義の名前です。

例からわかるように、 定義はすでに定義されているワードから構成されます。 Forth では、 システムの起動時に存在した定義と、 ユーザーが自分で定義した定義とを区別しません。

この例では、 . (ドット) や、 ." (ドット・クォート)や、 dup (デュープ) というワードも紹介しています。 ドットはスタック頂上から数詞を取得して表示します。 これは .s に似ていますが、 スタック頂上の項目のみを表示する点と破壊的である点が異なります。 実行後、スタック上にその数値は最早ありません。 数値の前はスペース無しで、 後ろには常に 1 つのスペースが表示されます。 ドット・クォートは、 ワードの実行時に出力される文字列(string)を定義します。 文字列には、 " を除く任意の印刷可能な文字を含めることができます。 " には特別な機能があります。 これは Forth ワードではありませんが、 区切り文字として機能します(区切り文字の仕組みについては次のセクションで説明します)。 最後に、 dup はスタック頂上の値を複製します。 5 dup .s と入力して、 dup がやることを確認してください。

あなたは、 既に、 テキスト・インタープリターが名前を見つけるためにディクショナリーを検索することを知っています。 あなたが上記の例の通りにした場合、 add-two という定義がすでにあるはずです。 それでは、 この定義に対して、 更に新しい定義を入力して変更してみるとしましょう:

: add-two dup . ." + 2 = " 2 + . ;RET redefined add-two  ok

Forth は、私たちがすでに存在するワードを定義しようとしていることを認識し、 それを警告するメッセージを出力しました。 さて、 それでは、 新しい定義を試してみましょう:

9 add-twoRET 9 + 2 = 11  ok

ただし、 ここで実際に行ったことは、 特定の名前を付けて新しい定義を作成することだけです。 同一の名前の定義がすでに存在するという事実は、 (Forth が警告メッセージを出力することを除いて、)新しい定義の作成方法に何の違いもありません。 add-two の古い定義はいまだ存在します(これが正しいかどうかを確認するには、 demo をもう一度試してください)。 add-two の新しい定義以降に定義するワードは add-two の新しい定義を利用しますが、 demo の定義は、 古い定義は demo をコンパイルする時点で既に存在していた add-two の古い定義のバージョンを引き続き使用します。

次のセクションに進む前に、 あなた自身のワードをいくつか定義したり再定義したりしてみましょう。


4.4 How does that work?

ここで、 前のセクションの 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 つの性質を持ちます:

  • その インタープリター機能(interpretation semantics)は、 テキスト・インタープリターがインタープリター状態でどのように動作するかを記述します。 ワードのインタープリター機能は、 その「実行トークン」(execution token)(see Execution token)によって表されます。
  • その コンパイル機能(compilation semantics)は、 テキスト・インタープリターがコンパイル状態でどのように動作するかを記述します。 ワードのコンパイル機能は、 その「コンパイル・トークン」(see Compilation token)によって表されます。

数値は常に決まった方法で処理されます:

  • その数値が通訳(interpret)される場合、 その振る舞いは、 その数値をスタックにプッシュすることです。
  • その数値がコンパイルされる場合、 実行時にその数値をプッシュするコードが現在の定義に追加されます。 (言い換えれば、 数値のコンパイル機能(compilation semantics)は、 コンパイルされる定義の実行時まで、 その数値のインタープリター機能(interpretation semantics)の実行を延期(postpone)します。)

ワードは常にこのような通常の振る舞いをするとは限りませんが、 ほとんどのワードにはデフォルトの機能(default semantics)があり、 以下のように振る舞うことを意味します:

  • ワードのインタープリター機能(interpretation semantics)は、 何かしら役に立つことを行うことです。
  • ワードのコンパイル機能(compilation semantics)は、 そのワードのインタープリター機能(interpretation semantics)を現在の定義に追加します(よって、 それは実行時に何かしら役に立つことを行います)。

特定のワードの実際の振る舞いは、 ワードの定義時に immediatecompile-only というワードを使用することで制御できます。 これらのワードは、 最後に定義されたワードの名前ディクショナリー・エントリにフラグを設定します。 これらのフラグは、 名前ディクショナリーでワードが見つかったときにテキスト・インタープリターによって取得されます。

immediate としてマークされたワードは、 そのインタープリター機能(interpretation semantics)と同じコンパイル機能(compilation semantics)を持ちます。 つまり、 以下のように振る舞います:

  • ワードのインタープリター機能(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 の定義を終了します。


4.5 Forth is written in Forth

あなたが Forth コンパイラーを起動すると、 すでに多数の定義が存在しています。 Forth では、 ボトムアップ・プログラミング手法を使って新しいアプリケーションを開発し、 既存の定義に基づいて定義される新しい定義を作成します。 作成した各定義は、 対話的にテストおよびデバッグできます。

この章の例を試したことがあるなら、 あなたは、それらをおそらく手動で入力したことがあるでしょう。 Gforth を終了すると、 あなたが定義したモノは失われてしまいます。 これを回避するには、 テキスト・エディタを使用して Forth ソース・コードをファイルに入力し、 include を使用してファイルからコードをロードします(see Forth source files)。 Forth のソース・ファイルは、 あたかも手入力したかのように、 テキスト・インタープリターによって処理されます7

Gforth は、 プログラム入力 にテキスト・ファイルを使用する、 伝統的な Forth の代替手段もサポートしています(see Blocks)。

ほとんどというほどではないにしても、 多くの Forth コンパイラーと共通して、 Gforth のほとんどは実際には Forth で書かれています。 インストール・ディレクトリ8内のすべての .fs ファイルは Forth ソース・ファイルであり、 あなたは それらを Forth プログラミングの例として拝んで学ぶことができます。

Gforth は、 テキスト・インタープリターに入力したすべての行を記録する履歴(history)ファイルを維持します。 このファイルはセッションをまたいで維持され、 コマンドラインの呼び出し機能を提供するために使用されます。 長い定義を手動で入力した場合は、 テキスト・エディターを使用して履歴ファイルから後で再利用するために Forth ソース・ファイルに貼り付けることができます(詳細については see Command-line editing)。


4.6 Review - elements of a Forth system

この章を要約すると:

  • Forth プログラムはファクタリングを使用して、 問題を「ワード」または「定義」と呼ばれる小さな断片に分割します。
  • Forth プログラムの開発は対話型のプロセスです。
  • 入力を受け付け、 通訳(interpret)とコンパイルの両方を制御するメイン・コマンド・ループは、「テキスト・インタープリター」と呼ばれます(外部インタープリターとも呼ばれます)。
  • Forth の構文は非常に単純で、 スペースまたは改行文字で区切られた単語(word)と数値で構成されます。 追加の構文は構文解析ワード(parsing words)によります。
  • Forth はスタックを使用してワード間でパラメーターを渡します。 その結果、 後置記法が使用されます。
  • 以前に定義されたワードを使用するために、 テキスト・インタープリターは「名前ディクショナリ」でそのワードを探します。
  • ワードはインタープリター機能(interpretation semantics)とコンパイル機能(compilation semantics)を持っています。
  • テキスト・インタープリターは、 state の値を使用して、 探し出したワードのインタプリタ機能(interpretation semantics)を使用するかコンパイル機能(compilation semantics)を使用するかを選択します。
  • ワードのインタープリター機能(interpretation semantics)とコンパイル機能(compilation semantics)の関係は、 そのワードが定義された方法(たとえば、 それが「即実行ワード」(immediate word)であるかどうか)によって異なります。
  • Forth 定義は、 Forth で実装(「高レベル定義」と呼ばれます)、 または、 その他のいくつかの方法(通常は低レベル言語で、 「低レベル定義」、「コード定義」、「プリミティブ」と呼ばれる事もある)で実装できます。
  • 多くの Forth システムは主に Forth で記述されています。

4.7 Where To Go Next

信じられないかもしれませんが、 あなたがここまで読んで(そして理解していれば)、 Forth システムの内部動作についての基本をほぼすべて知っていることになります。 あなたは今や確実に、 このマニュアルの残りの部分と標準 Forth ドキュメントを読んで理解し、 Forthの一般的な機能と、 特に Gforth が提供する機能について詳しく学ぶのに十分な知識を持っています。 更に恐るべきことに、 あなたは独自の Forth システムを実装するのにほぼ十分な知識を持っていますが、 独自の Forth システムを実装するには時期尚早かもしれません。 その前に、 あなたは Gforth でいくつかのプログラムを書いてみたほうがいいでしょう。

Forth には非常に豊富な語彙があるため、 学ぶにも、 どこから手を付ければいいかわからない場合があります。 このセクションでは、 小さいながらも有用なプログラムを作成するのに十分なワードのセットをいくつか提案します。 このドキュメントのワード索引を使用して各ワードについて詳しく学び、 それを試して、 それを使用して簡単な定義を書いてみてください。 まずは以下のワード達を試してみてください:

  • 算術演算: + - * / /MOD */ ABS INVERT
  • 比較: MIN MAX =
  • 論理演算: AND OR XOR NOT
  • スタック操作: DUP DROP SWAP OVER
  • ループと条件判断: IF ELSE ENDIF ?DO I LOOP
  • 入出力: . ." EMIT CR KEY
  • 定義ワード: : ; CREATE
  • メモリー割り当てワード: ALLOT ,
  • ツール: SEE WORDS .S MARKER

上記をマスターできたら、 以下に進みましょう:

  • さらなる 定義ワード いくつか: VARIABLE CONSTANT VALUE TO CREATE DOES>
  • メモリー・アクセス: @ !

これらをマスターしたら、 あなたは、 このマニュアルのすべてに目を通し、 あなたのプログラムに欠けているものを探し出す必要があります。


4.8 Exercises

TODO: すでに完了した内容やマニュアルの他のセクションにリンクされた一連のプログラミング演習を提供したい。 すべての演習に対する回答を、 ディストリビューション内の .fs ファイルで提供するようにしたいです。


5 Literals in source code

整数値をデータ・スタックにプッシュするために、 あなたはソース・コードに数値を書きます。 たとえば 123 です。 数字の連なりの前に - を付けると、 負の数値を示すことができます。 たとえば -123 です。 これはコロン定義の内部と外部の両方で機能します。 数値は base (基数) の値に従って通訳(interpret)されます(see Number Conversion)。 「数字」は 09a (10進数の10) ~ z (10進数の35) です。 ただし、 基数(base)より小さい「数字」のみが認識されます。 変換では大文字と小文字が区別されないため、Aa は同じ「数字」になります。

以下のプレフィックスを使用すると、 数値の基数(base)を明示的に指定できます:

基数プレフィックスと符号を含む組み合わせの場合、 標準的な順序では基数プレフィックスを最初に配置します(例: #-123)。 Gforth は両方の順番をサポートします。

小数点 . を数値の末尾(または非標準的に、 プレフィックスの前を除く他の場所)に置くと、 2倍長整数double-cell integer)として扱われます(例: #-123. または #-.123 (この2つは同一の数値です))。 別のプログラミング言語の経験があるユーザーは、 基数プレフィックスのない、 このような数値(例: -123.) を見たり書いたりする場合、 その数値が浮動小数点値を表すものと期待する可能性があります。 混乱を早期に解決するために、 Gforth はそのような使用法について警告しています。 警告を回避するには、 常に基数プレフィックスを付けて 2倍長整数(double-cell integer)を記述することをお勧めします(例: #-123.)

以下にいくつかの例を示します。 なお、 同値の 10 進数が括弧内に示されています:

$-41 (-65), %1001101 (205), %1001.0001 (145 ; 2倍長整数), #905 (905), $abc (2478), $ABC (2478).

(文字)コード・ポイントの数値を取得するには、 文字を ' で囲みます (例: 'a')。 末尾の ' は標準では必須ですが、Gforth では省略できます。 注意: これは非 ASCII 文字でも機能することに注意してください。 多くの用途では、 文字をセルとしてではなく文字列として持つ方が便利です。 文字列の構文については、 以下を参照してください。

Forth の浮動小数点数は、 その指数によって認識されます。 つまり、 1. は 2倍長整数(double-cell integer)で、 そして 1e0 は浮動小数点数です。 後者は 1e に短縮できます(通常は短縮します)。 仮数部(eE より前の部分)と指数の部分の両方に符号(+ を含む)を含めることができます。 仮数部には少なくとも 1 つの数字が含まれている必要があり、 小数点を含めることができます。 指数は空であってもかまいません。 浮動小数点数は仮数と指数の両方に常に 10 進数の基数を使用し、 基数が 10 進数の場合にのみ認識されます。 例: 1e 1e0 1.e 1.e0 +1e+0 (これらは全て同一の数値です)、 +12.E-4

Gforth 拡張機能 (1.0 以降)では、 浮動小数点数をスケーリングされた表記で書くことができます。 オプションで符号、 その次に 1 つ以上の数字を指定し、その後ろに、 主に SI で定義されたスケーリング記号(別名 メトリック・プレフィックス)または % のうちの 1 つを使用でき、 その後に、 オプションで更に多くの桁を指定できます。 Gforth が受け入れるスケーリング記号の完全なリストは以下のとおりです:

Gforth の残りのほとんどとは異なり、 スケーリング・シンボルは大文字と小文字が区別されて扱われます。 スケール表記を使用するということは、 スケール記号の代わりに小数点を使用し、 末尾に指数表記を追加することと同じです。 スケール表記の例: 6k5 (6500e), 23% (0.23e)

文字列を " で囲んで入力できます(例: "abc""a b")。 その結果は、データスタック上の、 文字列の開始アドレスと、 バイト(=char)のカウントです。

文字列内の "\ でエスケープする必要があります(例: "double-quote->\"<-")。 さらに、 この文字列構文は、 s\" でサポートされている制御文字を記述するすべての方法をサポートしています(see String and Character literals)。 この文字列構文の欠点は、 標準ではないことです。 標準プログラムの場合は、 "…" の代わりに s\" を使用してください。

環境変数を取得するには、 最初に rec-env.fs をロードし、 次に環境変数名を { と } で囲い、 その前に $ を付けます (例: ${HOME})。 結果は、 上で説明した形式のデータ・スタック上の文字列記述子(string descriptor)になります。 これは "HOME" getenv と同等です。つまり、 環境変数は実行時(run-time)に解決されます。

ワード名の前に ` を付けることで、 ワードの実行トークン (xt) を取得できます (例: `dup)。 ' または ['] を使うよりも有利な点は、 コロン定義の内側から外側へ、 またはその逆にコードをコピーして貼り付けるときに、 これらを切り替える必要がないことです。 欠点は、 この構文が標準ではないことです。

ワード名の前に `` を付けることで、 ワードの名前トークン (nt) を取得できます (例: ``dup)。 この構文も非標準です。

ワード名を<> で囲むことで、 ワードの本体(body)アドレスを取得できます(例: <spaces>)。 ワード名と末尾の > の間に + と数値を入れることで、 その本体アドレスから正のオフセットのアドレス(通常はそのワードの本体内のアドレス)を取得することもできます(例: <spaces+$15>, spaces+-3)。 例えば <spaces+$15> とか <spaces+-3>) とすると、 その本体アドレスに数値を足したのを得るでしょう。 この非標準機能は、 ... の出力をコピーして貼り付けることを可能にするために存在します(see Examining data and code)。


6 Forth Words


6.1 Notation

Forth のワードは、 以下にあるように、 Forth テキストのデファクトスタンダード(事実上の標準)となっている表記にて説明されます:

word     Stack effect   wordset   pronunciation

Description

word

ワード名。

Stack effect

スタック効果(stack effect)は、 before -- after という表記で記述されます。 ここで、beforeafter は、 ワード実行前と実行後のスタック・エントリの頂上部を表します。 スタックの残りの部分にはワードは触れません。 スタックの頂上は右端です。 つまり、 スタック・シーケンスはあなたが入力したとおりに書き込まれます。 注意: Gforth は別個の浮動小数点スタックを使用しますが、 統一されたスタック表記法を使用することに注意してください。 また、 リターンスタック効果は「スタック効果」には表示されませんが、 「説明」(Description)に表示されます。 スタック項目の名前は、 項目の型や機能を説明します。 型の説明については、 下記を参照してください。

すべてのワードには、 コンパイル・モードのスタック効果とインタープリター・モードのスタック効果という 2 つのスタック効果があります。 ほとんどのワードのコンパイル・モードのスタック効果は です。 ワードのコンパイル・モードのスタック効果がこの標準の振る舞いから逸脱している場合、 またはワードがコンパイル・モードに他の通常でない振る舞いを行う場合、 両方のスタック効果が表示されます。 それ以外の場合、 インタープリター・モードのスタック効果のみが表示されます。

また、 コード・テンプレートまたはサンプルでは、​​ その時点でのスタックの状態表示するコメントが括弧内にある場合があることにも注意してください。 これらのスタックの状態表示には -- はありません。 なぜなら前・後という状況が無いためです。

pronunciation

ワードの発音

wordset

ワードセット(wordset)は、 ワードが標準化されたものか、 または環境クエリ文字列であるか、 または Gforth 固有のワードであるかを指定します。 Gforth 固有のワードの場合、 ワードセット名には文字列 gforth が含まれており、 他のワードセット名は environment または標準のワードセット(standard word sets)を参照します。

Forth 標準はいくつかのワードセットに分かれています。 理論上、 標準システムはそれらすべてをサポートする必要はありませんが、 実際には、 組み込み用途な超小型のマシン以外の本格的なシステムは、 ほぼすべての標準化されたワードをサポートします(ただし、一部のシステムでは一部のワードセットを明示的にロードする必要があります)。 そのため、 実際のところはワードセットの使用をケチったところでその分移植性が上がる訳ではありません。

Gforth 固有のワードについては、 以下のカテゴリがあります:

gforth
gforth-<version>

我々はこのワードを Gforth で永続的にサポートするつもりであり、 Gforth <version> 以降で有効です(おそらくその <version> 時点ではサポートされていないワードです)。

gforth-experimental

このワードは現在のバージョンで使用できますが、 永続的なワードになるか、 Gforth の将来のリリースで削除されるか分かりません。 あなたのフィードバックをお待ちしています。

gforth-internal

このワードは内部用であり、 サポート対象のワードではないため、 Gforth の将来のリリースでは削除される可能性があります。

gforth-obsolete

このワードは、 Gforth の将来のリリースでは削除される予定です。

Description

そのワードの振る舞いを説明します。

スタック項目の型は、 以下のように、 スタック項目名のはじめの文字が何であるかによって指定されます:

f

二値フラグ(Boolean flags)。 つまり false または true

c

Char

w

セル。 整数(an integer)やアドレス(an address)も格納可能。

n

符号付き整数

u

符号なし整数

d

符号付き2倍長整数

ud

符号無し2倍長整数

r

浮動小数点数(Float)(FP スタック上に置かれる)

a-

セル・アライメント・アドレス

c-

文字(char)アライメント・アドレス(注意: Windows NT では文字(char)は 2 バイトになる場合があることに注意)

f-

浮動小数点数アライメントのアドレス

df-

IEEE倍精度浮動小数点数アライメントのアドレス

sf-

IEEE単精度浮動小数点数アライメントのアドレス

xt

実行トークン(Execution token)。 セルと同一サイズ

wid

ワード・リストID。セルと同一サイズ

ior, wior

セルサイズの入出力結果コード。 Gforth では iors を throw できます。

f83name

名前構造体へのポインター

"

(スタック上ではなく、)入力ストリーム内の文字列。 終端文字はデフォルトでは空白(a blank)です。 終端文字が空白でない場合は、 <> でクォートして表示します。


6.2 Case insensitivity

Gforth では英大文字小文字を区別しません(case-insensitive)。 大文字、 小文字、 または大文字と小文字の混合を使用して、 定義を入力したり標準のワードを呼び出したりできます(ただし、 こちらも参照ください see Implementation-defined options)

標準 Forth では、 標準のワードが完全に大文字で入力された場合にのみ実装が認識する必要があります。 したがって、 標準のプログラムでは、 すべての標準ワードに大文字を使用する必要があります。 あなたの定義したワードには大文字と小文字を自由に使用できますが、 標準のプログラムでは、 ワードを定義したときと同じ大文字と小文字で使用する必要があります。

Gforth は、 cs-wordlist (英大文字小文字を区別する(case-sensitive)ワード・リスト see Word Lists) を通じて英大文字小文字の区別をサポートします。

何人かは Gforth を英大文字小文字を区別する(case-sensitive)ように変換する方法を質問しました。 これは悪い考え(bad idea)だとは思いますが、 すべてのワードリストを以下のようなテーブルに変更できます:

' table-find forth-wordlist wordlist-map  !

注意: このように、 Gforth を英大文字小文字を区別する(case-sensitive)ように変換した場合、 定義済みのワードは、 定義したときと同一の大文字と小文字の組み合わせで入力する必要があることに注意してください。 大文字と小文字は異なります。 この操作を実行する前に、 それらをあなた好みの大文字と小文字の組み合わせに変換するとよいでしょう(その方法については説明しません。 Gforth を英大文字小文字を区別する(case-sensitive)ように変換することを実行することを検討している場合でも、 すでに定義済みワードのワード名をあなた好みの大文字と小文字の組み合わせに変換するその方法を知っているくらいの Forth システムの知識があったほうがよいと思います)。


6.3 Comments

Fors は 2 つのスタイルのコメントをサポートしています。 従来の行中コメントである ( と、 その現代的な親戚である、 行末までのコメント \ です。

( ( compilation ’ccc<close-paren>’ – ; run-time –  ) core,file “paren”

通常は次の ) までがコメントです。 「)」 が見つかるまで、 パース領域内の後続の文字をすべてパースして破棄します。 対話入力中は、 行末はコメント終了文字としても機能します。 ただし、 ファイル入力の場合はそうではありませんので、 「)」区切り文字のパース中にファイルの終わりに遭遇すると、 Gforth は警告を出します。

\ ( compilation ’ccc<newline>’ – ; run-time –  ) core-ext,block-ext “backslash”

行末までがコメントです。 ブロックからのロード中を除き、 パース領域内の残りの文字をすべてパースして破棄します。 ブロックからのロード中は、 1行64バイトと見なすので、 その 64 バイト行の残りの文字をすべてパースして破棄します。

\G ( compilation ’ccc<newline>’ – ; run-time –  ) gforth-0.2 “backslash-gee”

\ と同等ですが、 ドキュメントの定義コメントに注釈(annotate)を付けるためのタグとして使用されます。


6.4 Boolean Flags

ブール値フラグはセル・サイズです。 すべてのビットがクリアされているセルはフラグ false を表し、 すべてのビットがセットされているセルはフラグ true を表します。 フラグをチェックするワード (IF など) は、 セルの任意のビットがセットされているものを true として扱います。

true ( – f  ) core-ext “true”

定数 – f は全てのビットがセットされたセルです。

false ( – f  ) core-ext “false”

定数 – f は全てのビットがクリアされたセルです。

on ( a-addr –  ) gforth-0.2 “on”

指定の a-addr の変数の値を true にセットする。

off ( a-addr –  ) gforth-0.2 “off”

指定の a-addr の変数の値を false にセットする。

select ( u1 u2 f – u ) gforth-1.0 “select”

f が false なら u2u として返し、 そうでなければ u1u として返す。


6.5 Arithmetic

Forth の算術演算はチェックを行いません。 つまり、 加算または乗算での整数のオーバーフローについては問い詰められませんが、 運が良ければゼロによる除算については問い詰められることができるかもしれません。 演算子はオペランドの後に記述されますが、 オペランドは元の順序のままです。 つまり、 中置記法での 2-12 1 - に対応します。 Forth はさまざまな除算演算子を提供します。 あなたが、 潜在的に負になりうるオペランドを使用して除算を実行する場合、 実装により振る舞いが異なる //mod を使用せず、 たとえば、 /f/modffm/mod を使用します(see Integer division)。


6.5.1 Single precision

デフォルトでは、 Forth の数値は 1 セルのサイズの1倍長整数です。 扱い方に応じて、 符号付きまたは符号無しにすることができます。 1倍長整数を認識するためにテキスト・インタープリターで使用されるルールについては、 Number Conversion を参照してください。

これらのワードは、 すべて符号付きオペランドに対して定義されていますが、 一部のワードは符号無しの数値に対しても機能します: +, 1+, -, 1-, *

+ ( n1 n2 – n ) core “plus”
1+ ( n1 – n2 ) core “one-plus”
under+ ( n1 n2 n3 – n n2 ) gforth-0.3 “under-plus”

n3n1 に足し込む(n を得る)

- ( n1 n2 – n ) core “minus”
1- ( n1 – n2 ) core “one-minus”
* ( n1 n2 – n ) core “star”
negate ( n1 – n2 ) core “negate”
abs ( n – u ) core “abs”
min ( n1 n2 – n ) core “min”
max ( n1 n2 – n ) core “max”
umin ( u1 u2 – u ) gforth-0.5 “umin”
umax ( u1 u2 – u ) gforth-1.0 “umax”

6.5.2 Double precision

テキスト・インタープリターが2倍長整を認識するために使用するルールについては、 Number Conversion を参照してください。

数値は 2 の補数演算を使用する Gforth で表されるため、符号付き単精度浮動小数点数を (符号付き) 倍精度浮動小数点に変換するには、最上位セル全体で符号拡張が必要です。 倍精度整数はセルのペアで表され、 上位側のセルが TOS にあります。 符号無しの1倍長整数を2倍長整数に変換するのは簡単で、 0 を TOS にプッシュするだけです。 整数値は Gforth では 2 の補数表現を使うため、 符号付き1倍長整数を(符号付き)2倍長整数に変換するには、 上位側セル全体で符号拡張が必要です。 これは s>d を使用して実現できます。 このことから分かるとおり、 それが符号無し整数を表しているのか、 符号有り整数を表しているのか分からなければ、 数値を変換できないということです。

これらのワードはすべて符号付きオペランドに対して定義されていますが、 一部のワードは符号無しの数値に対しても機能します: d+d-

s>d ( n – d  ) core “s-to-d”
d>s ( d – n  ) double “d-to-s”
d+ ( ud1 ud2 – ud ) double “d-plus”
d- ( d1 d2 – d ) double “d-minus”
dnegate ( d1 – d2 ) double “d-negate”
dabs ( d – ud  ) double “d-abs”
dmin ( d1 d2 – d  ) double “d-min”
dmax ( d1 d2 – d  ) double “d-max”

6.5.3 Mixed precision

m+ ( d1 n – d2 ) double “m-plus”
m* ( n1 n2 – d ) core “m-star”
um* ( u1 u2 – ud ) core “u-m-star”

6.5.4 Integer division

以下にあるように、 除算を扱うために相当な数のワードがあることが分かります。 これらの主な違いは、 符号付き除算の処理にあります。 これらのワードはどのようにして符号付き除算に対応するのでしょうか? (U プレフィックスが付いたワードでは符号付き除算に対応しません)

商を四捨五入して整数に丸める場合、 負の無限大に向かって丸めるのでしょうか(floored division(床除算)、 接尾辞 F)、 それとも 0 に向かって丸めるのでしょうか(symmetric division、接尾辞 S)。 標準では、 ほとんどの標準ワード(/ mod /mod */ */mod m*/)について、 問題なのは、 各々の実装依存のままであり、 システムごとに異なる選択が行われています。 Gforth では、こ​​れらのワードをfloorとして実装します(Gforth 0.7 以降)。 floored division と symmetric division の違いは、 割られる数と割る数の符号が異なり、 かつ、 割られる数が割る数の倍数で無い場合のみです。 以下の表に組み合わせの結果を示します:

                      floored          symmetric
割られる  割る数      余り 商            余り 商
    10      7           3   1              3   1
   -10      7           4  -2             -3  -1
    10     -7          -4  -2              3  -1
   -10     -7          -3   1             -3   1

floored と symmetric とが違いを生む一般的なケースは、 様々な符号の被除数(割られる数) n1 が、 同一の正の除数(割る数) n2 で除算される場合です。 同一の正の除数(割る数) n2 で除算される場合、 通常は floored division が必要で、なぜなら、 同一の正の除数(割る数) n2 で除算される場合、 余りは常に正となり、 被除数(割られる数)に応じて符号が変化しないからです。 また、 floored division では、 n1 が n2 増加すると商は常に 1 増加しますが、 symmetric division では、 -n2<n1<n2 の場合は商は増加しません(この範囲では商は 0 です)。

いずれの場合でも、 floored と symmetric とで違いが生ずる数値を除算する場合は、 どのバリエーションが適切であるかを考えてから、 適切な接尾辞を付けた Gforth ワードか、 標準ワードの fm/mod または sm/rem のいずれかを使用する必要があります。

1倍長セル同士の除算:

/ ( n1 n2 – n  ) core “slash”

n=n1/n2

/s ( n1 n2 – n ) gforth-1.0 “slash-s”
/f ( n1 n2 – n ) gforth-1.0 “slash-f”
u/ ( u1 u2 – u ) gforth-1.0 “u-slash”
mod ( n1 n2 – n  ) core “mod”

n は n1/n2 の余り(modulus)

mods ( n1 n2 – n ) gforth-1.0 “mod-s”
modf ( n1 n2 – n ) gforth-1.0 “modf”
umod ( u1 u2 – u ) gforth-1.0 “umod”
/mod ( n1 n2 – n3 n4  ) core “slash-mod”

n1/n2 を行い、 n3 が余り(modulus)、 n4 が商です(n1=n2*n4+n3)。

/mods ( n1 n2 – n3 n4 ) gforth-1.0 “slash-mod-s”

n3 が余り(remainder)、 n4 が商

/modf ( n1 n2 – n3 n4 ) gforth-1.0 “slash-mod-f”

n3 が余り(modulus)、 n4 が商

u/mod ( u1 u2 – u3 u4 ) gforth-1.0 “u-slash-mod”

u3 が余り(modulus)、 u4 が商

2倍長セルを1倍長セルで割って1倍長セルの結果を得る; これらのワードは、 一部のアーキテクチャー(AMD64 など)では上記ワードとほぼ同じ速度ですが、 他のアーキテクチャ(さまざまな Aarch64 CPU など)でははるかに遅くなります。

fm/mod ( d1 n1 – n2 n3 ) core “f-m-slash-mod”

Floored division: d1 = n3*n1+n2, n1>n2>=0 or 0>=n2>n1.

sm/rem ( d1 n1 – n2 n3 ) core “s-m-slash-rem”

Symmetric division: d1 = n3*n1+n2, sign(n2)=sign(d1) or 0.

um/mod ( ud u1 – u2 u3 ) core “u-m-slash-mod”

ud=u3*u1+u2, 0<=u2<u1

du/mod ( d u – n u1 ) gforth-1.0 “du-slash-mod”

d=n*u+u1, 0<=u1<u; PolyForth スタイルの混合除算

*/ ( ( n1 n2 n3 – n4  ) core “star-slash”

n4=(n1*n2)/n3 中間結果は2倍長

*/s ( n1 n2 n3 – n4 ) gforth-1.0 “star-slash-s”

n4=(n1*n2)/n3 中間結果は2倍長

*/f ( n1 n2 n3 – n4 ) gforth-1.0 “star-slash-f”

n4=(n1*n2)/n3 中間結果は2倍長

u*/ ( u1 u2 u3 – u4 ) gforth-1.0 “u-star-slash”

u4=(u1*u2)/u3 中間結果は2倍長

*/mod ( n1 n2 n3 – n4 n5  ) core “star-slash-mod”

n1*n2=n3*n5+n4 中間結果(n1*n2)は2倍長、 n4 は剰余、 n5 は商。

*/mods ( n1 n2 n3 – n4 n5 ) gforth-1.0 “star-slash-mod-s”

n1*n2=n3*n5+n4, 中間結果(n1*n2)は2倍長、 n4 は余り(remainder)、 n5 は商

*/modf ( n1 n2 n3 – n4 n5 ) gforth-1.0 “star-slash-mod-f”

n1*n2=n3*n5+n4 中間結果(n1*n2)は2倍長。 n4 余り(modulus)、n5 は商

u*/mod ( u1 u2 u3 – u4 u5 ) gforth-1.0 “u-star-slash-mod”

u1*u2=u3*u5+u4 中間結果(u1*u2)は2倍長。

除算結果を2倍長セル(double-cell)で得ます。 以下のワード群は上記のワード群よりもはるかに遅いです。

ud/mod ( ud1 u2 – urem udquot  ) gforth-0.2 “ud/mod”

符号無し2倍長 ud1u2 で割る。 結果は、符号なし2倍長の商 udquot と、 1倍長の余り(remainder) urem です。

m*/ ( d1 n2 u3 – dquot  ) double “m-star-slash”

dquot=(d1*n2)/u3, 中間結果は3倍長です。 ANS Forth では u3 は正の符号付き数値のみです。

環境クエリ(environmental query) floored を使用すると、 / mod /mod */ */mod m*/ が floored division か symmetric division かを確認できます(see Environmental Queries)。

整数除算ワードのもう 1 つの側面は、 整数除算ワードのほとんどがオーバーフローする可能性があり、 かつ、 ゼロによる除算が数学的に定義されていないことです。 これらの条件のいずれかに該当した場合に何が起こるかは、 エンジンやハードウェアやオペレーティング・システムによって異なります。 gforth エンジンは、適切なエラーである -10 (Division by zero;ゼロ除算) または -11 (Result out of range;範囲外の結果) を throw しようと懸命に試みます、 が、 しかし、 一部のプラットフォームでは -55(浮動小数点未確認エラー;Floating-point unidentified fault)が throw されます。 gforth-fast エンジンでは、 不適切な throw コード(およびエラー・メッセージ)を生成する場合や、 エラーを生成せずに嘘の値のみを生成する場合があります。 つまり、 あなたは、 そのような条件が throw されることに賭けるべきではありません。 そして、 あなたが迅速なデバッグを行うためには、 gforth エンジンは gforth-fast エンジンよりも多くのエラーをキャッチし、 より正確なエラーを生成します。


6.5.5 Two-stage integer division

(Two-stage integer divison;2段階除算)ほとんどのハードウェアでは、 乗算は除算よりも大幅に高速です。 したがって、 多くの数値を同じ除数(割る数)で除算する必要がある場合は、 通常、 除数の逆数を一度求めて、 その逆数を数値に乗算する方が高速です。 整数の場合、 これは技巧的になってしまうため、 Gforth ではこの作業をこのセクションで説明するワード群にパッケージ化しています。

以下の例から始めるとしましょう。 あなたがセルの配列のすべての要素を同じ数値 n で除算したいとします。 これを素直に実装すると以下のようになります:

: array/ ( addr u n -- )
  -rot cells bounds u+do
    i @ over / i !
  1 cells +loop
  drop ;

より効率的なバージョンは以下のようになります:

: array/ ( addr u n -- )
  {: | reci[ staged/-size ] :}
  reci[ /f-stage1m
  cells bounds u+do
    i @ reci[ /f-stage2m i !
  1 cells +loop ;

この例では、 まず、 逆数データを格納するためのサイズ staged/-size のローカル・バッファー reci[ を作成します。 次に、 /f-stage1mn の逆数を計算し、 reci[ に格納します。 最後に、 ループ内で /f-stage2mreci[ のデータを使用して除算の商を計算します。

これにはいくつかの制限があります。 /f-stage1m では正の除数のみがサポートされます。 u/-stage1m には 2 以上の数を使用できます。 サポートされていない除数を使用しようとすると、 エラーが発生します。 floored 第2ステージ・ワードの相互(reciprocal)バッファーは /f-stage1m で初期化し、 符号な無しの第2ステージ・ワードの相互バッファーは u/-stage1m で初期化する必要があります。 最初のステージと2番目のステージの間で相互バッファーを変更してはなりません。 基本的に、 これはメモリー・バッファーとして扱うのではなく、 最初のステージでのみ変更可能なものとして扱います。 このルールのポイントは、 Gforth の将来のバージョンではこのバッファーのエイリアスが考慮されないということです。

これらのワードが以下です:

staged/-size ( – u  ) gforth-1.0 “staged-slash-size”

u/-stage1m または /f-stage1m のバッファーのサイズ。

/f-stage1m ( n addr-reci –  ) gforth-1.0 “slash-f-stage1m”

n の逆数を計算し、 サイズ staged/-size のバッファー addr-reci に格納します。 n<1 の場合、 エラーを出します(throw)。

/f-stage2m ( n1 a-reci – nquotient ) gforth-1.0 “slash-f-stage2m”

Nquotient は、n1a-reci で表される除数で除算した結果であり、 /f-stage1m によって計算されます。

modf-stage2m ( n1 a-reci – umodulus ) gforth-1.0 “mod-f-stage2m”

Umodulus は、 n1a-reci で表される除数で割った余り(remainder)で、 /f-stage1m によって計算されます。

/modf-stage2m ( n1 a-reci – umodulus nquotient ) gforth-1.0 “slash-mod-f-stage2m”

Nquotient は商で、 umodulusn1a-reci で表される除数で割った余り(remainder)で、 /f-stage1m によって計算されます。

u/-stage1m ( u addr-reci –  ) gforth-1.0 “u-slash-stage1m”

u の逆数を計算し、 サイズ staged/-size のバッファー addr-reci に格納します。 u<2 の場合、 エラーを出します(throw)。

u/-stage2m ( u1 a-reci – uquotient ) gforth-1.0 “u-slash-stage2m”

Uquotient は、 u1a-reci で表される除数で除算した結果であり、 u/-stage1m によって計算されます。

umod-stage2m ( u1 a-reci – umodulus ) gforth-1.0 “u-mod-stage2m”

Umodulus は、 u1a-reci で表される除数で割った余り(remainder)で、u/-stage1m によって計算されます。

u/mod-stage2m ( u1 a-reci – umodulus uquotient ) gforth-1.0 “u-slash-mod-stage2m”

Uquotient は商で、 umodulusa-reci で表される除数で u1 を割った余り(remainder)で、u/-stage1m によって計算されます。

Gforth は現在、 段階的対称除算(staged symmetrical division)をサポートしていません。

staged/-divisor @ を使用すると、 逆数(のアドレス)から除数を復旧(recover)できます:

staged/-divisor ( addr1 – addr2  ) gforth-1.0 “staged-slash-divisor”

Addr1 は逆数のアドレス、 addr2 は逆数の計算元となった除数を含むアドレスです。

これは、Gforth の逆コンパイラー出力を確認するときに役立ちます。 定数による除算は、 多くの場合、 逆数のアドレスとその後に続く第2ステージ・ワードを含むリテラルにコンパイルされます。

これらのワードを使用した場合のパフォーマンスへの影響は、 アーキテクチャー(ハードウェア除算があるかどうか)と、 特定の実装(ハードウェア除算の速さはどれくらいか)に大きく依存しますが、 これらのワードの相対的なパフォーマンスについてのアイデアを提供するために、 以下を示します。 2 つの AMD64 実装でのマイクロ・ベンチマークの反復ごとのサイクルを以下に示します。 norm 列は通常の除算ワード(例: u/)を示し、stg2 列は対応する stage2 ワード(例: u/-stage2m)を示します:

intel Skylake       AMD Zen2
norm stg2           norm stg2
41.3 15.8 u/        35.2 21.4 u/
39.8 19.7 umod      36.9 25.8 umod
44.0 25.3 u/mod     43.0 33.9 u/mod
48.7 16.9 /f        36.2 22.5 /f
47.9 20.5 modf      37.9 27.1 modf
53.0 24.6 /modf     45.8 35.4 /modf
    227.2 u/stage1      101.9 u/stage1
    159.8 /fstage1       97.7 /fstage1

6.5.6 Bitwise operations

and ( w1 w2 – w ) core “and”
or ( w1 w2 – w ) core “or”
xor ( w1 w2 – w ) core “x-or”
invert ( w1 – w2 ) core “invert”
mux ( u1 u2 u3 – u ) gforth-1.0 “mux”

multiplex(多重化): u3 の各ビットについて、 そのビットが 1 の場合は u1 から対応するビットを選択し、 それ以外の場合は u2 から対応するビットを選択します。 たとえば、 %0011 %1100 %1010 mux%0110 となります。

lshift ( u1 u – u2 ) core “l-shift”

u1u ビット左シフトします。

rshift ( u1 u – u2 ) core “r-shift”

u1 (セル) を u ビットだけ右にシフトし、 シフトインされたビットを 0 で埋めます(論理/符号無しシフトです)。

arshift ( n1 u – n2 ) gforth-1.0 “ar-shift”

n1 (セル) を u ビット右にシフトし、n1 の符号ビットからシフトインされたビットを埋めます (算術シフト)。

dlshift ( ud1 u – ud2 ) gforth-1.0 “dlshift”

ud1 (2倍長セル) を u ビット左にシフトします。

drshift ( ud1 u – ud2 ) gforth-1.0 “drshift”

ud1 (2倍長セル) を u ビットだけ右にシフトし、 シフトインされたビットを 0 で埋めます (論理/符号なしシフト)。

darshift ( d1 u – d2 ) gforth-1.0 “darshift”

d1 (2倍長セル) を u ビット右にシフトし、d1 の符号ビットからシフトインされたビットで埋めます (算術シフト)。

2* ( n1 – n2 ) core “two-star”

1 つ左にシフトします。符号なしの数値でも機能します

2/ ( n1 – n2 ) core “two-slash”

1 つ右に算術シフトします。 符号付き数値の場合、 これは 2 による floored division になります(/ は必ずしも floors ではないことに注意してください)。

d2* ( d1 – d2 ) double “d-two-star”

2倍長セルを左に 1 シフトします。 符号なしの数値でも機能します

d2/ ( d1 – d2 ) double “d-two-slash”

1 つ右に算術シフトします。 符号付き数値の場合、これは 2 による floored division になります。

>pow2 ( u1 – u2 ) gforth-1.0 “to-pow2”

u2 は、u2>=u1 の最小の 2 のべき乗数です。

log2 ( u – n ) gforth-1.0 “log2”

Nu の切り捨て2進対数、 つまり最初に設定されたビットのインデックスです。 u=0 の場合は n=-1 です。

pow2? ( u – f  ) gforth-1.0 “pow-two-query”

f は、 u が 2 の累乗の場合、 つまり u のビットが 1 つだけセットされている場合に true になります。

ctz ( x – u  ) gforth-1.0 “c-t-z”

x の2進数表現で末尾からのゼロの数を数える

他のほとんどの操作とは異なり、 幅の狭いユニットのローテートと幅の広いユニットのローテートを簡単に合成することはできないため、 1倍長セル幅および 2倍幅のセル幅のローテート操作を使用すると、 結果がセル幅に依存することになります。 公開されたアルゴリズムまたはセル幅に依存しない結果の場合、 通常は固定幅のローテート操作を使用する必要があります。

wrol ( u1 u – u2 ) gforth-1.0 “wrol”

u1 の下位側 16 ビットを u ビットだけ左に回転し、 他のビットを 0 にセットします。

wror ( u1 u – u2 ) gforth-1.0 “wror”

u1 の下位側 16 ビットを u ビットだけ右回転し、 他のビットを 0 にセットします。

lrol ( u1 u – u2 ) gforth-1.0 “lrol”

u1 の下位側 32 ビットを u ビットだけ左に回転し、 他のビットを 0 にセットします。

lror ( u1 u – u2 ) gforth-1.0 “lror”

u1 の下位側 32 ビットを u ビットだけ右回転し、 他のビットを 0 にセットします。

rol ( u1 u – u2 ) gforth-1.0 “rol”

u1 のすべてのビットを u ビットだけ左に回転します。

ror ( u1 u – u2 ) gforth-1.0 “ror”

u1 のすべてのビットを u ビットだけ右回転します。

drol ( ud1 u – ud2 ) gforth-1.0 “drol”

ud1 (2倍長セル) のすべてのビットを u ビットだけ左に回転します。

dror ( ud1 u – ud2 ) gforth-1.0 “dror”

ud1 (2倍長セル) のすべてのビットを u ビット右に回転します。


6.5.7 Numeric comparison

注意: 等しいかどうかを比較するワード(= <> 0= 0<> d= d<> d0= d0<>) は、 符号付き数値と符号なし数値の両方に対して機能することに注意してください。

< ( n1 n2 – f ) core “less-than”
<= ( n1 n2 – f ) gforth-0.2 “less-or-equal”
<> ( n1 n2 – f ) core-ext “not-equals”
= ( n1 n2 – f ) core “equals”
> ( n1 n2 – f ) core “greater-than”
>= ( n1 n2 – f ) gforth-0.2 “greater-or-equal”
0< ( n – f ) core “zero-less-than”
0<= ( n – f ) gforth-0.2 “zero-less-or-equal”
0<> ( n – f ) core-ext “zero-not-equals”
0= ( n – f ) core “zero-equals”
0> ( n – f ) core-ext “zero-greater-than”
0>= ( n – f ) gforth-0.2 “zero-greater-or-equal”
u< ( u1 u2 – f ) core “u-less-than”
u<= ( u1 u2 – f ) gforth-0.2 “u-less-or-equal”
u> ( u1 u2 – f ) core-ext “u-greater-than”
u>= ( u1 u2 – f ) gforth-0.2 “u-greater-or-equal”
within ( u1 u2 u3 – f ) core-ext “within”

u2<u3 かつ u1 が [u2,u3) にある(u2 < u3 and u2 <= u1 < u3 )、 または u2 >= u3 かつ u1 が [u3,u2) にない( u2 >= u3 and (u1 not in (u3 <= and < u2)。 これは、 符号なしの数値と符号付きの数値に対して機能します(ただし、 混ぜてはいけません)。 このワードについて考えるもう 1 つの方法は、 数値を循環として考えることです(符号なしの数値の場合は max-u から 0 まで、 符号付きの数値の場合は max-n から min-n まで循環します)。ここで、 u2 から u3 までの増加する数値(u3 を除く)の範囲を検討します(u2=u3 の場合は空の範囲を与えます)。 u1 がこの範囲内にある場合、 within は true を返します。

d< ( d1 d2 – f ) double “d-less-than”
d<= ( d1 d2 – f ) gforth-0.2 “d-less-or-equal”
d<> ( d1 d2 – f ) gforth-0.2 “d-not-equals”
d= ( d1 d2 – f ) double “d-equals”
d> ( d1 d2 – f ) gforth-0.2 “d-greater-than”
d>= ( d1 d2 – f ) gforth-0.2 “d-greater-or-equal”
d0< ( d – f ) double “d-zero-less-than”
d0<= ( d – f ) gforth-0.2 “d-zero-less-or-equal”
d0<> ( d – f ) gforth-0.2 “d-zero-not-equals”
d0= ( d – f ) double “d-zero-equals”
d0> ( d – f ) gforth-0.2 “d-zero-greater-than”
d0>= ( d – f ) gforth-0.2 “d-zero-greater-or-equal”
du< ( ud1 ud2 – f ) double-ext “d-u-less-than”
du<= ( ud1 ud2 – f ) gforth-0.2 “d-u-less-or-equal”
du> ( ud1 ud2 – f ) gforth-0.2 “d-u-greater-than”
du>= ( ud1 ud2 – f ) gforth-0.2 “d-u-greater-or-equal”

6.5.8 Floating Point

浮動小数点数を認識するためにテキスト・インタープリターで使用されるルールについては、 Number Conversion を参照してください。

Gforth には別個の浮動小数点スタックがありますが、 ドキュメントでは一緒に統一した表記が使用されています9

浮動小数点数は、 不注意な人にとっては多くの不快な驚きをもたらします(たとえば、浮動小数点の加算は結合的(associative)ではありません)。 また、 用心深い人にとってもいくつかの不快な驚きさえあります。 自分が何をしているのか理解していない場合、 または得られる結果が完全に偽物であることを気にしない場合を除き、 これらを使用すべきではありません。 浮動小数点数の問題(およびその回避方法)について知りたい場合は、 David Goldberg, What Every Computer Scientist Should Know About Floating-Point Arithmetic, ACM Computing Surveys 23(1):5−48, March 1991 から始めると良いでしょう(訳注: https://docs.oracle.com/cd/E19957-01/806-4847/ncg_goldberg.html これが合ってるかどうか不明。一部文字化けあり2024/06現在)。

整数と浮動小数点の間の変換:

s>f ( n – r ) floating-ext “s-to-f”
d>f ( d – r ) floating “d-to-f”
f>s ( r – n ) floating-ext “f-to-s”
f>d ( r – d ) floating “f-to-d”

算術演算:

f+ ( r1 r2 – r3 ) floating “f-plus”
f- ( r1 r2 – r3 ) floating “f-minus”
f* ( r1 r2 – r3 ) floating “f-star”
f/ ( r1 r2 – r3 ) floating “f-slash”
fnegate ( r1 – r2 ) floating “f-negate”
fabs ( r1 – r2 ) floating-ext “f-abs”
fcopysign ( r1 r2 – r3  ) gforth-1.0 “fcopysign”

r3 は r1 から絶対値を取得し r2 から符号を取得します

fmax ( r1 r2 – r3 ) floating “f-max”
fmin ( r1 r2 – r3 ) floating “f-min”
floor ( r1 – r2 ) floating “floor”

次に小さい整数値に向かって丸めます。 つまり、 負の無限大に向かって丸めます。

fround ( r1 – r2 ) floating “f-round”

最も近い整数値に丸めます(訳注: 0.5e – 0, 0.50e – 0, 0.51e – 1, 1.5e – 2, ... ??)

ftrunc ( r1 – r2  ) floating-ext “f-trunc”

0 に向かって丸める(正数でも負数でも)

f** ( r1 r2 – r3 ) floating-ext “f-star-star”

r3r1r2 乗です

fsqrt ( r1 – r2 ) floating-ext “f-square-root”
fexp ( r1 – r2 ) floating-ext “f-e-x-p”
fexpm1 ( r1 – r2 ) floating-ext “f-e-x-p-m-one”

r2=e**r1−1

fln ( r1 – r2 ) floating-ext “f-l-n”
flnp1 ( r1 – r2 ) floating-ext “f-l-n-p-one”

r2=ln(r1+1)

flog ( r1 – r2 ) floating-ext “f-log”

常用対数(decimal logarithm)

falog ( r1 – r2 ) floating-ext “f-a-log”

r2=10**r1

f2* ( r1 – r2  ) gforth-0.2 “f2*”

r1 に 2.0e0 を掛けた値

f2/ ( r1 – r2  ) gforth-0.2 “f2/”

r1 に 0.5e0 を掛けた値

1/f ( r1 – r2  ) gforth-0.2 “1/f”

1.0e0 を r1 で割った値

ベクトル演算:

v* ( f-addr1 nstride1 f-addr2 nstride2 ucount – r ) gforth-0.5 “v-star”

ドット積(dot-product): r=v1*v2 v1 の最初の要素は f_addr1 にあり、 次の要素は f_addr1+nstride1 というようになります(v2 も同様)。 どちらのベクトルにも ucount の数の要素があります。

faxpy ( ra f-x nstridex f-y nstridey ucount – ) gforth-0.5 “faxpy”

vy=ra*vx+vy

浮動小数点演算の角度はラジアン(radians)で指定します(完全な円は 2πラジアンです)。

fsin ( r1 – r2 ) floating-ext “f-sine”
fcos ( r1 – r2 ) floating-ext “f-cos”
fsincos ( r1 – r2 r3 ) floating-ext “f-sine-cos”

r2=sin(r1), r3=cos(r1)

ftan ( r1 – r2 ) floating-ext “f-tan”
fasin ( r1 – r2 ) floating-ext “f-a-sine”
facos ( r1 – r2 ) floating-ext “f-a-cos”
fatan ( r1 – r2 ) floating-ext “f-a-tan”
fatan2 ( r1 r2 – r3 ) floating-ext “f-a-tan-two”

r1/r2=tan(r3) ANS Forth ではそこまで求められてはいないのですが、 おそらくこれが fsincos の逆になることを意図していて、 gforth ではそのようになっています。

fsinh ( r1 – r2 ) floating-ext “f-cinch”
fcosh ( r1 – r2 ) floating-ext “f-cosh”
ftanh ( r1 – r2 ) floating-ext “f-tan-h”
fasinh ( r1 – r2 ) floating-ext “f-a-cinch”
facosh ( r1 – r2 ) floating-ext “f-a-cosh”
fatanh ( r1 – r2 ) floating-ext “f-a-tan-h”
pi ( – r  ) gforth-0.2 “pi”

Fconstant(定数) – r は値 pi です(π)。 円の面積と直径の比率。

浮動小数点演算に関する特別な問題の 1 つは、 等価性の比較が、 成功するはずなのに失敗することがよくあることです。 このため、 多くの場合、 近似的等価性が好まれます(ただし、 自分が何をしているのかを理解しておく必要があります)。 また、 IEEE NaN の比較があなたの予想とは異なる場合があることにも注意してください。 比較ワードは以下のとおりです:

f~rel ( r1 r2 r3 – flag  ) gforth-0.5 “f~rel”

相対誤差を含む近似等価性: |r1-r2|<r3*|r1+r2|

f~abs ( r1 r2 r3 – flag  ) gforth-0.5 “f~abs”

Approximate equality with absolute error: |r1-r2|<r3.< 絶対誤差を伴う近似等価性: |r1-r2|<r3

f~ ( r1 r2 r3 – flag  ) floating-ext “f-proximate”

r1 と r2 が等しいかどうかを比較するための ANS Forth ワードごたまぜ: r3>0 なら f~abs; r3=0 なら ビット単位の比較; r3<0 なら fnegate f~rel

f= ( r1 r2 – f ) gforth-0.2 “f-equals”
f<> ( r1 r2 – f ) gforth-0.2 “f-not-equals”
f< ( r1 r2 – f ) floating “f-less-than”
f<= ( r1 r2 – f ) gforth-0.2 “f-less-or-equal”
f> ( r1 r2 – f ) gforth-0.2 “f-greater-than”
f>= ( r1 r2 – f ) gforth-0.2 “f-greater-or-equal”
f0< ( r – f ) floating “f-zero-less-than”
f0<= ( r – f ) gforth-0.2 “f-zero-less-or-equal”
f0<> ( r – f ) gforth-0.2 “f-zero-not-equals”
f0= ( r – f ) floating “f-zero-equals”
f0> ( r – f ) gforth-0.2 “f-zero-greater-than”
f0>= ( r – f ) gforth-0.2 “f-zero-greater-or-equal”

IEEE754 の特別な値は、 たとえばゼロで除算することによって導出できます。 最も一般的なものは、使いやすいように浮動小数点定数として定義されています。

infinity ( – r  ) gforth-1.0 “infinity”

浮動小数点数 (正の)無限大(floating point infinity)

-infinity ( – r  ) gforth-1.0 “-infinity”

浮動小数点数 負の無限大(-infinity)

NaN ( – r  ) gforth-1.0 “NaN”

浮動小数点数 NaN(Not a Number)


6.6 Stack Manipulation

Gforth は、 いくつかの個別のスタックを維持します:

  • データ・スタック(「パラメーター・スタック」とも呼ばれます) – 文字(characters)と、 セル(cells)と、 アドレス(addresses)と、 2倍長セル(double cells) 用
  • 浮動小数点スタック – 浮動小数点 (FP) 数を保持します。
  • リターン・スタック – コロン定義およびその他の(FP 以外の)データのリターン・アドレスを保持します。
  • ローカル・スタック – ローカル変数を保持します。

6.6.1 Data stack

drop ( w – ) core “drop”
nip ( w1 w2 – w2 ) core-ext “nip”
dup ( w – w w ) core “dupe”
over ( w1 w2 – w1 w2 w1 ) core “over”
third ( w1 w2 w3 – w1 w2 w3 w1 ) gforth-1.0 “third”
fourth ( w1 w2 w3 w4 – w1 w2 w3 w4 w1 ) gforth-1.0 “fourth”
tuck ( w1 w2 – w2 w1 w2 ) core-ext “tuck”
swap ( w1 w2 – w2 w1 ) core “swap”
pick ( S:... u – S:... w ) core-ext “pick”

実際のスタック効果は x0 ... xu u -- x0 ... xu x0 です。

rot ( w1 w2 w3 – w2 w3 w1 ) core “rote”
-rot ( w1 w2 w3 – w3 w1 w2 ) gforth-0.2 “not-rote”
?dup ( w – S:... w ) core “question-dupe”

実際のスタック効果は次のとおりです: ( w -- 0 | w w ) つまり w がゼロ以外の場合、 dup が実行されます。

roll ( x0 x1 .. xn n – x1 .. xn x0  ) core-ext “roll”
2drop ( w1 w2 – ) core “two-drop”
2nip ( w1 w2 w3 w4 – w3 w4 ) gforth-0.2 “two-nip”
2dup ( w1 w2 – w1 w2 w1 w2 ) core “two-dupe”
2over ( w1 w2 w3 w4 – w1 w2 w3 w4 w1 w2 ) core “two-over”
2tuck ( w1 w2 w3 w4 – w3 w4 w1 w2 w3 w4 ) gforth-0.2 “two-tuck”
2swap ( w1 w2 w3 w4 – w3 w4 w1 w2 ) core “two-swap”
2rot ( w1 w2 w3 w4 w5 w6 – w3 w4 w5 w6 w1 w2 ) double-ext “two-rote”

6.6.2 Floating point stack

fdrop ( r – ) floating “f-drop”
fnip ( r1 r2 – r2 ) gforth-0.2 “f-nip”
fdup ( r – r r ) floating “f-dupe”
fover ( r1 r2 – r1 r2 r1 ) floating “f-over”
fthird ( r1 r2 r3 – r1 r2 r3 r1 ) gforth-1.0 “fthird”
ffourth ( r1 r2 r3 r4 – r1 r2 r3 r4 r1 ) gforth-1.0 “ffourth”
ftuck ( r1 r2 – r2 r1 r2 ) gforth-0.2 “f-tuck”
fswap ( r1 r2 – r2 r1 ) floating “f-swap”
fpick ( f:... u – f:... r ) gforth-0.4 “fpick”

実際のスタック効果は r0 ... ru u -- r0 ... ru r0

frot ( r1 r2 r3 – r2 r3 r1 ) floating “f-rote”
f-rot ( r1 r2 r3 – r3 r1 r2 ) floating “f-not-rote”

6.6.3 Return stack

Forth システムは、 リターン・スタックにローカル変数を保持することができます。 通常、 ローカル変数を使用すると、 リターン・スタックを明示的に使用する必要がなくなるため、 これは合理的です。 したがって、 標準に準拠したプログラムを作成する場合で、 ワード内でローカル変数を使用している場合は、 そのワード内でのリターン・スタック操作のことは忘れてください(正確なルールについては標準ドキュメントを参照してください)。

>r ( w – R:w ) core “to-r”
r> ( R:w – w ) core “r-from”
r@ ( – w ; R: w – w  ) core “r-fetch”
rdrop ( R:w – ) gforth-0.2 “rdrop”
2>r ( w1 w2 – R:w1 R:w2 ) core-ext “two-to-r”
2r> ( R:w1 R:w2 – w1 w2 ) core-ext “two-r-from”
2r@ ( R:w1 R:w2 – R:w1 R:w2 w1 w2 ) core-ext “two-r-fetch”
2rdrop ( R:w1 R:w2 – ) gforth-0.2 “two-r-drop”
n>r ( x1 .. xn n – r:xn..x1 r:n  ) tools-ext “n-to-r”
nr> ( r:xn..x1 r:n – x1 .. xn n  ) tools-ext “n-r-from”

6.6.4 Locals stack

Gforth で追加のローカル・スタックを使用します。 これは、 その存在理由を含めて Locals implementation にて説明されています。


6.6.5 Stack pointer manipulation

sp0 ( – a-addr  ) gforth-0.4 “sp0”

ユーザー変数 – データ・スタック・ポインターの初期値。

sp@ ( S:... – a-addr ) gforth-0.2 “sp-fetch”
sp! ( a-addr – S:... ) gforth-0.2 “sp-store”
fp0 ( – a-addr  ) gforth-0.4 “fp0”

ユーザー変数 – 浮動小数点スタック・ポインターの初期値。

fp@ ( f:... – f-addr ) gforth-0.2 “fp-fetch”
fp! ( f-addr – f:... ) gforth-0.2 “fp-store”
rp0 ( – a-addr  ) gforth-0.4 “rp0”

ユーザー変数 – リターン・スタック・ポインターの初期値。

rp@ ( – a-addr ) gforth-0.2 “rp-fetch”
rp! ( a-addr – ) gforth-0.2 “rp-store”
lp0 ( – a-addr  ) gforth-0.4 “lp0”

ユーザー変数 – ローカル・スタック・ポインターの初期値。

lp@ ( – c-addr ) gforth-0.2 “lp-fetch”

C_addr は、 ローカル・スタック・ポインターの現在の値です。

lp! ( c-addr – ) gforth-internal “lp-store”

6.7 Memory

標準 Forth のメモリー割り当てワードに加えて ガベージ・コレクター(garbage collector) もあります。


6.7.1 Memory model

標準 Forth は、 Forth システムが複数のアドレス空間で構成されているとみなします。 そのうちの「データ空間」(data space)のみを管理し、 メモリー・ワードでアクセスできます。 メモリーには、 スタックと、 コード(コード空間(code space))と呼ばれる)と、 ヘッダー(名前空間(name space)と呼ばれる)とが含まれ、 それらは必ずしもデータ空間にある必要はありません。 Gforth ではすべてがデータ空間内にありますが、 プリミティブのコードは通常読み取り専用です。

データ空間は、 いくつかの領域に分割されます。 ディクショナリー(dictionary)10と、 ヒープと、 システムによって割り当てられた多数のバッファーから成ります。

Gforth は 1 つの大きなアドレス空間を提供し、 その任意のアドレス間でアドレス演算を実行できます。 ただし、 ディクショナリーではヘッダーまたはコードがデータと代わる代わる出てくるため(interleaved)、 連続するデータ空間領域は、 標準 Forth で連続していると記述されているものだけがほとんどです。 しかし、 連続する領域間であっても、 増加するアドレス方向にディクショナリーが確実に割り当てられます。 ヒープ内でのメモリー割り当ての順序はプラットフォームに依存します(また、実行ごとに異なる可能性もあります)。


6.7.2 Dictionary allocation

ディクショナリーの割り当てはスタック指向の割り当てスキーム(stack-oriented allocation scheme)です。 つまり、 X の割り当てを解除したい場合は、 X の後に割り当てられたすべての割り当ても解除します。

以下のワード達を使用した割り当ては連続しており、 アドレスの増加方向に向けて領域が拡張されます。 あらゆる種類のディクショナリー・メモリーを割り当てる他のワード(つまり、 :noname を含む定義ワード)は、 連続領域(contiguous region)を終了し、 新しい領域を開始します。

標準 Forth では、 create されたワードのみが、 後続の連続領域の開始となるアドレスを生成することが保証されています。 特に、 variable によって割り当てられたセルが、 後続の allot で割り当てられたメモリーと連続していることは保証されません。

allot に負の引数を指定して使用すると、 メモリーの割り当てを解除できます(いくつかの制限があります。 allot を参照してください)。 大規模な割り当て解除の場合は、 marker を使用します。

here ( – addr  ) core “here”

データ空間内の次の空き位置のアドレスを返します。

unused ( – u  ) core-ext “unused”

here でアドレス指定された領域(以降)に残っている空き領域の量をアドレス単位(address units)で返します。

allot ( n –  ) core “allot”

初期化せずに、 データ空間に n アドレス単位を予約します。 n は符号付きの数値で、 負の n を渡すとメモリーが解放されます。 ANS Forth では、この方法で現在の連続領域からメモリーの割り当てを解除することしかできません。 Gforth では、この方法で名前付きワード以外のあらゆるものを割り当て解除できます。 システムはこの制限をチェックしません。

->here ( addr –  ) gforth-1.0 “to-here”

here の値を addr に変更します。

c, ( c –  ) core “c-comma”

文字(char)用に1つのデータ空間を予約し、 その空間に c を格納します。

f, ( f –  ) gforth-0.2 “f,”

浮動小数点数(floating-point number)用の1つのデータ空間を予約し、 その空間に f を格納します。

, ( w –  ) core “comma”

セル(cell)用の1つのデータ空間を予約し、 その空間に w を格納します。

2, ( w1 w2 –  ) gforth-0.2 “2,”

2つのセル用のデータ空間を予約し、 そこに w1 w2 格納します。 最初(低位アドレス側)に w2 を格納します。

w, ( w –  ) gforth-1.0 “w-comma”
l, ( l –  ) gforth-1.0 “l-comma”
x, ( x –  ) gforth-1.0 “x-comma”
xd, ( xd –  ) gforth-1.0 “x-d-comma”
A, ( addr –  ) gforth-0.2 “A,”

1 つのセル用のデータ空間を予約し、 そこに addr を格納します。 私達のクロス・コンパイラーの場合には、 再配置可能なイメージに必要な型情報(type information)を提供します。 ただし、 通常では、 これは , と同等です。

mem, ( addr u –  ) gforth-0.6 “mem,”

save-mem-dict ( addr1 u – addr2 u ) \ 訳注:文字列をhereからのディクショナリーに書き込み、その(ディクショナリー上の)文字列を返します

メモリー・アクセスはアライメントする必要があります(see Address arithmetic)。 したがって、 メモリー割り当てもアライメントされるべきです。 つまり、 セルを割り当てる前に、 here をセル・アライメントする必要があります。 以下のワード達は、 here が既に指定の型のアライメントに合っている状態で無い場合は合わせます。 基本的に、 既に割り当てたのがその型のサイズの倍数であり、 かつ、 here が以前にその型のアライメントに対して合うようにしてあった場合にのみ、 その型のアライメントに対して既にすでに合っているということが言えます。

新しくワードを create した後、here は 標準 Forth では align されます(Gforth では maxalign されます)。

align ( ) core “align”

データ空間ポインターがアライメントできてない場合は、 アライメントするのに十分な空間を予約します。

falign ( ) floating “f-align”

データ空間ポインターが浮動小数点数にアライメントされていない場合は、 アライメントするのに十分な空間を予約します。

sfalign ( ) floating-ext “s-f-align”

データ空間ポインターが単精度浮動小数点数にアライメントされていない場合は、 アライメントするのに十分な空間を予約します。

dfalign ( ) floating-ext “d-f-align”

データ空間ポインターが倍精度浮動小数点数にアライメントされていない場合は、 アライメントするのに十分な空間を予約します。

maxalign ( ) gforth-0.2 “maxalign”

すべてのアライメント要件に合わせてデータ空間ポインターをアライメントします。

cfalign ( ) gforth-0.2 “cfalign”

データ空間ポインターをコード・フィールドの要件に合わせてアライメントします(つまり、 対応する本体が maxalign された状態になるようにする)。


6.7.3 Heap allocation

ヒープ割り当ては、 割り当てられたメモリーの割り当て解除を任意の順序でサポートします。 ディクショナリーの割り当ては影響を受けません(つまり、 連続領域(contiguous region)は終了しません)。 Gforth では、 これらのワードは標準の C 言語ライブラリー呼び出しである malloc() や free() や realloc() を使用して実装されます。

allocate または resize の 1 回の呼び出しによって生成されるメモリ領域は、 内部的に連続しています。 このような領域と他の領域(ヒープから割り当てられた他の領域を含む)との間には連続性はありません。

allocate ( u – a-addr wior  ) memory “allocate”

連続したデータ空間を u アドレス単位分割り当てます。 データ空間の初期内容は未定義です。 割り当てが成功した場合、 a-addr は割り当てられた領域の開始アドレスで、 wior は 0 になります。 割り当てが失敗した場合、 a-addr は未定義で、 wior ゼロ以外の I/O 結果コードです。

free ( a-addr – wior  ) memory “free”

a-addr で始まるデータ空間の領域をシステムに返します。 領域は元々 allocate または resize を使用して取得されている必要があります。 操作が成功した場合、 wior は 0 になります。 操作が失敗した場合、 wior はゼロ以外の I/O 結果コードになります。

resize ( a-addr1 u – a-addr2 wior  ) memory “resize”

a-addr1 に割り当てられた領域のサイズを u アドレス単位に変更します。 但し内容を別の領域に移動する可能性があります。 a-addr2 は、 結果の領域のアドレスです。 操作が成功した場合、 wior は 0 になります。 操作が失敗した場合、 wior はゼロ以外の I/O 結果コードになります。 a-addr1 が 0 の場合、 Gforth の (非標準の ) resizeu アドレス単位の割り当てを行います。

以下のワード達はメモリー・ブロックを扱うのに役立ちます:

save-mem ( addr1 u – addr2 u  ) gforth-0.2 “save-mem”

指定のメモリー・ブロックをヒープ内で新しく割り当てられた領域にコピーします。

free-mem-var ( addr –  ) gforth-experimental “free-mem-var”

addr は、 メモリー範囲のアドレスとサイズを含む 2variable のアドレスです。 これはメモリーを解放し、 2variable をクリアします。

extend-mem ( addr1 u1 u – addr addr2 u2  ) gforth-experimental “extend-mem”

u (アドレス単位)によってヒープから割り当てられたメモリー・ブロック addr1 u1 を拡張します。 (おそらく再割り当てされた)開始アドレスは addr2 で、 その合計長さは u2 で、 拡張部分の開始アドレスは addr です(訳注: 例えば元々長さ10のブロックを5拡張すると( addr1 10 5 – addr2+10 addr2 10+5))

$tring ワード群は、 メモリー・ブロックの処理にも使用できます。 See $tring words (訳注: $tring (ダラー tring)と String (エス string) とあることに注意)

拡張可能なメモリー・バッファーの場合は、 $trings または以下のワード群を使用できます。 adjust-buffer で管理されるバッファーに割り当てられたメモリーは縮小できないため、 これまでに確認された最大サイズよりも小さいサイズにバッファーを調整(adjust)するときはヒープ管理のオーバーヘッドは発生しません。

buffer% ( – u1 u2  ) gforth-experimental “buffer%”

u1 は アライメント(alignment)、 u2 は バッファー・デスクリプタのサイズです。

init-buffer ( addr –  ) gforth-experimental “init-buffer”
adjust-buffer ( u addr –  ) gforth-experimental “adjust-buffer”

addr の buffer% を長さ u に調整します。 これにより、 割り当てられた領域が拡大する可能性がありますが、 決して縮小されることはありません。

2@ を使用すると、 このようなバッファーの現在のアドレスと長さを取得できます。

典型的な使い方:

create mybuf  buffer% %size allot  mybuf init-buffer
s" frobnicate" mybuf adjust-buffer  mybuf 2@ move
mybuf 2@ type
s" foo"        mybuf adjust-buffer  mybuf 2@ move
mybuf 2@ type

6.7.4 Memory Access

@ ( a-addr – w ) core “fetch”

a-addr に保存されているセルを w に取得します。

! ( w a-addr – ) core “store”

wa-addr のセルに格納します。

+! ( n a-addr – ) core “plus-store”

a-addr のセルに n を加算します。

c@ ( c-addr – c ) core “c-fetch”

c_addr に保存されている文字(char) を c に取得します。

c! ( c c-addr – ) core “c-store”

cc-addr の char に格納します。

2@ ( a-addr – w1 w2 ) core “two-fetch”

w2a-addr に格納されているセルの内容、 w1 はその次のセルの内容です。

2! ( w1 w2 a-addr – ) core “two-store”

w2c-addr のセルに格納し、 w1 をその次のセルに格納します。

f@ ( f-addr – r ) floating “f-fetch”

アドレス f-addr の浮動小数点数を r に取得します。

f! ( r f-addr – ) floating “f-store”

r をアドレス f-addr の浮動小数点数として格納します。

sf@ ( sf-addr – r ) floating-ext “s-f-fetch”

アドレス sf-addr から 単精度 IEEE 浮動小数点値を r に取得します。

sf! ( r sf-addr – ) floating-ext “s-f-store”

r を単精度 IEEE 浮動小数点値としてアドレス sf-addr に格納します。

df@ ( df-addr – r ) floating-ext “d-f-fetch”

アドレス df-addr からの倍精度 IEEE 浮動小数点値を r に取得します。

df! ( r df-addr – ) floating-ext “d-f-store”

r を 倍精度 IEEE 浮動小数点値としてアドレス df-addr からに保存します。


6.7.5 Special Memory Accesses

このセクションでは、 他のソフトウェアや他のコンピュータと通信する際に役立つメモリ・アクセスについて説明します。 これは、 アクセスが特定のビット幅であり(Gforth のセル幅とは独立)、 自然な並びでない可能性があり、 通常、 Gforth が実行されるシステムのネイティブなバイト順序(バイト・オーダー)とは異なる可能性がある特定のバイト順序を持つことを意味します。

私達は以下のプレフィックスを使います:

c

8ビット(文字;character)

w

16ビット

l

32ビット

x

64 ビットを 1 つのセルとして表現

xd

64 ビットを 2 つのセルとして表現

x プレフィックスのワードは 32 ビット・システムでは正しく機能しないため、 32 ビット・システムに移植することを目的としたコードの場合は、 xd プレフィックスのワードを使用する必要があります。 注意: xd プレフィックスのワードは 64 ビット・システムでも動作することに注意してください。 64 ビット・システムでは、 上位のセルは単なる 0 (符号なし値の場合)、 または下位のセルの符号拡張です。

以下のメモリー・アクセス・ワード群はすべて、 任意の(非)アライメントされたアドレスで動作します(一部のハードウェアでアライメントが必要な @!f@f! とは異なります)。

w@ ( c-addr – u ) gforth-0.5 “w-fetch”

uc_addr に格納されているゼロ拡張された 16 ビット値(zero-extended 16-bit value)です。

w! ( w c-addr – ) gforth-0.7 “w-store”

w の下位 16 ビットを c_addr に格納します。

l@ ( c-addr – u ) gforth-0.7 “l-fetch”

u は、 c_addr に格納されているゼロ拡張された 32 ビット値(zero-extended 32-bit value)です。

l! ( w c-addr – ) gforth-0.7 “l-store”

w の下位 32 ビットを c_addr に格納します。

x@ ( c-addr – u ) gforth-1.0 “x-fetch”

u は、 c_addr に格納されているゼロ拡張された 64 ビット値です。

x! ( w c-addr – ) gforth-1.0 “x-store”

w の下位 64 ビットを c_addr に格納します。

xd@ ( c-addr – ud ) gforth-1.0 “x-d-fetch”

ud は、 c_addr に格納されているゼロ拡張された 64 ビット値です。

xd! ( ud c-addr – ) gforth-1.0 “x-d-store”

ud の下位 64 ビットを c_addr に格納します。

特定のバイト順序(byte order)でアクセスする場合は、 取得直後(符号拡張の前)、 または格納の直前にバイト順序調整を行う必要があります。 これらのバイト順調整ワードの結果は常にゼロ拡張(zero-extended)されます。

wbe ( u1 – u2  ) gforth-1.0 “wbe”

u1 の 16 ビット値をネイティブ・バイト順からビッグ・エンディアンに、 またはビッグ・エンディアンからネイティブ・バイト順に変換します(両方は同一の操作です)

wle ( u1 – u2  ) gforth-1.0 “wle”

u1 の 16 ビット値をネイティブ・バイト順からリトル・エンディアンに、 またはリトル・エンディアンからネイティブ・バイト順に変換します(両方の操作は同一の操作です)

lbe ( u1 – u2  ) gforth-1.0 “lbe”

u1 の 32 ビット値をネイティブ・バイト順からビッグ・エンディアンに、 またはビッグ・エンディアンからネイティブ・バイト順に変換します(両方は同一の操作です)

lle ( u1 – u2  ) gforth-1.0 “lle”

u1 の 32 ビット値をネイティブ・バイト順からリトル・エンディアンに、 またはリトル・エンディアンからネイティブ・バイト順に変換します(両方は同一の操作です)

xbe ( u1 – u2  ) gforth-1.0 “xbe”

u1 の 64 ビット値をネイティブ・バイト順からビッグ・エンディアンに、 またはビッグ・エンディアンからネイティブ・バイト順に変換します(両方は同一の操作です)

xle ( u1 – u2  ) gforth-1.0 “xle”

u1 の 64 ビット値をネイティブ・バイト順からリトル・エンディアンに、 またはリトル・エンディアンからネイティブ・バイト順に変換します(両方は同一の操作です)

xdbe ( ud1 – ud2  ) gforth-1.0 “xdbe”

ud1 の 64 ビット値をネイティブ・バイト順からビッグ・エンディアンに、 またはビッグ・エンディアンからネイティブ・バイト順に変換します(両方は同一の操作です)

xdle ( ud1 – ud2  ) gforth-1.0 “xdle”

ud1 の 64 ビット値をネイティブ・バイト順からリトル・エンディアンに、 またはリトル・エンディアンからネイティブ・バイト順に変換します(両方は同一の操作です)

特定のバイト順序での符号付きでの取得の場合、 符号なし取得とバイト順序修正の後に符号拡張ワードを実行する必要があります:

c>s ( x – n ) gforth-1.0 “c-to-s”

x の 8 ビット値をセル n に符号拡張します。

w>s ( x – n ) gforth-1.0 “w-to-s”

x の 16 ビット値をセル n に符号拡張します。

l>s ( x – n ) gforth-1.0 “l-to-s”

x の 32 ビット値をセル n に符号拡張します。

x>s ( x – n  ) gforth-1.0 “x>s”

x の 64 ビット値をセル n に符号拡張します。

xd>s ( xd – d  ) gforth-1.0 “xd>s”

xd の 64 ビット値を 2倍長セル d に符号拡張します。

これら全般を、 以下のような流れで使います:

w@ wbe w>s   \ 16ビット 非アライメント 符号付き ビッグ・エンディアン を取得し、
>r lle r> l!  \ 32-bit 非アライメント リトル・エンディアン として 格納

6.7.6 Address arithmetic

アドレス演算は、 配列やレコード(see Structures)やオブジェクト(see Object-oriented Forth)のようなデータ構造を構築可能な基礎基盤です。

標準 Forth では、 データ型のサイズは規定されていません。 代わりに、 サイズを計算したりアドレス演算を行うための多数のワードが提供されます。 アドレス演算はアドレス単位(au;Address Unit, aus;Address UnitS)で実行されます。 ほとんどのシステムでは、 1アドレス単位は 1 バイトです。 注意: 1 つの文字に複数 au が含まれる可能性があるため、 chars は何もしない(noop)訳ではないことに注意してください(noop であるプラットフォームでは、 chars は何もコンパイルしません)。

基本的なアドレス算術ワードは +- です。 たとえば、 セルのアドレスがわかっている場合、 1 cells + を実行すると、 次のセルのアドレスがわかります。

標準 Forth では、 特定の型のアドレスをアライメントするためのワードも定義されています。 多くのコンピュータでは、 特定のデータ型へのアクセスは特定のアドレスでのみ行われる必要があります。 たとえば、 セルは 4 で割り切れるアドレスでのみアクセスできます。 マシンが非アライメント・アクセスを許可する場合でも、 通常はアライメント・アクセスの方が高速に実行できます。

パフォーマンス重視で行く場合: 通常、 アライメント操作はデータ構造の定義中にのみ必要であり、 (より頻繁な)データ構造へのアクセス中には必要ありません。

標準 Forth では、 文字のアドレス・アライメント用ワードは定義されていません。 Forth-2012 では、すべてのアドレスが文字単位でアライメントされています(all addresses are character-aligned)

標準 Forth は、 CREATE されたワードによって返されるアドレスがセル・アライメントされていることを保証します。 さらに、Gforth は、 こ​​れらのアドレスがあらゆる目的に合わせてアライメント済みであることを保証します(addresses are aligned for all purposes)。

注意: 標準の Forth ワード char はアドレス演算とは何の関係もないことに注意してください。

chars ( n1 – n2  ) core “chars”

n1 文字(char)が何アドレス単位になるかを n2 に返します。

char+ ( c-addr1 – c-addr2 ) core “char-plus”

1 chars +.

char- ( c-addr1 – c-addr2  ) gforth-0.7 “char-minus”
cells ( n1 – n2 ) core “cells”

n1 個のセルのアドレス単位の数を n2 に取得します。

cell+ ( a-addr1 – a-addr2 ) core “cell-plus”

1 cells +

cell- ( a-addr1 – a-addr2 ) core “cell-minus”

1 cells -

cell/ ( n1 – n2 ) gforth-1.0 “cell-divide”

n1 個のアドレス単位の幅の中に入れられるセルの数が n2 です

cell ( – u  ) gforth-0.2 “cell”

定数 – 1 cells

aligned ( c-addr – a-addr ) core “aligned”

a-addr は、 c-addr 以上の最初にアライメントされたアドレスです。

floats ( n1 – n2 ) floating “floats”

n2n1 個の浮動小数点数(float)のアドレスユニットの数です。

float+ ( f-addr1 – f-addr2 ) floating “float-plus”

1 floats +.

float ( – u  ) gforth-0.3 “float”

定数 – 浮動小数点数に対応するアドレスユニットの数。

float/ ( n1 – n2 ) gforth-1.0 “float-divide”
faligned ( c-addr – f-addr ) floating “f-aligned”

f-addr は、c-addr 以上の、 浮動小数点数にアライメントされた最初のアドレスです。

sfloats ( n1 – n2 ) floating-ext “s-floats”

n2 は、 n1 個の単精度 IEEE 浮動小数点数のアドレス単位の数です。

sfloat+ ( sf-addr1 – sf-addr2  ) floating-ext “s-float-plus”

1 sfloats +.

sfloat/ ( n1 – n2 ) gforth-1.0 “dfloat-divide”
sfaligned ( c-addr – sf-addr ) floating-ext “s-f-aligned”

sf-addr は、 c-addr 以上の、 最初の単精度浮動小数点数アライメント・アドレスです。

dfloats ( n1 – n2 ) floating-ext “d-floats”

n2 は、 n1 個の倍精度 IEEE 浮動小数点数のアドレス単位の数です。

dfloat+ ( df-addr1 – df-addr2  ) floating-ext “d-float-plus”

1 dfloats +.

dfloat/ ( n1 – n2 ) gforth-1.0 “sfloat-divide”
dfaligned ( c-addr – df-addr ) floating-ext “d-f-aligned”

df-addr は、 c-addr 以上の、 最初の倍精度浮動小数点数アライメント・アドレスです。

maxaligned ( addr1 – addr2  ) gforth-0.2 “maxaligned”

addr2 は、 すべてのアライメント制限を満たす addr1 以上の最初のアドレスです。

cfaligned ( addr1 – addr2  ) gforth-0.2 “cfaligned”

addr2 は、 addr1 以上の最初のアドレスで、 コード・フィールド用にアライメントされています(つまり、 対応する本体(body)が maxalign されるようにします)。

*aligned ( addr1 n – addr2  ) gforth-1.0 “*aligned”

addr1 以上で、 n でアライメントされた(n で割り切れる)アドレスを addr2 に返します。

*align ( n –  ) gforth-1.0 “*align”

heren で割り切れるアドレスにアライメントします。

waligned ( addr – addr’  ) gforth-1.0 “waligned”

Addr’ は、 addr 以上、かつ、次の偶数アドレスです。

walign ( ) gforth-1.0 “walign”

here を 偶数アドレスにアライメントします。

laligned ( addr – addr’  ) gforth-1.0 “laligned”

Addr’ は、 addr 以上、 かつ、 4 で割り切れるアドレスです。

lalign ( ) gforth-1.0 “lalign”

here を 4 で割り切れるアドレスにアライメントします。

xaligned ( addr – addr’  ) gforth-1.0 “xaligned”

Addr’addr 以上、 かつ、 8 で割り切れるアドレスにします。

xalign ( ) gforth-1.0 “xalign”

here を 8 で割り切れるアドレスにアライメントします。

環境クエリ address-unit-bits (see Environmental Queries) と、 以下のワード群は、 バイトアドレスを持たないマシン(non-byte-addressed machines)に移植可能なソフトウェアを作成したい人に役立つかもしれません。

/w ( – u  ) gforth-0.7 “slash-w”

16ビット値に必要なアドレス単位

/l ( – u  ) gforth-0.7 “slash-l”

32ビット値に必要なアドレス単位

/x ( – u  ) gforth-1.0 “slash-x”

64ビット値に必要なアドレス単位


6.7.7 Memory Blocks

メモリー・ブロックは多くの場合、 文字の連なり(character strings)を表します。 文字の連なりをメモリーに保存する方法については、 String representations を参照してください。 他の連なり処理ワード(string-processing words)については、 Displaying characters and strings を参照してください。

これらのワードのいくつかは、 アドレス単位ブロック(address unit blocks)で機能します。 その場合、 文字の連なり(character strings)を扱う際には通常、 ワードの前に CHARS を挿入する必要があります。 ほとんどのワードは文字ブロック(character blocks)で機能し、 文字アライメント・アドレス(char-aligned address)を期待します。

重複するメモリー領域間で文字達をコピーする場合は、 move を使用します。 cmovecmove> は、 適切に実装された move よりも遅くなる傾向があります。

move ( c-from c-to ucount – ) core “move”

c-from アドレスにある ucount 個のcharの内容を c-to アドレスにコピーします。 move は、 2 つの領域が重なっている場合でも正しく機能します。

cmove ( c-from c-to u – ) string “c-move”

データ空間で c-from アドレスから ucount 個のcharの内容を c-to アドレスにコピーします。コピーは、 1 char ずつコピーしながら下位アドレスから上位アドレス方向へ進めます。 つまり、 重複領域の場合、 c-to <= c-from であれば安全です。

cmove> ( c-from c-to u – ) string “c-move-up”

データ空間で c-from アドレスから ucount 個のcharの内容を c-to アドレスにコピーします。 コピーは、 1 char ずつコピーし上位アドレスから下位アドレス方向に進みます。 つまり、 重複領域の場合、 c-to >= c-from であれば安全です。

fill ( c-addr u c – ) core “fill”

cc-addr から u 個char単位で格納します。

erase ( addr u –  ) core-ext “erase”

addr から始まる u aus のすべてのビットをクリアします。

blank ( c-addr u –  ) string “blank”

スペース文字を c-addr アドレスから u 個char単位で格納します。

insert ( string length buffer size –  ) gforth-0.7 “insert”

バッファーの先頭に文字列を挿入します。 残りのバイトは後ろにずらされます(訳注: sizeを超えた分は捨てられます)。

delete ( buffer size u –  ) gforth-0.7 “delete”

最初の u バイトをバッファーから削除し、 その分前にずらして、 残りの最後のバイトを空白(0x20)で埋めます。

compare ( c-addr1 u1 c-addr2 u2 – n ) string “compare”

連なり(string)内のバイトの値に基づいて、 2 つの連なり(strings)を(英語の)辞書順(lexicographically)に比較します(つまり、 英大文字と小文字が区別され、 ロケール固有の照合順序は無視されます)。 2つが等しい場合、 n は 0 です。 最初の連なり(string)が小さい場合、 n は -1 です。 最初の連なり(string)が大きい場合、 n は 1 です。

pad ( – c-addr  ) core-ext “pad”

c-addr は、 一時的なデータ・ストレージとして使用できる一時領域のアドレスです。 少なくとも 84 文字分の空間が使用可能です。


6.8 Strings and Characters


6.8.1 Characters

Forth は、 c@ などのワードで使用される char (別名バイト)をサポートします。 これらは ASCII 文字を表すために使用できます。

Forth は、 複数のバイト(つまり、 複数の char)のシーケンスで表現できる拡張文字(extended characters)もサポートしています。 一般的な文字エンコーディングは、 ユニコードの UTF-8 表現です。

一般に、 ほとんどのプログラム・コードは拡張文字(extended characters)について心配する必要はありません。 連なり(string)表現では、 当該バイトが拡張文字の一部であるか、 それ自体が1つの文字であるかは問題ではありません。 拡張文字が char のシーケンスとして転送される場合、 (emit のような) char を消費するワードも機能します。 Forth は未だ、 拡張文字を処理するためのワード群を提供しています(see Xchars and Unicode)。

ユニコード用語では、 char コード・ユニット、 拡張文字は コード・ポイント です。 Unicode 抽象文字(abstract character) はコード・ポイントのシーケンスとしで構成されますが、 (他のプログラミング言語と同様、 ) Forth には抽象文字個々のデータ型はないことに注意してください。 もちろん、 これらは連なり(string)として表すことができます。

スタック上の char および Xchar には通常の整数ワードを使用できますが、 Gforth にはスタック上の char を処理するためのワードもいくつかあります:

toupper ( c1 – c2 ) gforth-0.2 “toupper”

c1 が小文字の ASCII 文字である場合、 c2 は同等の大文字です。 それ以外の場合、 c2c1 は同じです。


6.8.2 String representations

Forth は通常、 文字列(strings)をスタック上のセルのペア c-addr u として表します。 u はバイト単位の文字列の長さ(別名 文字数)、 c-addr は文字列の最初のバイトのアドレスです。 コード・ポイントは、 文字列内の複数の文字(char)のシーケンスによって表される場合があることに注意してください(また、 ユニコード の「抽象文字」(abstract character)は複数のコード・ポイントで構成される場合があります)。 See String words

もう一つの文字列表現が、 $ を含む文字列ライブラリー・ワード群で使用されます。 それらは、 変数などに配置できるセル・サイズの文字列ハンドル(cell-sized string handle)のアドレスを通じてスタック上の文字列を表します。 See $tring words

旧来からの文字列表現は「カウンタ付き文字列」(counted strings)で、 スタック上では c-addr と表現されます。 c-addr によって指定される char には、 文字列の文字数 n が含まれており、 その文字列はメモリー内の後続の n char アドレスを占有します。 カウントされる文字列の長さは 255 バイトに制限されます。 カウンタ付き文字列は、 必要なスタック項目が 1 つだけであるため魅力的に見えるかもしれませんが、 この制限があるため、 特にワードの入力パラメーターとしては使用しないことをお勧めします。 See Counted string words


6.8.3 String and Character literals

文字列リテラル(string literal)を記述する至高の方法は、 "STRING" と記述することです。 s\" の場合と同じく、 バックスラッシュによるエスケープ(\-escapes)を使用できます。 ただし、 この方法は標準ではないため(non-standard)、 移植性を高めるためには以下のいずれかのワードを使用することをお勧めします:

s\" ( compilation ’ccc"’ – ; run-time – c-addr u  ) core-ext,file-ext “s-backslash-quote”

これは s" と似ていますが、 C言語のようなバックスラッシュ・エスケープ・シーケンス(\-escape-sequences)を次のように変換します: \a BEL (ビープ音)、 \b BS 、 \e ESC (not in C99)、 \f FF 、 \n 改行(newline)、 \r CR 、 \t HT 、\v VT 、\" " 、 \\ \ 、 \[0-7]{1,3} 8進数(非標準)、 \x[0-9a-f]{0,2} 16進指定char値(標準は2桁のみ)、 \u[0-9a-f]{4} ユニコード・コードポイント(サロゲート・ペア自動マージ;auto-merges surrogate pairs)、 \U[0-9a-f]{8} 拡張ユニコード・コード・ポイント。 \ は他の文字よりも前に予約されています。
注意: \xXX は生のバイトを生成することに注意してください。 一方、 \uXXXX と \UXXXXXXXX は、 現在のエンコーディングのコード・ポイントを生成します。 たとえば、 UTF-8 エンコーディングを使用し、 ä (コード・ポイント U+00E4)をエンコードしたい場合は、 次のように指定できます: 文字 ä 自体を記述するか、 \xc3\xa4 (このコード・ポイントの UTF-8 バイト) または \u00e4 または \U000000e4 を記述します。
注意: C言語とは異なり、 \n はホストOS に適した改行シーケンスを生成します。 これは複数の文字で構成される場合があります。 つまり、 "\n"newline と同等です。

S" ( compilation ’ccc"’ – ; run-time – c-addr u  ) core,file “s-quote”

コンパイル・モード用コードは " (二重引用符) を区切り文字として文字列 ccc をパースします。 その実行時コードは、 長さ u と 文字列の開始アドレス c-addr を返します。 インタープリター時: 同様に文字列をパースし、 c-addru 返します。 Gforth では文字列(string)を allocate します。 結果として生じるメモリー・リークは通常は問題ではありません。 例外は、 S" を含む文字列を作成し、 その文字列を evaluate する場合です。 その場合、 リークはインタプリトされたファイルのサイズに制限されないため、 文字列を free することもできます。 Forth-2012 では、それぞれ 80 文字のバッファーが 2 つしか保証されないため、 標準プログラムでは、 文字列は 2 つ前の s" までしか存続していない想定する必要があります。

同様に、 'C' を使用すると、 文字 C のコード xc を取得できます。 この方法は Forth-2012 から標準化されています。 これを取得する古い方法は、 以下のいずれかのワードを使用することです:

char ( ’<spaces>ccc’ – c  ) core,xchar-ext “char”

先頭のスペース達をスキップします。 文字列 ccc をパースし、 ccc の最初の文字の文字コード c を返します(訳注:ASCII以外の文字コードもいけるっぽい(LANG=ja_JP.UTF-8) char あ hex . decimal 3042 ok)

[char] ( compilation ’<spaces>ccc’ – ; run-time – c  ) core,xchar-ext “bracket-char”

コンパイル状態: 先頭のスペース達をスキップします。 文字列 ccc をパースします。 その実行時コード: ccc の最初の文字の文字コード c を返します。 このワードのインタープリター機能(interpretation semantics)は未定義です。

通常は、 コロン定義の外側で char 使用するか、 コロン定義の内側で [char] を使用するか、 単にその両方で 'C' を使用するかです。

注意: 例えば、

"C" type

の方が下記よりも(わずかに)効率的です

'C' xemit

なぜなら、 後者はコード・ポイントをバイトのシーケンスに変換し、 それらを個別に emit するからです。 同様に、 一般的な文字達を扱う場合は、 通常、 コード・ポイントではなく文字列として表す方が効率的です。

S" または 'C' では生成できない、 一般的に使用される文字や文字列を生成するために以下のワード群があります:

newline ( – c-addr u ) gforth-0.5 “newline”

ホストOSの改行シーケンスを含む文字列(訳注: 文字コードを返す訳では無いことに注意。 文字列の内容は LF だったり CRLF だったりする)

bl ( – c-char  ) core “b-l”

c-char は空白(space)の文字コード値です。

#tab ( – c  ) gforth-0.2 “number-tab”
#lf ( – c  ) gforth-0.2 “number-l-f”
#cr ( – c  ) gforth-0.2 “number-c-r”
#ff ( – c  ) gforth-0.2 “number-f-f”
#bs ( – c  ) gforth-0.2 “number-b-s”
#del ( – c  ) gforth-0.2 “number-del”
#bell ( – c  ) gforth-0.2 “number-bell”
#esc ( – c  ) gforth-0.5 “number-esc”
#eof ( – c  ) gforth-0.7 “number-e-o-f”

実際には EOT (ASCII コード 4 別名 ^D)


6.8.4 String words

メモリー・ブロックに使用されるワードは文字列(string)にも役立つため、 文字列(string)の移動やコピーや比較や検索を行うワードについては、 Memory Blocks を参照してください。 文字や文字列を表示するワードについては、 see Displaying characters and strings を参照してください。

以下のワード群は、 既に存在している文字列(strings)に対して機能します:

str= ( c-addr1 u1 c-addr2 u2 – f  ) gforth-0.6 “str-equals”
str< ( c-addr1 u1 c-addr2 u2 – f  ) gforth-0.6 “str-less-than”
string-prefix? ( c-addr1 u1 c-addr2 u2 – f  ) gforth-0.6 “string-prefix-question”

c-addr2 u2c-addr1 u1 の接頭辞部分と合致しますか?

string-suffix? ( c-addr1 u1 c-addr2 u2 – f  ) gforth-1.0 “string-suffix-question”

c-addr2 u2c-addr1 u1 の接尾辞部分と合致しますか?

search ( c-addr1 u1 c-addr2 u2 – c-addr3 u3 flag  ) string “search”

文字列 c-addr1, u1 の中で、 文字列 c-addr2, u2 を検索します。 flag が true の場合: 見つかったアドレスを c-addr3 に、 文字列 c-addr2, u2 を含むそれ以降の文字数を u3 に返します(訳注: s" GNU gforth マニュアル" s" ニュア" search drop ." [" type ." ]" [ニュアル] ok )。 flag が false の場合: 一致するものが見つかりませんでした。 c-addr3, u3c-addr1, u1 と同じです。

scan ( c-addr1 u1 c – c-addr2 u2 ) gforth-0.2 “scan”

c に等しくないすべての文字をスキップします。 結果は c で始まる(c-addr2 u2)か空(empty; c-addr2+u2, 0)です。 Scan はシングルバイト文字(ASCII)に限定されます。 マルチバイト文字を検索するには、 search を使用します。

scan-back ( c-addr u1 c – c-addr u2  ) gforth-0.7 “scan-back”
skip ( c-addr1 u1 c – c-addr2 u2 ) gforth-0.2 “skip”

c に等しいすべての文字をスキップします。 結果は最初の非 C 文字で始まる(アドレス、長さ)か、 空(文字列の長さ0)になります。 skip はシングルバイト文字(ASCII)に限定されます。

-trailing ( c_addr u1 – c_addr u2  ) string “dash-trailing”

c-addr, u1 で指定された文字列の末尾をトリムします(末尾のスペースをすべて削除します)。 u2 は変更後の文字列の長さです。

/string ( c-addr1 u1 n – c-addr2 u2 ) string “slash-string”

c-addr1, u1 で指定された文字列の先頭から n 文字削除します(訳注: c-addr1+n, u1-n するだけっぽい…)。

safe/string ( c-addr1 u1 n – c-addr2 u2 ) gforth-1.0 “safe-slash-string”

c-addr1, u1 で指定された文字列の先頭から n 文字を削除します。 /string とは異なり、safe/string は少なくとも 0 文字、 最大で u1 文字を削除します。

cstring>sstring ( c-addr – c-addr u  ) gforth-0.2 “cstring-to-sstring”

C-addr はゼロで終わる文字列の開始アドレス、 u はその長さです。

以下のワード群は、 ASCII 文字については大文字と小文字を区別せずに比較しますが、 (ワードリストの検索のような)非 ASCII 文字については大文字と小文字を区別します 。

capscompare ( c-addr1 u1 c-addr2 u2 – n ) gforth-0.7 “capscompare”

文字列内のバイトの値に基づいて、 2 つの文字列(string)を辞書順(lexicographically)に比較します。ただし、 ASCII 文字は大文字と小文字を区別せずに比較し、 非 ASCII 文字は大文字と小文字を区別して、 かつ、 ロケール固有の照合順序を使用せずに比較します。 それらが等しい場合 n は 0 です。 最初の文字列が小さい場合 n は -1 です。 最初の文字列が大きい場合n は 1 です。

capsstring-prefix? ( c-addr1 u1 c-addr2 u2 – f  ) gforth-1.0 “capsstring-prefix?”

string-prefix? と似ていますが、 ASCII 文字の大文字と小文字は区別されません: c-addr2 u2c-addr1 u1 の接頭辞か?

capssearch ( c-addr1 u1 c-addr2 u2 – c-addr3 u3 flag  ) gforth-1.0 “capssearch”

search と似ていますが、 ASCII 文字の大文字と小文字は区別されません: c-addr1 u1 内で c-addr2 u2 を検索します。 見つかった場合 flag は true になります。

以下のワード群は、 ヒープに文字列(string)を作成、 またはヒープの文字列(string)を拡張します。

s+ ( c-addr1 u1 c-addr2 u2 – c-addr u  ) gforth-0.7 “s-plus”

c-addr u は、c-addr1 u1 (最初) と c-addr2 u2 (2 番目) を連結したのを含む、 新しく allocate された文字列です。

append ( c-addr1 u1 c-addr2 u2 – c-addr u  ) gforth-0.7 “append”

C-addr u は、c-addr1 u1 (最初) と c-addr2 u2 (2 番目) を連結したものです。 c-addr1 u1allocate された文字列であり、 u 文字の領域に対応するために(可能なら)サイズ変更(resize)して c-addr2 u2 の文字列を追加(append)します(サイズ変更できなくて、新しいアドレスに移動する可能性があります)。

>string-execute ( ... xt – ... addr u  ) gforth-1.0 “>string-execute”

xt を実行(execute)すると、 標準出力 (typeemit やそれらを使用するすべてのもの) を文字列にリダイレクトします。 結果の文字列は addr u で、これは割り当てられた(allocate)メモリー内にあります。 この文字列を開放(free)するのは、 >string-execute の呼び出し元の責任です。

以下のようにして、>string-execute を使用して s+ を定義できます:

: s+ ( c-addr1 u1 c-addr2 u2 – c-addr u ) [: 2swap type type ;] >string-execute ;

2 つの文字列だけを連結する場合、 >string-execute は効率的ではありませんが、 多くの文字列を連結する場合は、 >string-execute の方が効率的です。


6.8.5 $tring words

以下の文字列ライブラリーは、 文字列を通常のセル・サイズの変数(文字列ハンドル;string handle)に格納します。 これらのハンドルには、 ヒープに割り当てられたセル・カウント文字列(cell-counted string;カウンターがセル・サイズであるカウンター付き文字列)へのポインターが含まれています。 この文字列ライブラリーは bigFORTH 由来です。

内容への恒久的な参照は 1 つだけ(ハンドル内の参照)しかないため、 未解決の参照を恐れることなく文字列を再配置または削除できます。 これには、 プログラマが、 例えば $@ 等によって生成された参照を一時的な目的でのみ使用する必要があります。 つまり、 これらの一時的な参照は、 戻り値として渡されたり、 グローバル・メモリーに格納されたりすることはなく、 ハンドルを変更する可能性のあるワードは、 これらの一時的な参照が存在する間は呼び出されません。

このライブラリーを補完するものとしてセル・ペア表現(cell-pair representation)があります。 文字列変数に対して $tring ワードを使用しますが、 これは c-addr u 表現では面倒です。 セル・ペア表現は、 文字列を変更しないで処理(検査など)するために使用します。

$! ( addr1 u $addr –  ) gforth-0.7 “string-store”

文字列 addr1 u を、 新しく割り当てられた文字列バッファーに格納し、 その文字列バッファーのアドレスを $addr に格納します。 必要に応じて以前のバッファーを解放します。

$@ ( $addr – addr2 u  ) gforth-0.7 “string-fetch”

格納された文字列のアドレスと長さを返します。

$@len ( $addr – u  ) gforth-0.7 “string-fetch-len”

格納されている文字列の長さを返します。

$!len ( u $addr –  ) gforth-0.7 “string-store-len”

格納されている文字列(string)領域の長さを変更します(足し算ではない。 長さを直にいじる) それゆえ、メモリー領域を変更し、 アドレスとカウンタ・セルも調整(adjust)する必要があります(訳注: 元の長さより長くした場合、 領域を拡張するだけで初期化はしない。 そこにはゴミが入っているので自分で初期化する必要がある)

$+!len ( u $addr – addr  ) gforth-1.0 “string-plus-store-len”

$addr によって参照されるメモリー領域の最後に u バイト用の空間を確保します(追加。確保するだけで初期化はしない。 $addr の「長さ」に追加もする)。 addr は、 確保した空間の最初のアドレスです。

$del ( addr off u –  ) gforth-0.7 “string-del”

文字列のオフセット位置 off から u バイト削除します。

$ins ( addr1 u $addr off –  ) gforth-0.7 “string-ins”

オフセット位置 off に文字列を挿入します。

$+! ( addr1 u $addr –  ) gforth-0.7 “string-plus-store”

文字列(addr1 u)を別の文字列($addr)に追加します。

c$+! ( char $addr –  ) gforth-1.0 “c-string-plus-store”

文字列に文字を追加します。

$free ( $addr –  ) gforth-1.0 “string-free”

$addr が指す文字列を解放し、 $addr のポインターを 0 にします( $@ すると 0 0 を返す)

$init ( $addr –  ) gforth-1.0 “string-init”

以前に何があったかに関係なく、そこに空の文字列を保存します(訳注: ポインターを 0 にする訳ではなくて、 ポインターの指す先に長さゼロの文字列がある)

$split ( addr u char – addr1 u1 addr2 u2  ) gforth-0.7 “string-split”

文字列中で最初に現れる char を区切り文字として文字列を 2 つに分割します(例: HTML クエリの引数の ’?’)(訳注: 区切り文字 char 自体は分割後の文字列に含まれない)

$iter ( .. $addr char xt – ..  ) gforth-0.7 “string-iter”

区切り文字 char で切り出した部分文字列(addr u)ごとに xt を呼び出します。 xt は (addr u – ) でなければなりません。 これにより、 例えば ’&’ で区切られた引数を簡単に分解できます(訳注: : my-type ( addr u – ) ." [" type ." ]" ; variable title s" GNU gforth manual" title $! title $ my-type [GNU gforth manual] title bl ’ my-type $iter [GNU][gforth][manual] ok)。

$over ( addr u $addr off –  ) gforth-1.0 “string-over”

$addr の文字列のオフセット位置 off から文字列(addr u)で上書きします。

$exec ( xt addr –  ) gforth-1.0 “string-exec”

実行トークン xt を実行し、 その標準出力(TYPE や EMIT やそれらを使用するすべてのもの)を addr が指す文字列の末尾に「追加」します。

$tmp ( xt – addr u  ) gforth-1.0 “string-t-m-p”

ワードの出力から一時的な文字列を生成します

$. ( addr –  ) gforth-1.0 “string-dot”

文字列を出力する、 ショートカット( $@ TYPE → $. )

$slurp ( fid addr –  ) gforth-1.0 “string-slurp”

(slurp;音を立ててすする)ファイル fid を最後まで読み取り(ファイルのクローズは行いません)、 読み取ったデータを addr の指す文字列に入れます。

$slurp-file ( c-addr u addr –  ) gforth-1.0 “string-slurp-file”

c-addr u という名前のファイル内のすべてのデータを addr の文字列に入力します。

$+slurp ( fid addr –  ) gforth-1.0 “string-plus-slurp”

ファイル fid を最後まで読み取り(但しクローズはしません)、 読み取ったデータを addr の文字列に「追加」します。

$+slurp-file ( c-addr u addr –  ) gforth-1.0 “string-plus+slurp-file”

c-addr u という名前のファイル内のすべてのデータを addr の文字列に「追加」します。

$[] ( u $[]addr – addr’  ) gforth-1.0 “string-array”

addr’ は、 文字列配列 $[]addru 番目の要素のアドレスです。 配列のサイズは必要に応じて変更されます。

$[]! ( c-addr u n $[]addr –  ) gforth-1.0 “string-array-store”

文字列 c-addr u を文字列配列 $[]addr のインデックス n に格納します。 必要に応じて配列のサイズが変更されます。

$[]+! ( c-addr u n $[]addr –  ) gforth-1.0 “string-array-plus-store”

文字列 c-addr u をインデックス n の文字列に「追加」します。 必要に応じて配列のサイズが変更されます。 これを $+[]! と混同しないでください。

$+[]! ( c-addr u $[]addr –  ) gforth-1.0 “string-append-array”

文字列 c-addr u を文字列配列 $[]addr の新しい最後の要素として保存します(つまり要素が1つ増える)。 必要に応じて配列のサイズが変更されます。

$[]@ ( n $[]addr – addr u  ) gforth-1.0 “string-array-fetch”

配列インデックス n から文字列を取得します — 空の場合はゼロ文字列( 0 0 )を返し、 誤って配列が成長しないようにします(訳注:要素はあるけど空文字列(長さ0)の場合と、範囲外の要素を指した場合の区別は付かないので注意)

$[]# ( addr – len  ) gforth-1.0 “string-array-num”

配列内の要素の数を返します

$[]map ( addr xt –  ) gforth-1.0 “string-array-map”

文字列配列 addr のすべての要素に対して 実行トークン xt を実行します。 xt は ( addr u – ) で、一度に 1 つの文字列を取得します

$[]slurp ( fid addr –  ) gforth-1.0 “string-array-slurp”

ファイル fid の内容を 1 行ずつ文字列配列 addr に入れます

$[]slurp-file ( addr u $addr –  ) gforth-1.0 “string-array-slurp-file”

名前付きファイル addr u を 1 行ずつ文字列配列 $addr に入れます。

$[]. ( addr –  ) gforth-1.0 “string-array-dot”

すべての配列エントリを出力します

$[]free ( addr –  ) gforth-1.0 “string-array-free”

addr は沢山のセルカウント文字列(string)へのアドレス達を含む、 セル・カウント文字列(string)へのアドレスで、 $[]free はこれらの文字列を解放、 つまり、 この配列を開放し、 addr の値を 0 にセットします。

$save ( $addr –  ) gforth-1.0 “string-save”

savesys のディクショナリーに文字列(string)をプッシュします

$[]save ( addr –  ) gforth-1.0 “string-array-save”

文字列配列を savesys のディクショナリーにプッシュ

$boot ( $addr –  ) gforth-1.0 “string-boot”

ディクショナリーから文字列を新しく割り当てたメモリーに取り込みます。 その後、 ディクショナリーのその文字列の領域を(0で)クリアします(長さはそのまま)。

$[]boot ( addr –  ) gforth-1.0 “string-array-boot”

ディクショナリーから、 文字列配列を割り当てられたメモリーに取得します

$saved ( addr –  ) gforth-1.0 “string-saved”

アドレスを ブート済み(booted)/保存済み(saved) としてマークする

$[]saved ( addr –  ) gforth-1.0 “string-array-saved”

アドレスを ブート済み(booted)/保存済み(saved) としてマークする

$Variable ( ) gforth-1.0 “string-variable”

savesystem 全体にわたって保存される文字列変数。

$[]Variable ( ) gforth-1.0 “string-array-variable”

savesystem 全体にわたって保存される文字列変数。


6.8.6 Counted string words

カウンタ付文字列(counted string)は、 指定のアドレスのバイトとして長さを格納し、 その後に文字列のバイトが続きます。 可能な長さは厳しく制限されており、 入力文字列を破壊せずにその場で部分文字列を作成することはできません。 したがって、 カウンタ付文字列を使用しないことをお勧めします。 それでも、 カウンタ付文字列を処理する必要がある場合、 以下のようなワード群があります:

count ( c-addr1 – c-addr2 u ) core “count”

c-addr2 は最初の文字で、 uc-addr1 のカウンタ付文字列の、 長さです。

以下のワードは、 (s" とは異なり) 有用なインタープリター機能(interpretation semantics)がなく、 ([char] とは異なり) 対応するインタプリタ用コードがないため、 コロン定義内でのみ使用する必要があります(コロン定義がある場合):

C" ( compilation "ccc<quote>" – ; run-time  – c-addr  ) core-ext “c-quote”

コンパイル状態: " (二重引用符) を区切り文字として文字列 ccc をパースします。 その実行時コードは、 指定のカウンタ付文字列 ccc のアドレスを c-addr として返します。 インタープリター機能は未定義です。

( gforth-obsolete ) place ( c-addr1 u c-addr2 –) \ c-addr2 に長さ u のカウンタ付き文字列を作成し、 文字列 c-addr1 u をその場所にコピーします。

string, ( c-addr u –  ) gforth-0.2 “string,”

文字列をカウンタ付き文字列として(here以降の)データ空間に書き込みます。


6.9 Control Structures

Forth の制御構造は、 コロン定義内のみで、 対話的(interpretively)に使用することはできません11。 私たちもこのような制限を好まないので、 多くのスキームが提案されてはいますが、 これを回避する満足のいく方法はまだ見つかっていません。


6.9.1 Selection

flag
IF
  code
ENDIF

flag がゼロ以外の場合(IF などに関する限り、 任意のビットが設定されたセルは true 扱いです)、 code が実行されます。

flag
IF
  code1
ELSE
  code2
ENDIF

flag が true の場合は code1 が実行され、 それ以外の場合は code2 が実行されます。

ENDIF の代わりに THEN を使用できます。 実際 THEN は標準です、 がしかし、 ENDIF は非常に人気があるものの、 標準ではありません。 私達は ENDIF の使用をお勧めします。 なぜなら、 他の言語を知っている人にとっても混乱が少ないためです(また、 ENDIF に対しては、 これらの人々の Forth に対する否定的な偏見が強化される傾向が見られません)。 なお、 THEN のみを提供するシステムに ENDIF を追加するのは以下のように簡単です:

: ENDIF   POSTPONE then ; immediate

[Webster’s New Encyclopedic Dictionary によると、then (副詞) には以下の意味があります:

... 2b: 順番に次の後に続く ... 3d: 必然的な結果として (if you were there, then you saw them)

Forth の THEN は 2b の意味を持ちますが、 Pascal や他の多くのプログラミング言語の THEN は 3d の意味を持ちます。]

Gforth には ?DUP-IF および ?DUP-0=-IF というワードも用意されているため、 ?dup の使用を避けることができます。これらの代替手段を使用することは、 ?dup を使用するよりも効率的です。 ENDIF?DUP-IF?DUP-0=-IF の標準 Forth での定義は、compat/control.fs で提供されます。

x
CASE
  x1 OF code1 ENDOF
  x2 OF code2 ENDOF
  ...
  ( x ) default-code ( x )
ENDCASE ( )

xix に等しいなら、 最初の codei を実行します。 x1xn のいずれも一致しない場合は、 オプションの default-code が実行されます。 オプションのデフォルト・ケースは、 最後の ENDOF の後にコードを記述するだけで追加できます。 スタック頂上にある x を使用することはできますが、 それを消費してはなりません。 値 x は、 この構造によって(一致する OF によって、 または一致する OF がない場合は ENDCASE によって) 消費されます。 例:

: num-name ( n -- c-addr u )
 case
   0 of s" zero " endof
   1 of s" one "  endof
   2 of s" two "  endof
   \ default case:
   s" other number" 
   rot \ get n on top so ENDCASE can drop it
 endcase ;

(非標準の) ?of を使用して、 case を 3 つ以上の選択肢の一般的な選択構造として使用することもできます。 ?Of はフラグを受け取ります。 例:

: sgn ( n1 -- n2 )
    \ sign function
    case
	dup 0< ?of drop -1 endof
	dup 0> ?of drop 1 endof
	dup \ n1=0 -> n2=0; dup an item, to be consumed by ENDCASE
    endcase ;

プログラミング・スタイル・メモ: コードを理解しやすくするには、 選択構造を介したすべての経路で同一の方法でスタックを変更(消費およびプッシュされるスタック項目の数と型)するようにする必要があります。


6.9.2 Simple Loops

BEGIN
  code1
  flag
WHILE
  code2
REPEAT

code1 が実行され、 flag が計算されます。 flag が true の場合、 code2 が実行され、 BEGIN からループが再開されます。 flag が false の場合、 REPEAT の後へ実行が続行されます。

BEGIN
  code
  flag
UNTIL

code が実行されます。 flag が false の場合、 BEGIN からループが再開されます。

プログラミング・スタイル・メモ: コードを理解しやすくするために、 ループの完全な反復(complete iteration)によってスタック上の項目の数と型が変更されるべきではありません。

BEGIN
  code
AGAIN

これは無限ループです。


6.9.3 Counted Loops

基本のカウント・ループ:

limit start ?DO
  body
LOOP

これは、 start から始まり limit まで(limit 自身は除く)、 各整数値に対して 1 回の反復を実行します。 カウンタ、 つまりインデックスには、 i を使用してアクセスできます。 たとえば、 以下のループをご覧ください:

10 0 ?DO
  i .
LOOP

出力: 0 1 2 3 4 5 6 7 8 9

最も内側のループのインデックスには i を使用してアクセスでき、 その一つ外側のループのインデックスには j を使用してアクセスでき、 さらにもう一つ外側のループのインデックスには k を使用してアクセスできます。

i' を使用すると最も内側のループの limit にアクセスでき、 delta-i を使用すると i' - i にアクセスできます。

: foo 7 5 ?do cr i . i' . delta-i . loop ; 

出力:

5 7 2 
6 7 1

ループ制御データはリターン・スタックに保持されるため、 リターン・スタックへのアクセスとカウント・ループ・ワードの混在にはいくつかの制限があります。 特に、ループの外側のリターン・スタックに値を置いた場合、 ループ内で値を読み取る事はできません12。 ループ内のリターン・スタックに値を置く場合は、 ループの終了前、 およびループのインデックスにアクセスする前に値を削除する必要があります。

カウント・ループにはいくつかのバリエーションがあります:

  • LEAVE は、 最も内側のカウント・ループを直ちに抜け出します。 それが関係する LOOP または NEXT の後へ実行は移ります。 例:
    10 0 ?DO  i DUP . 3 = IF LEAVE THEN LOOP
    

    出力: 0 1 2 3

  • UNLOOP は、 例えば EXIT などを介しての異常なループ終了の準備を行います。 UNLOOP は、 EXIT がリターン・アドレスに到達できるように、 リターン・スタックからループ制御パラメーターを削除します(訳注: 1重ループなので unloop 1つ。2重ループなら unloop 2つ)。 例:
    : demo 10 0 ?DO i DUP . 3 = IF UNLOOP EXIT THEN LOOP ." Done" ;
    

    出力: 0 1 2 3

  • startlimit より大きい場合でも、 ?DO はループを開始します(ラップアラウンド演算(処理可能な範囲をの最後に達した後に最初に戻る事)によって両者が等しくなるまで LOOP を反復します)。 通常、 この振る舞いは望ましくないものです。 したがって、 Gforth は (?DO の代替として、) +DOU+DO を提供します。 これらは、 startlimit より大きい場合にはループを開始しません。 +DO は符号付きループ・パラメータ用で、 U+DO は符号なしループ・パラメーター用です。
  • ?DODO に置き換えることはできます。 DO は、 ループ・パラメーター に関係なく、 常にループに入ります。 あなたが、 どの場合にもループに入ることを知っている場合でも DO は使用しないでください。 このような知恵はプログラムを保守していく中で無効になる傾向があり、 それゆえ DO が問題を引き起こすことになります。
  • LOOPn +LOOP に置き換えることができます。 これにより、インデックスが 1 ではなく n によって更新されます。 limit-1limit の間の境界を越えると、 ループは終了します。 例:
    4 0 +DO  i .  2 +LOOP
    

    出力: 0 2

    4 1 +DO  i .  2 +LOOP
    

    出力: 1 3

  • n が負数の場合、 n +LOOP の動作は奇妙です:
    -1 0 ?DO  i .  -1 +LOOP
    

    出力: 0 -1

    0 0 ?DO  i .  -1 +LOOP
    

    出力: なし。

    私たちは ?DO+LOOP を組み合わせないことをお勧めします。 Gfors はいくつかの代替手段を提供します:

    I=limit の反復を含める -1 +LOOP の振る舞いが必要な場合、 -[DO または U-[DO] でループを開始します(ここで、 [ は、 包含範囲の数学的表記法(例: [1,n] からインスピレーションを得ています):

    -1 0 -[DO  i .  -1 +LOOP
    

    出力: 0 -1.

    0 0 -[DO  i .  -1 +LOOP
    

    出力: 0

    0 -1 -[DO  i .  -1 +LOOP
    

    出力: なし。

    limit を除外したい場合、 代わりに 1 -LOOP (または一般的には u -LOOP)を使用し、 ?DO または -DO または U-DO でループを開始します。 -LOOP は、limit+1limit の間の境界を越えたときにループを終了します。 例:

    -2 0 -DO  i .  1 -LOOP
    

    出力: 0 -1

    -1 0 -DO  i .  1 -LOOP
    

    出力: 0

    0 0 -DO  i .  1 -LOOP
    

    出力: なし。

    残念ながら、 +DO, U+DO, -DO, U-DO, -LOOP は 標準 Forth では定義されていません。 ただし、 標準のワードのみを使用するこれらのワードの実装が compat/loops.fs にて提供されています。

  • よくある作業は、 配列の要素を前方または後方に反復処理することです。 要素のアドレスに対して反復処理することには 2 つの利点があります。 配列の開始アドレスを保持する必要がなくなり、 データ・スタックの負荷が軽減されます。 また、 反復ごとにアドレス計算を実行する必要がなくなります。 欠点は、 通常の配列表現 addr u要素 または addr uバイト から始めて、 start アドレスと limit アドレスを生成するために何らかの処理が必要なことです。 Gforth には、addr ubytes 表現からそこに到達するために bounds があるため、セル配列 v を介した前方ループを以下のように記述できます:
    create v 1 , 3 , 7 ,
    : foo v 3 cells bounds U+DO i  . cell +LOOP ;
    foo
    

    これは 1 3 7 を出力します。 逆方向にたどるための入力の前処理はより複雑であるため、 Gforth はそれを行う MEM-DOLOOP 形式のループ構造を提供します。 これは addr uバイト 表現の配列と要素サイズを受け取り、 要素のアドレスを逆順に反復処理します。

    create v 1 , 3 , 7 ,
    : foo1 v 3 cell array>mem MEM-DO i  . LOOP ;
    foo1
    

    これは 7 3 1 を出力します。ARRAY>MEMaddr uelems uelemsizeMEM-DO が期待する addr ubytes uelemsize に変換します(ubytes は uelems * uelmsize です)。 このループは MEM-DO 通過後にuelemsize ずつ減算される、 LOOP と対になるループとなります。

    Gforth は、 完全を期すために MEM+DO も追加します。 MEM-DO と同一のパラメーターを受け取りますが、 配列を順(forwards)に処理します:

    create v 1 , 3 , 7 ,
    : foo2 v 3 cell array>mem MEM+DO i  . LOOP ;
    foo2
    

    出力: 1 3 7

  • 別のカウント・ループ:
    n
    FOR
      body
    NEXT
    

    これは、 ?DO ループを適切に最適化するのが面倒な、 ネイティブ・コード・コンパイラー作成者達が好むループです。 このループ構造は標準 Forth では定義されていません。 Gforth では、 このループは n+1 回繰り返します。 i は、 n で始まり 0 で終わる値を生成します。 他の Forth システムは、FOR ループをサポートしている場合でも、 振る舞いが異なる場合があります。 この問題を回避するには、 FOR ループを使用しないようにしてください。

カウント・ループ・ワード群:

?DO ( compilation – do-sys ; run-time w1 w2 – | loop-sys  ) core-ext “question-do”

See Counted Loops.

+DO ( compilation – do-sys ; run-time n1 n2 – | loop-sys  ) gforth-0.2 “plus-do”

See Counted Loops.

U+DO ( compilation – do-sys ; run-time u1 u2 – | loop-sys  ) gforth-0.2 “u-plus-do”

See Counted Loops.

bounds ( addr u – addr+u addr ) gforth-0.2 “bounds”

開始アドレス addr と長さ u で表されるメモリー・ブロックを指定すると、 u+do または ?do の終了アドレス addr+u と開始アドレス addr を正しい順序で生成します。

-[do ( compilation – do-sys ; run-time n1 n2 – | loop-sys  ) gforth-experimental “minus-bracket-do”

負の方向へカウントされるループを開始します。 n2<n1 の場合、 ループをスキップします。 このようなカウント・ループは、 増分が負である +loop と対になります。 I>=n1 である限り実行されます。

u-[do ( compilation – do-sys ; run-time u1 u2 – | loop-sys  ) gforth-experimental “u-minus-bracket-do”

負の方向へカウントするループを開始します。 u2<u1 の場合、ループをスキップします。 このようなカウント・ループは、 増分が負の +loop と対になります。 I>=u1 である限り実行されます。

-DO ( compilation – do-sys ; run-time n1 n2 – | loop-sys  ) gforth-0.2 “minus-do”

See Counted Loops.

U-DO ( compilation – do-sys ; run-time u1 u2 – | loop-sys  ) gforth-0.2 “u-minus-do”

See Counted Loops.

array>mem ( uelements uelemsize – ubytes uelemsize) \ ubytes は uelements * uelementsize です

mem+do ( compilation – w xt do-sys; run-time addr ubytes +nstride –  ) gforth-experimental “mem-plus-do”

Iaddr から開始し、 I<addr+ubytes である限り、 nstride 幅のステップでメモリー内をアドレスが増える方向にカウント・アップするカウント・ループを開始します。 loop と対にする必要があります。

mem-do ( compilation – w xt do-sys; run-time addr ubytes +nstride –  ) gforth-experimental “mem-minus-do”

Iaddr+ubytes-ustride として開始し、 I>=addr である間 -nstride 幅のステップでメモリーをアドレス下位方向(backward)にステップするカウント・ループを開始します。 loop と対にしなければなりません。

DO ( compilation – do-sys ; run-time w1 w2 – loop-sys  ) core “DO”

See Counted Loops.

FOR ( compilation – do-sys ; run-time u – loop-sys  ) gforth-0.2 “FOR”

See Counted Loops.

LOOP ( compilation do-sys – ; run-time loop-sys1 – | loop-sys2  ) core “LOOP”

See Counted Loops.

+LOOP ( compilation do-sys – ; run-time loop-sys1 n – | loop-sys2  ) core “plus-loop”

See Counted Loops.

-LOOP ( compilation do-sys – ; run-time loop-sys1 u – | loop-sys2  ) gforth-0.2 “minus-loop”

See Counted Loops.

NEXT ( compilation do-sys – ; run-time loop-sys1 – | loop-sys2  ) gforth-0.2 “NEXT”

See Counted Loops.

i ( R:n – R:n n ) core “i”

n は、最も内側のカウント・ループのインデックスです。

j ( R:n R:w1 R:w2 – n R:n R:w1 R:w2 ) core “j”

n は、 最も内側から数えて 2 番目のカウント・ループのインデックスです。

k ( R:n R:w1 R:w2 R:w3 R:w4 – n R:n R:w1 R:w2 R:w3 R:w4 ) gforth-0.3 “k”

n は、 最も内側から数えて3番目のカウント・ループのインデックスです。

i' ( R:w R:w2 – R:w R:w2 w ) gforth-0.2 “i-tick”

最も内側のカウント・ループの limit

delta-i ( r:ulimit r:u – r:ulimit r:u u2 ) gforth-1.0 “delta-i”

u2=I'-I (limit とインデックスの差)

LEAVE ( compilation – ; run-time loop-sys –  ) core “LEAVE” 
\ 訳注: LEAVE は、 最も内側のカウンタ付きループを抜け出します。
\       それが関係する LOOP または NEXT の直後から実行を続行します。

See Counted Loops.

?LEAVE ( compilation – ; run-time f | f loop-sys –  ) gforth-0.2 “question-leave” 
\訳注: f が true なら leave します。

See Counted Loops.

unloop ( R:w1 R:w2 – ) core “unloop”
DONE ( compilation do-sys – ; run-time –  ) gforth-0.2 “DONE”

do-sys までのすべての LEAVE を解決します(訳注: loop, +loop , next 等の中で内部的に呼び出されます)。

標準では、 do-sysCS-PICKCS-ROLL を使用することは許可されていません。 MEM+DOMEM-DO によって生成される do-sys を除いて、 Gforth では CS-PICKCS-ROLL の使用を許可しますが、 すべての ?DO などに対して、 定義を介した任意の経路上に UNLOOP が正確に 1 つだけ存在すること(LOOP などの失敗経路上で UNLOOP コンパイルするなど)を確認するのはあなたの仕事です。 また、 すべての LEAVE が(ループ終了ワードの 1 つまたは DONE を使用して)解決されていることを確認する必要があります。


6.9.4 Begin loops with multiple exits

カウント・ループの場合、 複数箇所で leave を使用できます。 begin ループの場合は、 以下の選択肢があります:

ループ内で exit を使用(複数記述できます)すると、 ループだけでなくコロン定義全体からも去ります。 例:

: foo
  begin
    condition1 while
      condition2 if
        exit-code2 exit then
      condition3 if
        exit-code3 exit then
    ...
  repeat
  exit-code1 ;

このアプローチの欠点は、 ループ後に共通コードが必要な場合、 共通コードを含む別のワードで foo を包むか、 または、 それぞれの exit-code から共通コードを呼び出す必要があることです。

もう 1 つのアプローチは、 begin ループ内で複数の while を使用することです。 追加の while ごとにループの後ろに then を追加する必要があります。 例:

begin
  condition1 while
    condition2 while
      condition3 while
again then then then

ここでは、 ループの最後に again を使用して、 各 whilethen を用意しました。 repeatthen を 1 つ減らしますが、 それ以外の場合は同じ動作になります。 これが機能する理由の説明については、 See Arbitrary control structures をご覧下さい。

後で共通のコードを使用することはできますが、 上で示したように、 異なる出口(exit)に対して異なる exit-code を使用することはできません。 以下のようにすると、 これらの異なる exit-code を使用できます:

begin
  condition1 while
    condition2 while
      condition3 while
again then exit-code3
else exit-code2 then
else exit-code1 then

exit-code は終了条件から比較的離れているため、 これを理解するのは比較的困難です(このような制御構造に慣れていないことも理由にはなりません)。


6.9.5 General control structures with case

Gforth は、 拡張 case を提供することで、 上で説明した複数出口ループの問題を解決する追加のオプションを提供します。 この拡張 case の移植可能な実装は compat/caseext.fs にあります。

この拡張には 3 つの追加ワードがあります。 1 つ目は ?of で、 case 内で(単なる同等性のテストではなく、)一般的なテストが可能です。 例:

: sgn ( n -- -1|0|1 )
  ( n ) case
    dup 0 < ?of drop -1 endof
    dup 0 > ?of drop 1  endof
    \ otherwise leave the 0 on the stack
  0 endcase ;

注意: endcase は値(a value)を drop することに注意してください。 これは of ではほとんどうまいこと機能しますが、 ?of ではたいていうまいこといかないので、 今回も endcase で drop するための値として 0 をスタック置きます。 ここでは、 sgn に渡される n は、 いずれの ?of もトリガーしない場合返り値の 0 そのものになります。

2 番目の追加ワードは next-case で、 これにより case をループに変えることができます。 出口が3つのループは以下のようになります:

case
  condition1 ?of exit-code1 endof
  condition2 ?of exit-code2 endof
  condition3 ?of exit-code3 endof
  ...
next-case
common code afterwards

ご覧のとおり、 これにより、 先程議論したバリエーションの両方の問題が解決されます(see Begin loops with multiple exits)。 注意: endcase とは異なり、 next-case は値をドロップしないことに注意してください。 13

最後の追加ワードは contof です。 これは endof の代わりに使用され、 ループを終了する代わりに次の反復を開始します。 これは、 ダイクストラのガード付きコマンド 繰り返し: do と同様の方法で使用できます。 例:

: gcd ( n1 n2 -- n )
    case
        2dup > ?of tuck - contof
        2dup < ?of over - contof
    endcase ;

ここで、 2 つの ?of はループを継続する異なる方法を持っています。 どちらの ?of もトリガーされない場合、 2 つの数値は等しく、gcd(最大公約数) になります。 Endcase はそれらの 1 つを削除し、もう 1 つは n として残します。

これらのワードを組み合わせることもできます。 以下は、 endcase を除く、 各 case ワードをそれぞれ 1 回使用する例です:

: collatz ( u -- )
    \ print the 3n+1 sequence starting at u until we reach 1
    case
        dup .
        1 of endof
        dup 1 and ?of 3 * 1+ contof
        2/
    next-case ;

この例では、 シーケンスの現在の値をスタックに保持します。 1 の場合、 of がトリガーされ、 値が削除され、 case 構造から去ります。 奇数の場合、 ?of がトリガーされ、 3n+1 が計算され、 contof で次の反復が開始されます。 それ以外の場合、 数値が偶数の場合は 2 で除算され、 next-case でループが再開されます。


6.9.6 Arbitrary control structures

標準 Forth は、 ネストされない方法での制御構造の使用を許可・サポートします。 まだ完成されてない制御構造に関する情報は、 制御フロー・スタック(control-flow stack)に保存されます。 このスタックは Forth のデータ・スタック上に実装でき、 Gforth はそうしました。

orig エントリは未解決の前方分岐を表し、 dest エントリは後方分岐ターゲットを表します。 いくつかのワードは、 可能なあらゆる制御構造を構築するための基礎となります(呼び出しやコルーチンやバックトラッキングのような、 ストレージを必要とする制御構造を除く)。

IF ( compilation – orig ; run-time f –  ) core “IF”

実行時(run-time)、 f=0 の場合、 (コンパイル時に) orig を消費する THEN (または ELSE) の後から実行が続行されます。 それ以外の場合は、 IF の直後に続きます(see Selection)。

AHEAD ( compilation – orig ; run-time –  ) tools-ext “AHEAD”

実行時、 (コンパイル時に) orig を消費する THEN の後から実行が続行されます(訳注: つまり、 単純に THEN へジャンプする)。

THEN ( compilation orig – ; run-time –  ) core “THEN”

(コンパイル時に) orig をプッシュした IF または AHEAD または ELSE または WHILE は、 THEN の直後にジャンプします(see Selection)。

BEGIN ( compilation – dest ; run-time –  ) core “BEGIN”

(コンパイル時に) dest を消費する UNTIL または AGAIN または REPEAT は、 BEGIN の直後へジャンプします(see Simple Loops)。

UNTIL ( compilation dest – ; run-time f –  ) core “UNTIL”

実行時、 f=0 の場合、 (コンパイル時に) dest をプッシュした BEGIN の直後から実行が続行されます。 それ以外の場合は、 UNTILの直後から実行が続行されます(see Simple Loops)。

AGAIN ( compilation dest – ; run-time –  ) core-ext “AGAIN”

実行時、 (コンパイル時に) dest をプッシュした BEGIN の直後から実行が続行されます(see Simple Loops)。

CS-PICK ( orig0/dest0 orig1/dest1 ... origu/destu u – ... orig0/dest0  ) tools-ext “c-s-pick”
CS-ROLL ( destu/origu .. dest0/orig0 u – .. dest0/orig0 destu/origu  ) tools-ext “c-s-roll”
CS-DROP ( dest –  ) gforth-1.0 “CS-DROP”

標準ワードの CS-PICKCS-ROLL を使用すると、 移植可能な方法で制御フロー・スタックを操作できます。 これら無しだと制御フロー・エントリが占めるスタック項目の数を知る必要があります(多くのシステムは 1 つのセルを使用します。 Gforth では現在 4 つを使用しますが、 これは将来変更される可能性があります)。

orig は 1 回だけ解決する必要があるため、 CS-PICKdest を pick することしかできず、 かつ、 CS-DROPdest を drop することしかできません。

一部の標準の制御構造ワードは、 以下のワード群から構築されます:

ELSE ( compilation orig1 – orig2 ; run-time –  ) core “ELSE”

実行時、 (コンパイル時に) orig を消費する THEN の直後から実行が続行されます。 orig1 をプッシュした IF または AHEAD または ELSE または WHILE は、 ELSE の直後にジャンプします(see Selection)。

WHILE ( compilation dest – orig dest ; run-time f –  ) core “WHILE”

実行時、 f=0 の場合、 (コンパイル時の) orig を消費する REPEAT (または THEN または ELSE) の直後から実行が継続されます。 それ以外の場合は、 WHILE の直後から実行されます(see Simple Loops)。

REPEAT ( compilation orig dest – ; run-time –  ) core “REPEAT”

実行時、 (コンパイル時に) dest をプッシュした BEGIN の直後から実行が続行されます。 orig をプッシュした WHILE または IF または AHEAD または ELSE は、 REPEAT の直後にジャンプします(see Simple Loops)。

Gforth は、さらにいくつかの制御構造ワードを追加します:

ENDIF ( compilation orig – ; run-time –  ) gforth-0.2 “ENDIF”

THEN と同一です。

?dup-IF ( compilation – orig ; run-time n – n|  ) gforth-0.2 “question-dupe-if”

これは、スタック・チェッカー(stack checker)などのツールでより適切に処理できるため、 イディオム「?DUP IF」の代替として推奨されます。 しかも、 ?DUP IF より速いです。

?DUP-0=-IF ( compilation – orig ; run-time n – n|  ) gforth-0.2 “question-dupe-zero-equals-if”

制御構造ワードのもう一つのグループ:

case ( compilation  – case-sys ; run-time  –  ) core-ext “case”

case 構造の開始。

endcase ( compilation case-sys – ; run-time x –  ) core-ext “end-case”

case 構造を終わらせます。 x を drop して、endcase の後ろへ進みます。 x の drop は、 元の(of のみの)case 構造では便利ですが、 他の場合(特に ?of を使用する場合)では(drop する為の) x を明示的に指定する必要がある場合があります。

next-case ( compilation case-sys – ; run-time –  ) gforth-1.0 “next-case”

一致する case にジャンプして、 case ループを再開します。 endcase とは異なり、 next-case はセルを drop しないことに注意してください。

of ( compilation  – of-sys ; run-time x1 x2 – |x1  ) core-ext “of”

x1=x2 の場合は続行します(両方を drop します)。 それ以外の場合は、 x1 をスタック上に残し、 endof または contof の後ろにジャンプします。

?of ( compilation  – of-sys ; run-time  f –  ) gforth-1.0 “question-of”

f が true の場合は続行します。 それ以外の場合は、 endof または contof の後ろにジャンプします。

endof ( compilation case-sys1 of-sys – case-sys2 ; run-time  –  ) core-ext “end-of”

endcase/next-case の後ろにジャンプして、 囲んでいる case 構造を終了(exit)します。

contof ( compilation case-sys1 of-sys – case-sys2 ; run-time  –  ) gforth-1.0 “cont-of”

囲んでいる case にジャンプして、 case ループを再開します。

内部的には、 of-sysorig で、 case-sys はセルとスタック深さ情報と、0 個以上の orig と、 dest です。

6.9.6.1 Programming Style

読みやすさを確保するために、 任意の制御構造を直接作成せず、 必要な制御構造に対して新しい制御構造ワードを定義し、 プログラム内でこれらのワードを使用することをお勧めします。たとえば、 以下のように書く代わりに:

BEGIN
  ...
IF [ 1 CS-ROLL ]
  ...
AGAIN THEN

以下のように制御構造のワードを定義することをお勧めします。 例:

: WHILE ( DEST -- ORIG DEST )
 POSTPONE IF
 1 CS-ROLL ; immediate

: REPEAT ( orig dest -- )
 POSTPONE AGAIN
 POSTPONE THEN ; immediate

そして、 次に、 これらを使用して制御構造を作成します:

BEGIN
  ...
WHILE
  ...
REPEAT

このほうがずっと読みやすいですよね。 もちろん、 REPEATWHILE は定義済みなので、 この例を見て改めて定義する必要はありません。


6.9.7 Calls and returns

呼び出す定義の名前を記述するだけで定義を呼び出すことができます。 通常、 定義はそれ自身の定義中は表示されません。 直接再帰的な定義を記述したい場合は、 recursive を使用して現在の定義を見えるようにする(使えるようにする)か、 recurse を使用して現在の定義を直接呼び出すことができます。

recursive ( compilation – ; run-time –  ) gforth-0.2 “recursive”

現在の定義中の定義をその定義内で呼び出せるように(表示できるように)し、 それ自体を再帰的に呼び出せるようにします。

recurse ( ... – ...  ) core “recurse”

現在の定義の別名(alias)。

これらのワードの使用例については See Recursion を参照してください。

プログラミング・スタイル・メモ: 著者は、recurse よりも recursive を使用することを好みます。 名前で定義を呼び出す方が、 やや難解な recurse よりも説明的であるためです(名前が適切に選択されていれば)。 たとえば、クイックソートの実装では、「今、再帰呼び出しを行う」(now do a recursive call)と読むよりも、「今、パーティションをソートする」(now sort the partitions)と読む(そして考える)方がはるかに優れています。

相互再帰(mutual recursion)の場合は、 以下のように defer ワードを使用します:

Defer foo

: bar ( ... -- ... )
 ... foo ... ;

:noname ( ... -- ... )
 ... bar ... ;
IS foo

defer された ワードについては、 Deferred Words で詳しく説明します。

定義の終わりに達するか、 EXIT に遭遇すると、 現在の定義は呼び出し元の定義に制御を返します。

EXIT ( compilation – ; run-time nest-sys –  ) core “EXIT”

呼び出し元の定義に戻る(return): 通常、 定義から速やかに戻るのを強制する方法として使用されます。 EXIT する前に、 リターン・スタックをクリーンアップし、 未処理の ?DO...LOOPUNLOOP する必要があります。 ローカル変数(local)が無い場合に exit のように動作するティック可能なワード(tickable word)には ;s を使用します。

?EXIT ( ) gforth-0.2 “?EXIT”

f が true の場合、 呼び出し元の定義に戻ります(return)。

;s ( R:w – ) gforth-0.2 “semis”

EXIT によってコンパイルされたプリミティブ。


6.9.8 Exception Handling

ワードが処理できないエラー状態を検出した場合、 例外を投げる(throw)ことができます。 最も単純なケースでは、 これによりプログラムが終了し、適切なエラーが報告されます。

throw ( y1 .. ym nerror – y1 .. ym / z1 .. zn error  ) exception “throw”

nerror が 0 の場合は、 それを drop して続行します。 それ以外の場合は、 動的に囲んでいる次の例外ハンドラー(next dynamically enclosing exception handler)に制御を移し、 それに応じてスタックをリセットし、 nerror をプッシュします。

fast-throw ( ... wball – ... wball ) gforth-experimental “fast-throw”

軽量の throw バリエーション: ゼロ以外のみに使用され、 バックトレースを保存したり、 欠落している catch を扱ったりしません。

throw は、スタック上のセル・サイズのエラー番号数値を消費します。 標準 Forth には事前定義されたエラー番号がいくつかあります(errors.fs 参照)。 Gforth (および他のほとんどのシステム)では、 さまざまなワードによって生成された ior をエラー番号として使用できます(たとえば、 allocate の一般的な使用法は allocate throw です)。 Gforth は、 (適切なエラー報告付きで)独自のエラー番号を定義するための Exception というワードも提供します。 このワードの標準 Forth バージョン(ただしエラー・メッセージなし) は compat/excel.fs で入手できます。 最後に、 あなた独自のエラー番号(-4095 〜 0 の範囲以外の任意の番号)を使用できますが、 表示されるのは適切なエラー・メッセージではなく、数字のみです。 たとえば、 以下のことを試してみてください:

-10 throw                    \ 標準で定義済
-267 throw                   \ システムで定義済
s" my error" exception throw \ ユーザー定義
7 throw                      \ 思いつくままの任意の番号
exception ( addr u – n  ) gforth-0.2 “exception”

n は、 -4095 〜 -256 の範囲内で以前に使用されていなかった throw 値です。 Exception を連続して呼び出すと、 連続して減少する数値が返されます。 Gforth は文字列 addr u をエラー・メッセージとして使用します(訳注: 同じエラーメッセージを使いまわすには、 s" hoge err msg" exception constant hoge-error hoge-error ! などとして得られた hoge-error を使いまわす。 hoge-error throw 等する)

エラー番号を文字列に変換するためのワード(通常は POSIX の strerror をモデルにしたもの)がある場合もあります。 以下のワードを使用すると、 これらの文字列を Gforth のエラー処理に取り込むことができます:

exceptions ( xt n1 – n2  ) gforth-1.0 “exceptions”

throw時: xt ( +n -- c-addr u ) は、 0<=n<n1 の範囲のローカルのエラー・コードをエラー・メッセージに変換します。 Exceptions は、 n2-n1<n3<=n2 の範囲で n1 個(0 〜 n1)のエラー・コードを予約します。 n2 に、 対応する Gforth エラー・コード(ローカルのエラー番号 0 に対応。 つまり、 0<=n<n1 の範囲のローカルのエラー・コードに 対応する Gforth エラー・コードは n2 <= n3 < n2+n1)を返します。 (後の時点で)その範囲に対応した Gforth エラー・コード n3 が throw されると、 n2-n3 がプッシュされ(つまりローカルのエラー番号に変換した値をプッシュし)、 xt が実行されて、 xt がエラー・メッセージを生成します(訳注: minos2/pulse-audio.fs 等で確認。 xt にC-interface ワードをセットし、 外部C言語ライブラリーのエラー・メッセージを gforth 内から表示するのに使っているようだ)

たとえば、 C言語ライブラリーの errno エラー (および strerror を使用した変換) がまだ Gforth で直接サポートされていないとした場合、 以下のようにして strerror を gforth と結び付けることができます:

' strerror 1536 exceptions constant errno-base
: errno-ior ( -- n )
\ n は errno の値に対応する Gforth ior を求めなければならないので、
\ ここでerrno 範囲と Gforth ior の範囲の間で変換する必要があります。
\ ERRNO は Gforth ワードではないため、
\ それにアクセスするには C インターフェイスを使用する必要があります。
  errno errno-base over - swap 0<> and ;

C言語の関数(C言語インターフェイス(C interface)を使用)を呼び出し、 その戻り値がエラーが発生したことを示している場合、 errno-ior throw を実行して、適切なエラー・メッセージ (“Permission denied” など)を含む例外を生成できます。

フラグが true の場合、 特定の err# でエラーを投げる(THROW)一般的な慣用句は以下のとおりです:

( flag ) 0<> err# and throw

あなたのプログラムで、 例外をキャッチする例外ハンドラーを提供できます。 例外ハンドラーを使用すると、 問題を修正したり、 一部のデータ構造をクリーンアップして例外を次の例外ハンドラーに投げたり(throw)することができます。 throw は動的に最も内側の例外ハンドラー(the dynamically innermost exception handler)にジャンプすることに注意してください。 システムの例外ハンドラーは最も外側にあり、 エラーを出力してコマンド・ラインの通訳(interpretation)を再開するだけです(または、 バッチ・モード(つまり、 シェル・コマンド・ラインの処理中)では Gforth を終了します)。

例外をキャッチする標準 Forth での方法は catch です:

catch ( x1 .. xn xt – y1 .. ym 0 / z1 .. zn error  ) exception “catch”

xt を実行します。 実行から正常に戻った場合、 catch はスタックに 0 をプッシュします。 throw を介して実行が戻った場合、 すべてのスタックは catch へ入る時点の深さにリセットされ、 TOS (xt の位置) は throw コードに置き換えられます。

nothrow ( ) gforth-0.7 “nothrow”

再 throw しない catch または endtry の後ろでこれ (または標準のシーケンス ['] false catch 2drop) を使用します。 これにより、 次の throw でバックトレースが確実に記録されます。

例外ハンドラーの最も一般的な使用法は、 エラーが発生したときに状態をクリーンアップすることです。 例:

base @ >r hex \ actually the HEX should be inside foo to protect
              \ against exceptions between HEX and CATCH
['] foo catch ( nerror|0 )
r> base !
( nerror|0 ) throw \ pass it on

myerror というエラー番号を処理するための catch の使用は以下のようになります:

['] foo catch
CASE
  myerror OF ... ( do something about it ) nothrow ENDOF
  dup throw \ default: pass other errors on, do nothing on non-errors
ENDCASE

コードを別のワードでくるむ要があるのは面倒な場合が多いため、 Gforth では代替構文を提供しています:

TRY
  code1
  IFERROR
    code2
  THEN
  code3
ENDTRY

これは、 code1 を実行しします。 code1 が正常に完了すると、 code3 の実行へ続きます。 code1 または、 endtry より前で例外があった場合、 スタックは try 時の深さにリセットされ、 throw された値をデータ・スタックにプッシュし、 code2 の実行に続き、 そして、 最終的に code3 に到達します。

try ( compilation  – orig ; run-time  – R:sys1  ) gforth-0.5 “try”

例外キャッチ領域の開始

endtry ( compilation  – ; run-time  R:sys1 –  ) gforth-0.5 “endtry”

例外キャッチ領域の終わり

iferror ( compilation  orig1 – orig2 ; run-time  –  ) gforth-0.7 “iferror”

例外処理コードを開始します(tryendtry の間に例外がある場合に実行されます)。 この部分は then で終了する必要があります。

code2 が必要ない場合は、 iferror then の代わりに restore を記述できます:

TRY
  code1
RESTORE
  code3
ENDTRY

先程の例をこの構文で身綺麗にしてみます:

base @ { oldbase }
TRY
  hex foo \ now the hex is placed correctly
  0       \ value for throw
RESTORE
  oldbase base !
ENDTRY
throw

このバリエーションの追加の利点は、 restoreendtry の間の例外(たとえば、 ユーザーが Ctrl-C を押すことによる例外)でも、 restore の直後へコードの実行が移ることです。 ゆえに、 いかなる状況であっても base は復元されます。

ただし、 このコード自体が例外を引き起こさないようにする必要があります。 そうしないと、 iferror/restore コードがループします。 さらに、 iferror/restore コードで必要なスタックの内容が tryendtry の間のあらゆる場所に存在することも確認する必要があります。 この例では、 これは try の前にデータをローカル変数(local)に置くことによって実現されます(リターン・スタック上の例外フレーム(sys1)が邪魔なのでリターン・スタックは使用できません)。

この種の使用法は、 Lisp の unwind-protect と同様のものです。

もし、あなたが、 この例外再開始(exception-restarting)の振る舞いを望まない場合は、 以下のようにしてください:

TRY
  code1
ENDTRY-IFERROR
  code2
THEN

code1 に例外がある場合は code2 が実行され、 それ以外の場合は then の後ろ (または else 分岐の可能性あり) から実行が続行されます。 これはバージョン 0.7 より前の Gforth では以下の構成要素に該当します

TRY
  code1
RECOVER
  code2
ENDTRY

つまり、 この recover を使用しているコードを直に try ... entry-iferror ... then へと置き換えることができます。 ただし、 その置き換え作業中に他の try バリエーションのいずれかを使用した方が良いかどうかも検討することをお勧めします。

移行を容易にするために、 Gforth は 2 つの互換性ファイルを提供します: 1つ目は endtry-iferror.fs で、 古いシステム用に try ... endtry-iferror ... then 構文を提供します(ただし、 iferror または restore は提供しません)。 2つ目の recover-endtry.fs は、 新しいシステム上で古い構文の try ... recover ... endtry 構文を提供するので、 古いプログラムを実行するための一時しのぎとして使用できます。 どちらのファイルもどのシステムでも動作します(システムが、 実装する構文を既に定義済みの場合は何も行わないだけです)。 そのため、 古いシステムと新しいシステムを混在させて使用している場合でも、 これらのファイルのいずれかを無条件に require することができます。

restore ( compilation  orig1 – ; run-time  –  ) gforth-0.7 “restore”

コードの復元(restore)を開始します。 これは、 例外がある場合と無い場合に行われます。

endtry-iferror ( compilation  orig1 – orig2 ; run-time  R:sys1 –  ) gforth-0.7 “endtry-iferror”

例外キャッチ領域を終了し、 その領域外で例外処理コードを開始します(tryendtry-iferror の間に例外がある場合に実行されます)。 この部分は then (または else...then) で終了する必要があります。

ここで、 エラー処理の例を以下に示します:

TRY
  foo
ENDTRY-IFERROR
  CASE
    myerror OF ... ( do something about it ) nothrow ENDOF
    throw \ pass other errors on
  ENDCASE
THEN

プログラミング・スタイル・メモ: いつものように、 エラーを渡すための throw の後、 または ENDTRY の後のいずれか(または、catch を使用する場合は、エラーを処理するための選択構造の終了後)で、 スタックの深さが静的に明白であることを保証する必要があります。

throw の代替は 2 つあります: Abort" は条件付きでエラー・メッセージを提供できます。 Abort は「中止」(Abort)エラーを生成するだけです。

これらのワードの問題は、 例外ハンドラーが、 異なる abort" を区別できないことです。 例外ハンドラーにとってはそれらは -2 throw のように見えるだけです(標準のプログラムではエラー・メッセージにアクセスできません)。 同様に、 abort は例外ハンドラーに対して -1 throw のように見えます。

ABORT" ( compilation ’ccc"’ – ; run-time f –  ) core,exception-ext “abort-quote”

f のいずれかのビットがゼロ以外の場合、 -2 throw の機能を実行し、 例外スタック上例外フレームがない場合は文字列 ccc を表示します。

abort ( ?? – ??  ) core,exception-ext “abort”

-1 throw.

実行を中止する必要があるほど深刻でない問題の場合は、 警告を表示するだけで済みます。 変数 warnings を使用すると、 表示される警告の数を調整できます。

WARNING" ( compilation ’ccc"’ – ; run-time f –  ) gforth-1.0 “WARNING"”

f がゼロ以外の場合、 警告メッセージとして文字列 ccc を表示します。

warnings ( – addr  ) gforth-0.2 “warnings”

以下の警告レベルをセットしてください

0

警告オフ

-1

通常警告オン

-2

初心者警告オン

-3

偏執狂的警告オン

-4

全ての警告をエラーとして扱います(初心者警告を含む)


6.10 Defining Words

定義ワード(defining word)は、 ディクショナリーに新しいエントリを作成することによって Forth を拡張するために使用されます。


6.10.1 CREATE

定義ワードは、 ディクショナリーに新しいエントリを作成するために使用されます。 最も単純な定義ワードは CREATE です。 CREATE は以下のように使用します:

CREATE new-word1

CREATE はパース・ワード(parsing word)です。 つまり、入力ストリームから引数(argument)を受け取ります(この例では new-word1 です)。 CREATEnew-word1 のディクショナリー・エントリを作成します。 new-word1 が実行される時は、 アドレスがスタックに残されるだけです。 そのアドレスは、 new-word1 が定義された時点のデータ空間ポインター(HERE)の値を表します。したがって、 CREATE は名前をメモリー領域のアドレスに関連付ける方法です。

Create ( "name" –  ) core “Create”

注意: 標準 Forth は、 create に対してのみ、 そのボディ部分がディクショナリのデータ空間(つまり、 hereallot などが機能する空間。 see Dictionary allocation)にあることを保証することに注意してください。 また、 標準 Forth では、 does> で変更できるのは create で作成されたワードのみで(see User-defined Defining Words)、 標準 Forth の >bodycreate されたワードにのみ適用できます。

この new-word1 の例を拡張して、 データ空間にメモリーを少々確保すると、 最終的には variable のような代物になります。 これを行う 2 つの異なる方法を以下に示します:

CREATE new-word2 1 cells allot  \ 1 セル予約 - 初期値未定義
CREATE new-word3 4 ,            \ 1 セル予約 かつ (4で)初期化

これらの変数は、 以下のように @ (「フェッチ」(fetch;取り出す)) と ! (「ストア」(store;格納する)) を使用して検査(examine)および変更(modify)できます:

new-word2 @ .      \ get address, fetch from it and display
1234 new-word2 !   \ new value, get address, store to it

同様のメカニズムを使用して配列を作成できます。 たとえば、 80 文字のテキスト入力バッファーです:

CREATE text-buf 80 chars allot

text-buf 0 chars + c@ \ the 1st character (offset 0)
text-buf 3 chars + c@ \ the 4th character (offset 3)

メモリーに適切な領域を割り当てることで、 思いつく限りの複雑なデータ構造を構築できます。 これについてさらに詳しく説明し、 それを容易にする Gforth ツールについて知りたい場合は、 See Structures を参照ください。


6.10.2 Variables

前のセクションでは、 一連のコマンドを使用して変数を生成する方法を説明しました。 最終的な改良として、 (次のセクションの主題を先取りして)そのコード・シーケンス全体を定義ワードでまとめることができ、 新しい変数の作成が容易になります:

: myvariableX ( "name" -- a-addr ) CREATE 1 cells allot ;
: myvariable0 ( "name" -- a-addr ) CREATE 0 , ;

myvariableX foo \ variable foo starts off with an unknown value
myvariable0 joe \ whilst joe is initialised to 0

45 3 * foo !   \ set foo to 135
1234 joe !     \ set joe to 1234
3 joe +!       \ increment joe by 3.. to 1237

当然のことながら、 Forth にはすでに Variable の定義があるため、 myvariable を定義する必要はありません。 標準 Forth は、 Variable が作成時に初期化されることを保証しません(つまり、 myvariableX のように振る舞う可能性があります)。 対照的に、Gforth の Variable は変数を 0 に初期化します(つまり、 myvariable0 とまったく同じように振る舞います)。 Forth は、 2倍長変数と浮動小数点変数に対して、 それぞれ 2Variablefvariable も提供します。 これらは、Gforth ではそれぞれ 0. と 0e に初期化されます。 Variable を使用してブール値を保存する場合、 onoff を使用してその状態を切り替えることができます。

Variable ( "name" –  ) core “Variable”

name を定義し、 addr で始まるセルを予約します。 name 実行時: ( -- addr )

AVariable ( "name" –  ) gforth-0.2 “AVariable”

variable と同様に機能しますが、 (クロス・コンパイルされたコードで使用される場合)その変数に格納されているセルがアドレスであることをクロス・コンパイラーに伝えます。

2Variable ( "name" –  ) double “two-variable”
fvariable ( "name" –  ) floating “f-variable”

最後に、 任意の長さのバッファーは以下のようになります

buffer: ( u "name" –  ) core-ext “buffer-colon”

name を定義し、addr から始まる u バイトを予約します。 name 実行時: ( -- addr ) なお、 Gforth は予約したバイトを 0 に初期化しますが、 標準では保証されません。


6.10.3 Constants

constant を使用すると、 固定値を宣言し、 名前でそれを参照できます。 例:

12 Constant INCHES-PER-FOOT
3E+08 fconstant SPEED-O-LIGHT

Variable は読み取りと書き込みの両方ができるため、 その実行時の振る舞いは、 現在の値を操作できるアドレスを提供することです。 それとは対照的に、 Constant の値は一度宣言すると変更できないため、 アドレスを指定する必要はありません14 – 定数の値を直接返す方が効率的です。 そして正にそのとおりになります。 つまり、 定数の実行時の効果は、その値をスタックの頂上に置くことです((Constant を実装する方法の1つは User-defined Defining Words で見つけることができます)。

Forth は、それぞれ2倍長定数と浮動小数点定数を定義するための 2Constantfconstant も提供します。

Constant ( w "name" –  ) core “Constant”

定数 name を値 w で定義します。

name 実行時: – w

AConstant ( addr "name" –  ) gforth-0.2 “AConstant”

constant と似ていますが、 アドレスのための定数を定義します(これはクロス・コンパイラーでのみ違いが生じます)。

2Constant ( w1 w2 "name" –  ) double “two-constant”
fconstant ( r "name" –  ) floating “f-constant”

Forth の定数は、他のプログラミング言語の定数とは異なる振る舞いをします。 他の言語では、 定数(アセンブラーの EQU や C の #define など)はコンパイル時にのみ存在します。 実行プログラム(executable program)では、 定数は即値(absolute number)に変換されているため、 シンボリック・デバッガを使用しない限り、 その数値がどのような抽象的なものを表しているかを知ることは不可能です。 Forth では、 定数はヘッダー空間にエントリを持ち、 それを使用するコードが定義された後もそこに残ります。 実際、 実行時に機能する義務があるため、 それをディクショナリに残しておく必要があります。 例:

12 Constant INCHES-PER-FOOT
: FEET-TO-INCHES ( n1 -- n2 ) INCHES-PER-FOOT * ;

ここで、 FEET-TO-INCHES が実行されると、 定数 INCHES-PER-FOOT に関連付けられた xt が実行されます。 see を使用して FEET-TO-INCHES の定義を逆コンパイルすると、 INCHES-PER-FOOT を呼び出していることがわかります。 一部の Forth コンパイラーは、定数を使用する場所にインライン展開(in-lining)することによって定数を最適化しようとします。 以下のようにして Gforth に定数をインライン化するように強制できます:

: FEET-TO-INCHES ( n1 -- n2 ) [ INCHES-PER-FOOT ] LITERAL * ;

ここで、 see を使用して FEET-TO-INCHES の このバージョンを逆コンパイルすると、 INCHES-PER-FOOT が存在しないことがわかります。 これがどのように機能するかを理解するには、 Interpret/Compile statesLiterals を読んでください。

この方法で定数をインライン化すると、 実行時間がわずかに改善される可能性があり、 定数がコンパイル時にのみ参照されるようにすることができます。 ただし、 定数の定義はまだディクショナリーに残っています。 一部の Forth コンパイラーは、 一時的なワード(transient words)を保持する 2 番目のディクショナリーを制御するメカニズムを提供し、 メモリー領域を回復するために後でこの 2 番目のディクショナリーを削除できるようしています。 ただし、 これを行う標準の方法はありません。


6.10.4 Values

ValueConstant のように動作しますが、 変更することができます。 TO は、Values を変更するパース・ワード(parsing word)です。 (標準 Forth ではなく) Gforth では、 >body を使用しても value にアクセス(および変更)できます。

ここで幾つか例を示します:

12 Value APPLES     \ APPLES を初期値 12 で定義
34 TO APPLES        \ APPLES の値を変更。 TO はパース・ワード
1 ' APPLES >body +! \ APPLES をインクリメント。 非標準の使い方
APPLES              \ スタック頂上に 35 を置く(はず)
Value ( w "name" –  ) core-ext “Value”

name を初期値 w で定義します。 この値は to name または ->name で変更できます(訳注: -> と name の間に空白を開けない。 ->name とする。 ワードではなく、 テキスト・インタープリターの認識器(recognizer)機能によるもの)。

name 実行時: – w2

AValue ( w "name" –  ) gforth-0.6 “AValue”

value と似ていますが、 アドレスの為の値を定義します(これはクロス・コンパイラーでのみ違いが生じます)。

2Value ( d "name" –  ) double-ext “two-value”
fvalue ( r "name" –  ) floating-ext “f-value”

実行時: ( -- r1 )name を定義します。 ここで r は初期値です。 値は to name または ->name で変更できます。

TO ( value "name" –  ) core-ext “TO”

name の値を value に変更します

+TO ( value "name" –  ) gforth-1.0 “+TO”

name の値に value を足し込みます


6.10.5 Varues

(訳注: vaLue ではなくて vaRue) value のようなワードでアドレスを取得したい場合があります。 これにはいくつかの欠点があるため、 Gforth では、 これについて明示的に指定し、 varueを使用して名前を宣言するように求めます(variable と value の特性を組み合わせたものであるため、 そのように名付けられました)。

Varue ( w "name" –  ) gforth-1.0 “Varue”

value と似ていますが、 addr name で得たアドレスで値にアクセスすることもできます。 将来的には、 varues の効率が values よりも低くなる可能性があります。

2varue ( x1 x2 "name" –  ) gforth-1.0 “2varue”

2value と似ていますが、 addr name で得たアドレスで値にアクセスすることもできます。 将来的には、2varues は 2values よりも効率が低くなる可能性があります。

fvarue ( r "name" –  ) gforth-1.0 “fvarue”

fvalue と似ていますが、 addr name で得たアドレスで値にアクセスすることもできます。 将来的には、fvarues は fvalues よりも効率が低くなる可能性があります。

addr ( "name" – addr  ) gforth-1.0 “addr”

varue name または 2varue name または fvarue name のアドレスを提供します。 または wa: ca: da: fa: xta: のいずれかで定義されたローカル変数 name のアドレスを提供します。


6.10.6 Colon Definitions

: name ( ... -- ... )
    word1 word2 word3 ;

name というワードを作成し、 実行時に word1 word2 word3 を実行します。 name は 定義(コロン定義)((colon) definition) です。

上記の説明はやや表面的です。 コロン定義の簡単な例については、 Your first Forth definition を参照してください。 関連する問題の一部についての詳細な説明については、 See Interpretation and Compilation Semantics を参照してください。

: ( "name" – colon-sys  ) core “colon”
; ( compilation colon-sys – ; run-time nest-sys –  ) core “semicolon”

最終的には自動インライン化を実行する予定ですが、 今のところは以下のようにしてインライン化を実行できます

inline: ( "name" – inline:-sys  ) gforth-experimental “inline-colon”

インライン・コロン定義を開始します。 inline:;inline の間のコードは、 インライン化するコードを(実行するのではなく)コンパイルする必要がありますが、 結果の定義 name は、 インライン化されたコードを実行するコロン定義です。 コンパイルするコードはスタック効果が( -- ) (スタックの深さが変わらない)である必要があることに注意してください。 さもないと、 Gforth が name のコロン定義を作成しようとしたときにエラーが発生します。

;inline ( inline:-sys –  ) gforth-experimental “semi-inline”

inline: で始まるインライン定義を終了します

例として、 インライン化されたワードを定義し、 以下のようにして使います

inline: my2dup ( a b -- a b a b )
    ]] over over [[ ;inline

#1. my2dup d. d.
: foo my2dup ;
#1. foo d. d.
see foo

インライン・ワードはマクロ(see Macros)に関連しています。 マクロとの違いは、マクロには即時コンパイル機能(immediate compilation semantics)があるのに対し、 inline: で定義されたワードにはデフォルトのコンパイル機能(compilation semantics)があることです。 つまり、 通常はコロン定義内でのみマクロを使用しますが、 inline: ワードは対話的(interpretively)にも使用できることを意味します。 しかしそれは、 inline: ワードとしては実行できないいくつかのことをマクロでは実行できることも意味します。 例:

\ Doesn't work:
\   inline: endif ]] then [[ ;inline
\ Instead, write a macro:
: endif ]] then [[ ; immediate

逆に、 非即時コロン定義(non-immediate colon definitions)として問題ないワードについては、 非即時コロン定義として定義するか、 (最大限のパフォーマンスが必要な場合) inline: ワードとして定義します。 それらをマクロとして定義しないでください。 対話的(interpretively)に適切に使用できなくなります:

: another2dup ]] over over [[ ; immediate
\ Doesn't work:
\   #1. another2dup d. d.

なぜ inline:;inline の間にコンパイル・コードを書かなければならないのか疑問に思われるかもしれません。 これは、 上記の my2dup のようなインライン・ワードの実装が以下のように動作するためです:

: compile-my2dup ( xt -- )
    drop ]] over over [[ ;

: my2dup [ 0 compile-my2dup ] ;
' compile-my2dup set-optimizer

DROP0 があるのは、 compile-my2dupmy2dup ための compile, の実装であり、 compile, は xt を期待する為です(see User-defined compile,)。


6.10.7 Anonymous Definitions

しばしば匿名のワード(anonymous word)を定義したい場合があります。 つまり、 名前無しのワードです。 これは以下のようにします:

:noname ( – xt colon-sys  ) core-ext “colon-no-name”

これにより、 終わりの ; の後にワードの実行トークンがスタックに残ります。 以下は、 defer された ワード(deferred word)が匿名コロン定義(anonymous colon definition)の xt で初期化される例です:

Defer deferred
:noname ( ... -- ... )
  ... ;
IS deferred

Gforth は、 2 つの別々のワードを使用して、 これを行う別の方法を提供します:

noname ( ) gforth-0.2 “noname”

次に定義するワードは匿名になります。 その定義するワードは(nonameがいじる訳ではなくて)入力ストリームからのをそのままを使います(The defining word will leave the input stream alone)。 その定義したワードの xt は latestxt で取得します。

latestxt ( – xt  ) gforth-0.6 “latestxt”

xt は、 最後に定義されたワードの実行トークンです。

先の例を、 nonamelatestxt を使用して書き直すことができます:

Defer deferred
noname : ( ... -- ... )
  ... ;
latestxt IS deferred

noname は、 : だけでなく、 あらゆる定義ワードで機能します。

latestxt は、 最後のワードが noname として定義されていない場合にも機能します。 ただし、 複合ワード(combined words)には機能しません。 また、 これは、 定義のヘッダーが構築されるやいなや有効になる便利なプロパティでもあります。 したがって、 以下のようにすると:

latestxt . : foo [ latestxt . ] ; ' foo .

これは3つの数値を出力: 後ろの2つは同一の数値です。


6.10.8 Quotations

引用(quotation)は、 別のコロン定義内の匿名コロン定義です。 引用(quotation)は、 catchoutfile-execute など、 実行トークンを消費するワードを扱うときに便利です。 例えば、 以下の outfile-execute (see Redirection) の使用例を考えてみましょう:

: some-warning ( n -- )
    cr ." warning# " . ;

: print-some-warning ( n -- )
    ['] some-warning stderr outfile-execute ;

上記のように、 some-warning をヘルパー・ワードとして定義し、 その xt を outfile-execute に渡すことができます。 その代わりに、 引用(quotation)を使用して print-some-warning 内でそのようなワードを匿名で定義できます:

: print-some-warning ( n -- )
  [: cr ." warning# " . ;] stderr outfile-execute ;

引用(quotation)は [:;] で囲まれています。 それは実行時に実行トークンを生成します。

[: ( compile-time: – quotation-sys flag colon-sys  ) gforth-1.0 “bracket-colon”

引用(quotation)を開始します

;] ( compile-time: quotation-sys – ; run-time: – xt  ) gforth-1.0 “semi-bracket”

引用(quotation)を終了します


6.10.9 Supplying the name of a defined word

デフォルトでは、 定義ワードは入力ストリームから定義されるワードの名前を取得します。 しばしば文字列から名前を指定したい場合があります。 これは以下のようにして行うことができます:

nextname ( c-addr u –  ) gforth-0.2 “nextname”

次に定義されるワードの名前は c-addr u になります。 定義中のワードは入力ストリームからそのままで nextname がいじることはありません。

例:

s" foo" nextname create

これは以下と同等です:

create foo

nextname は、 あらゆる定義ワードで機能します。


6.10.10 User-defined Defining Words

既存の定義ワードに基づいて新しい定義ワードを定義できますが、 :create...does>/set-does> は特に柔軟です。 一方、 たとえば、 constant の子供達は全て単なる定数です。


6.10.10.1 User-defined defining words with colon definitions

既存の定義ワードを定義時(defining-time)コードで取り囲み、 その取り囲んだ一連のコードをコロン定義に入れることで、 新しい定義ワードを作成できます。

たとえば、 定義の xt を指定してコロン定義に関する統計を収集するワード stats があり、 アプリケーション内のすべてのコロン定義で stats を呼び出す必要があるとします。 これには、 以下のように : の新しいバージョンを定義して使用できます:

: stats
  ( xt -- ) DUP ." (Gathering statistics for " . ." )"
  ... ;  \ other code

: my: : latestxt postpone literal ['] stats compile, ;

my: foo + - ;

my: を使用して foo を定義する場合、 以下のステップが実行されます:

  • my: が実行されます。
  • 定義内の : (my:latestxt の間にあるもの) が実行され、 : として、 いつもと同じことを行います。 名前を得るために入力ストリームをパースし、 名前 foo のディクショナリー・ヘッダーを構築し、 state をインタープリター状態からコンパイル状態に切り替えます。
  • latestxt というワードが実行されます。 定義中のワード(foo)の xt をスタックに置きます。
  • postpone literal によって生成されたコードが実行されます。 これにより、 スタック上の値(fooxt)が foo のコード領域にリテラルとしてコンパイルされます。
  • コード ['] stats は、 my: の定義時に、 リテラルを my: の定義内にコンパイルします(訳注: そして、 my: の実行時は statsxt をスタックに積みます)。 そして、 compile, が実行されると、 foo のコード領域に、 上記に続けて statsxt をコンパイルします15
  • この時点で、 my: の実行が完了し、 制御がテキスト・インタープリターに戻ります。 テキスト・インタープリターはコンパイル状態にあるため、 後続のテキスト + -foo の定義にコンパイルされ、 いつものように ; によって定義が終了します。

see を使用すると、 my: を使用して定義されたワードを逆コンパイルし、 それが通常の : 定義とどのように異なるかを確認できます。 例:

: bar + - ;  \ like foo but using : rather than my:
see bar
: bar
  + - ;
see foo
: foo
  `foo stats + - ;

`foo['] foo を記述する別の方法です。


6.10.10.2 User-defined defining words using create

定義ワードで定義されたワードを、 標準の定義ワードで定義されたワードとは異なる振る舞いにしたい場合は、 以下のように定義ワードを記述できます:

: def-word ( "name" -- )
    CREATE code1
DOES> ( ... -- ... )
    code2 ;

def-word name

このコード断片は 定義ワード(defining word) def-word を定義し、 そして、 それを実行します。 def-word が実行されると、 新しいワード nameCREATE され、コード code1 が実行されます。 コード code2 は現時点では実行されません。 name というワードは、 def-word の子供(child)と呼ばれることもあります。

name を実行すると、 name の本体のアドレスがデータ・スタックに置かれ、 code2 が実行されます(name の本体のアドレスは、 CREATE の直後に HERE が返すアドレス、 つまり、 create されたワードがデフォルトで返すアドレスです)。

def-word を使用して、 同様に動作する一連の子ワード達を定義できます。 つまり、 これらはすべて code2 によって決定される共通の実行時の振る舞いを持っています。 通常、 code1 シーケンスは、 子ワードの本体にデータ領域を構築します。 データの構造は def-word のすべての子に共通ですが、 データ値は各子ワードに固有で、 そして、 プライベートです。 子ワードが実行されると、 そのプライベート・データ領域のアドレスが TOS 上のパラメーターとして渡され、 code2 によって使用および操作されます16

定義ワードを構成する 2 つのコード断片は、 完全に別々の 2 つの時点で動作(実行)されます:

  • 定義時 、定義ワードが code1 を実行して子ワードを生成します
  • 子の実行時、 子ワードが呼び出されると、 子ワード固有のプライベートなパラメータ達(データ)を使って、 code2 が実行されます。

def-wordname の振る舞いを理解するもう 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-wordCREATE されたワードであった場合、 単純にそれを実行してデータ・フィールドのアドレスを取得できます。 ただし、 DOES> アクションを持つように定義されているため、 その実行機能(execution semantics)は、 それらの DOES> アクションを実行することになります。 データ・フィールドのアドレスを取得するには、 ' を使用して xt を取得し、 次に >BODY を使用して xt をデータ・フィールドのアドレスに変換する必要があります。 first-word を実行すると、 123 が表示されます。 second-word を実行すると、-1 が表示されます。

上記の例では、 DOES> の後のスタック・コメントは、 DOES> に続くコードのスタック効果ではなく、 定義されたワードのスタック効果を指定しています(DOES> に続くコードは、 本体のアドレスがスタックの先頭にある事を期待しています。 これはスタック・コメントには反映されません)。 これは著者が使用し、 推奨している規則です(ただし、 スタック効果の指定にローカル変数宣言を使うのと少々衝突する)。


6.10.10.3 Applications of CREATE..DOES>

あなたは、 この機能をどのように使用するのか不思議に思うかもしれません。 いくつかの使用パターンを以下に示します:

とあるコードのフレーズが複数回出現し、 その意味を同一視できる場合は、 それをコロン定義としてくくり出します。 類似のコロン定義が見つかった場合は、 CREATE..DOES> を使用してそれらをファクタリングできます。 たとえば、 アセンブラは通常、 非常によく似たいくつかのワードを定義します:

: ori, ( reg-target reg-source n -- )
    0 asm-reg-reg-imm ;
: andi, ( reg-target reg-source n -- )
    1 asm-reg-reg-imm ;

これは以下のようにファクタリングできます:

: reg-reg-imm ( op-code -- )
    CREATE ,
DOES> ( reg-target reg-source n -- )
    @ asm-reg-reg-imm ;

0 reg-reg-imm ori,
1 reg-reg-imm andi,

CREATE..DOES> の別の観点は、 これをワードのパラメーターの一部を提供する素朴な方法(関数型言語コミュニティではカリー化(currying)として知られています)とみなすことです。 たとえば、 + には 2 つのパラメーターが必要ですが、そのうち 1 つのパラメーターを固定した + のバージョンを作成するには、 以下のようにします:

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

 3 curry+ 3+
-2 curry+ 2-

6.10.10.4 The gory details of CREATE..DOES>

DOES> ( compilation colon-sys1 – colon-sys2  ) core “does”

以下は、 一つの定義内で CREATEDOES> の両方を使用する必要がないことを意味します。 DOES> 部分を別の定義に置くことができます。 これにより、 たとえば、 さまざまな DOES> 部分から選択するようにすることができます:

: does1 
DOES> ( ... -- ... )
    code1 ;

: does2
DOES> ( ... -- ... )
    code2 ;

: def-word ( ... -- ... )
    create ...
    IF
       does1
    ELSE
       does2
    ENDIF ;

この例では、 does1does2 のどちらを使用するかの選択は、 子ワードが CREATE される定義時に行われます。

注意: 定義を終了する does> の性質により、 追加の定義 does1does2 を導入する必要があることに注意してください。 set-does> を使用するとこれを回避できます:

: def-word ( ... -- ... )
    create ...
    IF
       [: code1 ;] set-does>
    ELSE
       [: code2 ;] set-does>
    ENDIF ;

標準のプログラムでは、 最後のワードが CREATE で定義されている場合にのみ DOES> 部分を適用できます。 Gforth では、 DOES> 部分は、 いかなる場合でも定義された最後のワードの振る舞いをオーバーライドします。 標準のプログラムでは、 DOES> はコロン定義でのみ使用できます。 Gforth では、 一種のワンショット・モードとして、 インタープリター状態で使用することもできます。 例:

CREATE name ( ... -- ... )
  initialization
DOES>
  code ;

これは、 以下の標準のと同等です:

:noname
DOES>
    code ;
CREATE name EXECUTE ( ... -- ... )
    initialization

Gforth は対話時に打ち込んだコード内での引用(quotations)もサポートしており、 引用は現在の定義を保存および復元するため、 上記の例を以下のように記述することもできます:

CREATE name ( ... -- ... )
  initialization
[: code ;] set-does>
set-does> ( xt –  ) gforth-1.0 “set-does>”

現在のワードを変更して、 本体アドレスをプッシュしてから xt を実行します。 それに応じて compile, の実装も変更します。 より効率的な実装が必要な場合、 この後で set-optimizer を呼び出します。

>body ( xt – a-addr  ) core “to-body”

xt で表されるワードの本体(body)のアドレス(ワードのデータ・フィールドのアドレス)を取得します。


6.10.10.5 Advanced does> usage example

MIPS 逆アセンブラー(arch/mips/disasm.fs)には、 非常に反復的なスキームに従って逆アセンブルするための多くのワードが含まれています(訳注: very repetetive scheme; repetitiveのスペルミスっぽい):

:noname disasm-operands s" inst-name" type ;
entry-num cells table + !

もちろん、 これは共通点をくくり出して以下のような定義を可能にするというアイデアを刺激します:

disasm-operands entry-num table define-inst inst-name

通常、 パラメーター disasm-operandstable は相関しています。 さらに、 著者が逆アセンブラーを作成する前に、 以下のような命令を定義するコードがすでに存在していました:

entry-num inst-format inst-name

このコードは assembler 由来で、 arch/mips/insts.fs にあります。

したがって、 実行時に上記のスキームを実行する inst-format ワードを定義する必要がありました。 著者は、 まず最初に、ランタイム・コード生成を使用することを選択しました:

: inst-format ( entry-num "name" -- ; compiled code: addr w -- )
  :noname Postpone disasm-operands
  name Postpone sliteral Postpone type Postpone ;
  swap cells table + ! ;

注意: これにより、 上記のスキームの他に 2 つのパラメーターが提供されることに注意してください。

別の方法として、 create/does> を使用してこれを記述することもできます:

: inst-format ( entry-num "name" -- )
  here name string, ( entry-num c-addr ) \ parse and save "name"
  noname create , ( entry-num )
  latestxt swap cells table + !
does> ( addr w -- )
  \ disassemble instruction w at addr
  @ >r 
  disasm-operands
  r> count type ;

どういうわけか、 最初の解決策の方が簡単です。 その主な理由は、 string, やその友達を使用するよりも、 sliteral を使用した方が、 文字列を定義時から使用時へシフトするのが簡単だからです。

私はこのスキームに従ってたくさんのワードを書き、 すぐにそれらの共通点を取り出すことを考えました。 これは 2 レベルの定義ワード、 つまり通常の定義ワードを定義するワードを使用していることに注意してください。

今回は、 postpone とそのファミリーが関与する解決策はより困難に思えたので(研究課題として試してみましょう)、 create/does> というワードを使用することにしました。 私はすでにそれを行っていたので、 下位レベルにも create/does> を使用しました(研究課題として postpone などを使用してみましょう)。 その結果、 以下の定義が得られました:

: define-format ( disasm-xt table-xt -- )
    \ 逆アセンブルに disasm-xt を使用する命令フォーマットを定義し、
    \ 定義された命令を
    \ 表(table) table-xt に入れます
    create 2,
does> ( u "inst" -- )
    \ 命令 inst を逆アセンブルする匿名ワードを定義し、
    \ それを u 番目のエントリとして table-xt に入れます
    2@ swap here name string, ( u table-xt disasm-xt c-addr ) \ remember string
    noname create 2,      \ define anonymous word
    execute latestxt swap ! \ enter xt of defined word into table-xt
does> ( addr w -- )
    \ disassemble instruction w at addr
    2@ >r ( addr w disasm-xt R: c-addr )
    execute ( R: c-addr ) \ disassemble operands
    r> count type ; \ print name 

注意: ここでのテーブルは(上記とは対照的に) cells + を単独で実行することに注意してください (そのため、xt を渡す必要があります)。 このワードは以下のように使用されます:

' disasm-operands ' table define-format inst-format

上に示したように、 定義された命令フォーマットは以下のように使用されます:

entry-num inst-format inst-name

カリー化に関しては、 この種の 2 レベルの定義ワードは 3 段階でパラメーターを提供します。 最初に disasm-operandstable、 次に entry-numinst-name 、最後に addr w つまり、 逆アセンブル対象の命令です。

もちろん、 これは insts.fs で使用されるすべての命令フォーマット名に完全に適合するわけではないため、 パラメーターを正しい形式に条件付けるいくつかのラッパーを定義する必要がありました。

あなたが、 このセクションを理解するのが難しい場合でも、 心配する必要はありません。 まず、 これは複雑であり、 理解するのに時間がかかります(おそらく多少いじくりまわすことも必要です)。 2 番目に、 これは私が Forth の 17 年間で書いた最初の 2 レベルの create/does> ワードです。 また、 最初に insts.fs がなかった場合は、 1 レベルの定義ワードのみを使用することを選択した可能性があります(定義ワードを使用するときにパラメーターをいくつか繰り返します)。 したがって、 これを理解する必要はありませんが、 あなたの Forth についての理解が深まるかもしれません。


6.10.10.6 User-defined to and defer@

Gforth の value にはいくつかの操作子(operators)があります。 to (is はエイリアスであり、 defer! は入力ストリーム内の名前の代わりに xt を受け取ります)や +toaddraction-of (defer@ は入力ストリーム内の名前の代わりに xt を受け取ります)です。

Gforth を使用すると、 ワードの (to) アクションを変更できます。

(to) ( val operation xt –  ) gforth-1.0 “paren-to”

name という名前の、 ワードに似ている value の xt です。 nameval を保存します。 operation は、 to+toaddraction-of から選択します。

to-table: ( "name" "xt1" .. "xtn" –  ) gforth-experimental “to-table-colon”

TO+TOADDRACTION-OF のエントリを含むテーブルを作成します。 n/a を使用して、 サポートされていない操作をマークします。

to-method: ( xt table "name" –  ) gforth-experimental “to-method-colon”

to-method を作成します。 ここで、xt はフィールドにアクセスするためのアドレスを計算し、 table にはそれに格納する操作子(operators)が含まれます。

set-to ( to-xt –  ) gforth-1.0 “set-to”

現在のワードの (to) ( val xt -- ) メソッドの実装を to-xt に設定します。

n/a ( ) gforth-experimental “not-available”

このワードをチック(tick)することはできますが、 インタープリター時およびコンパイル時に “Operation not supported” (操作はサポートされていません)という例外が投げられます(throw)。 サポートされていないメソッドなどにこれを使用します。

(to)to 内で使用されるワードです。 実行時(run-time)に値を保存します。 (to) メソッドの一般的なスタック効果は ( val Operation xt -- ) です。 ここで、 xt は格納されているワードを示し、 operationto 風の操作の実際のバリエーションを示します。 val はそこに格納されている (適切な型の) 値です。

to-table: を使用して to メソッドを実装し、 タイプ固有の操作テーブルを作成し(テーブルの最後にある指定されていないスロットは n/a で埋められます)、 to-method: を、 value の xt からそのデータ・フィールドを取得する操作と組み合わせて使用​​します(通常、 ディクショナリー内の値の場合は >body ですが、 value-style データは構造体またはユーザー領域に存在することもできます)。

たとえば、 以下のように fvalue を実装できます:

to-table: f!-table f! f+!
' >body f!-table to-method: fvalue-to

: fvalue ( r "name" -- ; name: -- r )
  create f,
  ['] f@ set-does>
  ['] fvalue-to set-to ;

5e fvalue foo
: bar foo 1e f+ to foo ;
see bar

6.10.10.7 User-defined compile,

以下を使用して、 とあるワードのための compile, の実装を変更することもできます

set-optimizer ( xt –  ) gforth-1.0 “set-optimizer”

compile,xt execute するように現在のワードを変更します(compile, に渡されたものと同じスタック内容を使用)。 注意: compile,execute とスタック内容が一致している必要があるため、 set-optimizer は同じ振る舞いの、 より効率的な実装をインストールする場合にのみ使用しなければならないことに注意してください。

opt: ( compilation – colon-sys2 ; run-time – nest-sys  ) gforth-1.0 “opt:”

名前無しのコロン定義を開始します。 完了すると、 このコロン定義は(opt: の前の)最新のワードの compile, の実装になります。

注意: 結果として得られる compile, は、 依然として postpone literal postpone execute と同等である必要があることに注意してください。 そのため、 set-optimizer は、 振る舞いを変更するためではなく、 効率化のために役立てるものです。 しかし、 あなたが自分の足を撃つことを妨げるものは何もありません。 あなたが set-optimizer を使用したときの結果と、 最初に以下を定義して使用を無効にしたときに得られる結果を比較することで、 set-optimizer の使用が正しいかどうかを確認できます。

: set-optimizer drop ;

set-optimizer の使用例として、 上記の CONSTANT の定義の 1 つを以下のように拡張できます。

: CONSTANT ( n "name" -- ; name: -- n )
  create ,
  ['] @ set-does>
  [: >body @ postpone literal ;] set-optimizer
;

唯一の変更は、 set-optimizer 行の追加です。 あなたが定数を定義してコンパイルすると、 以下のようになります:

5 constant five
: foo five ;

foo 内のコンパイル済み five は、 five の一般的な呼び出しではなく、 literal 5 とコンパイルされるようになりました。 引用(quotation)には、 compile, と同じスタック効果があり、 それは ( xt -- ) です。 渡された xt は compile, されたワード、 つまりこの例では five に属します。 この例では、 まず xt が本体アドレスに変換され、 次にその場所の値 5 が取り出され、 その値が postpone literal でコンパイルされます(see Literals)。

この set-optimizer の使用は、 ユーザーが、 例えば 6 ' five >body ! などして定数の値を変更しないことを前提としています。 fivecreate で定義されていますが、 これは CONSTANT の実装の詳細であり、 それを文書化しない場合、 ユーザーはそれに依存してはなりません。 また、 本体(body)が変更されないことを前提とした方法で set-optimizer を使用する場合、 (ここで行われているように) create が使用されていることを文書化してはなりません。 逆に、 それを文書化する場合は、 本体の変更を処理できるように compile, 実装を記述する必要があります。

もう一つの例は、 前述の fvalue の例をさらに最適化したものです:

: compile-fvalue-to ( xt-value-to -- )
  drop ]] >body f! [[ ;
  
: fvalue-to ( r xt -- )
  >body f! ;
' compile-fvalue-to set-optimizer

: fvalue ( r "name" -- ; name: -- r )
  create f,
  ['] f@ set-does>
  [: >body ]] literal f@ [[ ;] set-optimizer
  ['] fvalue-to set-to ;

5e fvalue foo
: bar foo 1e f+ to foo ;
see bar

bar のコードを以前の定義のコードと比較します。 ここでは、 fvalue を読み取るコード(fvalueset-optimizer より)と、 fvalue を書き込むコード(fvalue-to に適用された set-optimizer より)の両方の最適化が見られます。 fvalue は(定数とは異なり)変化する可能性があるため、 (fvalue 内部の)読み取り部分が、 アドレスと、 実行時に実行される f@ をコンパイルします。

fvalue-to の場合、 compile, の実装は基本的に、 fvalue によってインラインで実行されるコードをコンパイルするだけです。 to のコンパイル機能(compilation semantics)は、アドレスをリテラルとしてコンパイルしてから、 (to) の実装(つまり、 fvalue-to)をコンパイルします。 この処理では、 >body が最適化されて削除されます。

実際には、 Gforth の fvalue は、 たとえば +TO をサポートするなど、 いくつかの追加の工夫が含まれています。

注意: set-optimizer の呼び出しは、 set-does> (または does> の呼び出しの後に実行する必要があることに注意してください。 なぜなら、 set-does>compile, の実装それ自体を上書きするからです。

fvalue-to の例でわかるように、 set-optimizerconstantfvalue のような定義ワード内ではなく、 個々のワードに適用することもできます。 この場合、 オプティマイザに渡されるワードの xt は通常は不要であり、 compile-fvalue-to のように drop されます。

エンジン gforth-itccompile,, を使用するので、 そこでは set-optimizer は効果ありません。


6.10.10.8 Creating from a prototype

上記では、 最初に create を使用してワードを定義し、 それから set-does>set-toset-optimizer などで変更する方法を示しました。

別の方法として、 以下のワード群を使用してプロトタイプを作成し、そのプロトタイプから新しいワードを作成します。 この種のコピーでは本体(body)部分は網羅されないため、 明示的に割り当てて初期化する必要があります。 上記の fvalue を例に取ると、 代わりに以下のように定義できます:

create fvalue-prototype ( -- r )
['] f@ set-does>
[: >body ]] literal f@ [[ ;] set-optimizer
['] fvalue-to set-to

: fvalue ( r "name" -- ; name: -- r )
  ``fvalue-prototype create-from f, reveal ;

このアプローチの利点は、まず create ヘッダー・メソッド達を複製し、 それらを変更し、 最終的に重複排除する必要がないため、 fvalue ワード群の作成が高速化されることだ。 ただし、 この利点は、 この定義ワードで作成されるワードの数が膨大な場合にのみ意味があります。

create-from ( nt "name" –  ) gforth-1.0 “create-from”

指定の nt のように振る舞うが、 ボディが空のワード name を作成します。 nt は名前付きワードの nt でなければなりません。 結果として得られるヘッダーはまだ非公開状態です(訳注: not yet reveal;ワードリスト未登録なので普通は呼び出せない)。 set- ワード群を使用せずに create-from でワードを作成すると、 set- ワード群または immediate または does> を使用してワードを作成するよりも高速になります。 nonamecreate-from を使用できます。

reveal ( ) gforth-0.2 “reveal”

(reveal;明らかにする、暴露する)ヘッダー定義時の現在のワード・リスト(wordlist current)に現在のワードを入れます。

noname を定義ワードとともに使用しても、 パフォーマンス上の利点は得られません。 したがって、 以下もいっしょに使います

noname-from ( xt –  ) gforth-1.0 “noname-from”

指定の xt のように動作するが、 本体が空の名前のないワードを作成します。 xt は、 名前のないワードの xt でなければなりません。

以下に使用例を示します:

``fvalue-prototype noname create-from
latestnt constant noname-fvalue-prototype

: noname-fvalue ( r -- xt ; xt execution: -- r )
  noname-fvalue-prototype noname-from f,
  latestxt ;

6.10.10.9 Making a word current

immediateset-optimizer など、 上記の多くのワードは、「現在の」(current)ワードまたは「一番最近定義された」(most recently defined)ワードを変更します。 しばしば以前のワードを変更したい場合があると思います。 その場合は以下のようにして行うことができます

make-latest ( nt –  ) gforth-1.0 “make-latest”

nt を最新の定義にし、 immediate および set-* 操作で操作できるようにします。 nt によって参照されるワードを既に使用している(特にコンパイル済みの)場合、 そのワードの振る舞いを変更しないでください(その実装だけを変更してください)。 さもないと、 Gforth エンジンやバージョン間で一貫性のない驚くべき振る舞いの組み合わせが発生する可能性があります。


6.10.10.10 Const-does>

create...does> は、 定義時から実行時に幾つかの値を転送(transfer)するためによく使用されます。 Gforth は、 このために以下を用意しています

const-does> ( run-time: w*uw r*ur uw ur "name" –  ) gforth-obsolete “const-does”

定義時: name を定義して返ります。

name 実行時: w*uw r*ur をプッシュし、 const-does> に続くコードを実行します。

このワードの一般的な使用例は以下のとおりです:

: curry+ ( n1 "name" -- )
1 0 CONST-DOES> ( n2 -- n1+n2 )
    + ;

3 curry+ 3+

ここで 1 0 は、 1 つのセルと 0 の浮動小数点が定義時から実行時に転送されることを意味します。

const-does> を使用する利点は以下のとおりです:

  • 値の保存と取得を扱う必要がなくなります。 つまり、 プログラムの書き込みと読み取りがより容易になります。
  • does> を使用する場合は、 最適化できない @ を導入する必要があります(なぜなら >body...! を使用してデータを変更できるため)。 const-does> はこの問題を回避します。

const-does> の標準 Forth 実装は compat/const-does.fs で利用できます。


6.10.11 Deferred Words

定義ワード defer (訳注: (期限を定めない)延期)を使用すると、 その振る舞いを定義せずに名前でワードを定義できます。 その振る舞いの定義は延期(defer)されます。 これが役立つ 2 つの状況を以下に示します:

  • ワードの振る舞いを後で変更できるようにし、 振る舞いが変更されたときにそのワードへの全てのプリコンパイルされた参照も変更できるようにしたい場合。
  • 相互再帰(mutual recursion)の場合。 See Calls and returns

以下の例では、 foo は常に greet の「Good breakfast」を出力するバージョンを呼び出し、 bar は常にgreet の「Hello」を出力するバージョンを呼び出します。 ソースコードを並べ替えて再コンパイルすることなく、 foo で新しいバージョンを使用できるようにする方法はありません。

: greet ." Good morning" ;
: foo ... greet ... ;
: greet ." Hello" ;
: bar ... greet ... ;

この問題は、 greetdefer された ワードとして定義することで解決できます。 defer された ワードの振る舞いは、 IS を使用して以前に定義されたワードの xt と関連付けることによって、 いつでも定義および再定義できます。 上記の例は以下のようになります:

Defer greet ( -- )
: foo ... greet ... ;
: bar ... greet ... ;
: greet1 ( -- ) ." Good morning" ;
: greet2 ( -- ) ." Hello" ;
' greet2 IS greet  \ make greet behave like greet2

プログラミング・スタイル・メモ: すべての defer された ワードに対してスタック・コメントを記述し、 かつ、 そのスタック効果に一致する XT のみを defer されたワードに入れる必要があります。 そうしないと、 defer されたワードを使用するのは非常に困難です。

defer された ワードを使用すると、 User-defined Defining Words の統計収集の例(statistics-gathering example)を改善できます。 アプリケーションのソース・コードを編集してすべての :my: に変更するのではなく、 以下のようにします:

: real: : ;     \ retain access to the original
defer :         \ redefine as a deferred word
' my: IS :      \ use special version of :
\
\ load application here
\
' real: IS :    \ go back to the original

注意すべき点の 1 つは、 IS には特別なコンパイル機能(compilation semantics)があり、 (TO のように)コンパイル時に名前をパースするということです:

: set-greet ( xt -- )
  IS greet ;

' greet1 set-greet

IS が適合しない状況では、 代わりに defer! を使用してください。

defer された ワードは、 xt から実行機能(execution semantics)のみを継承できます(xt が表現できるのはそれだけであるためです – これについての詳しい説明は see Tokens for Words 参照)。 デフォルトでは、この実行機能(execution semantics)から派生したデフォルトのインタープリター機能(interpretation semantics)とコンパイル機能(compilation semantics)を持ちます。 ただし、 defer された ワードのインタープリター機能とコンパイル機能は、 通常の方法で変更できます。

: bar .... ; immediate
Defer fred immediate
Defer jim

' bar IS jim  \ jim has default semantics
' bar IS fred \ fred is immediate
Defer ( "name" –  ) core-ext “Defer”

defer された ワード name を定義します。 その実行機能(execution semantics)は defer! または is で設定できます(最初に name を実行する前に設定する必要があります)。

defer! ( xt xt-deferred –  ) core-ext “defer-store”

xt (xt-deferred) で表される defer された ワードに実行のための xt を設定します。

IS ( xt "name" –  ) core-ext “IS”

defer された ワード name に実行のための xt を設定します。

defer@ ( xt-deferred – xt  ) core-ext “new-defer-fetch”

xt は、 defer された ワード xt-deferred に現在関連付けられているワードを表します。

action-of ( interpretation "name" – xt; compilation "name" – ; run-time – xt  ) core-ext “action-of”

Xt は、 現在 name に割り当てられている XT です。

Forth-94 では、 これら Forth-2012 のワードの定義は、compat/defer.fs で提供されます。 さらに、 Gforth は以下を提供します:

defers ( compilation "name" – ; run-time ... – ...  ) gforth-0.2 “defers”

defer された ワード name の現在の内容を現在の定義にコンパイルします。 つまり、 これにより、 name が defer されなかったかのように静的結び付け(static binding)が生成されます。

wrap-xt ( xt1 xt2 xt: xt3 – ...  ) gforth-1.0 “wrap-xt”

defer された ワード xt2 の現在の xt-current をどこかに退避して xt1 に設定した上で、 xt3 を実行し、 xt3 実行後、 退避した xt-current を defer されたワード xt2 に戻します(訳注: xt3 の内部のどこかで defer されたワード xt2 を使っているとして、 xt2 の実行機能を一時的に xt1 に設定してから xt3 を実行し、 実行後に xt2 の実行機能を元に戻しておく。 元に戻すのは try ... restore ... endtry で囲まれた部分なので xt3 実行中にエラーがあっても確実に復旧される。 詳しくはソースコード見て下さい。 なお、 xt: は xt3 を deferフレーバーに設定する)

preserve ( "name" –  ) gforth-1.0 “preserve”

指定の defer された ワード name で、 現在設定されている実行機能 xt を、 その場で isdefer! したかのようなコードに変換します(訳注: 上記例より、 ’ greet2 is greet : preserve-greet2 preserve greet ; → greet1 is greet ok → greet Good morning ok → preserve-greet2 ok → greet Hello ok ; see preserve-greet2 → : preserve-greet2 ‘greet2 ‘greet ! ; ok)


6.10.12 Forward

forward.fs 内の定義ワード forward を使用すると、 前方参照(forward references)を作成できます。 これは自動的に解決され、 Defer の間接化(indirection)のような追加コストは発生しません。 ただし、 これらの forward 定義はコロン定義に対してのみ機能します。

forward ( "name" –  ) gforth-1.0 “forward”

コロン定義への前方参照(forward reference)を定義します。 同一のワードリスト内(wordlist)で同一の名前のコロン定義を定義すると、 前方参照が解決されます。 .unresolved を使用して、 未解決の forward があるかどうかを確認します。

.unresolved ( ) gforth-1.0 “.unresolved”

未解決の前方参照(forward references)をすべて出力します


6.10.13 Aliases

定義ワード synonym を使用すると、 他のワードと同一の振る舞いをするワードを名前で定義できます。 これが役立つ 2 つの状況を以下に示します:

  • 別のワードリストからワードの定義にアクセスしたい場合(この例については、 Gforth ソースの root ワードリストの定義を参照してください)。
  • あなたが同義語(synonym)を作成したい場合。 2つの名前どちらでも認識できる定義(たとえば、 THENENDIF は同義語(synonyms)です)。
Synonym ( "name" "oldname" –  ) tools-ext “Synonym”

oldname と同一に振る舞うように name を定義します。 つまり、 同一のインタプリタ機能(interpretation semantics)と、 同一のコンパイル機能(compilation semantics)と、 同一の to/defer!defer@ 機能を持ちます。

Gforth は、 コンパイル機能、 または to/defer!defer@ 機能を親から継承しない、 非標準の alias も提供します。 あなたは後で、 例えば immediate などを使用してコンパイル機能などを変更できます。

Alias ( xt "name" –  ) gforth-0.2 “Alias”

namext を実行するワードとして定義します。 defer された ワードとは異なり、 エイリアス(aliase)にはコンパイル時に間接的なオーバーヘッドがありません。

Example:

: foo ... ; immediate

' foo Alias bar1           \ bar1 is not an immediate word
' foo Alias bar2 immediate \ bar2 is an immediate word
synonym bar3 foo           \ bar3 is an immediate word

synonyms(同義語)とaliases(別名)はどちらも元の nt とは異なる nt を持ちますが、 それをチック(tick)すると(または name>interpret を使用すると)、 元の xt と同じ xt が生成されます(see Tokens for Words)。


6.11 Interpretation and Compilation Semantics

(名前付き)ワード(word)のインタープリター機能(interpretation semantics)は、 テキスト・インタープリターがインタープリター状態でワードに遭遇したときに行うことです。 これは他の文脈でも表れます。 たとえば、 ' word によって返される実行トークンは、 word のインタープリター機能(interpretation semantics)を識別します(つまり、 ' word execute は、 word のインタープリター状態でのテキスト通訳(interpretation)と同等です)。

(名前付き)ワード(word)のコンパイル機能(compilation semantics)は、 テキスト・インタープリターがコンパイル状態でワードに遭遇したときに行うことです。 これは他の文脈でも表れます。 たとえば、 POSTPONE word は、 word のコンパイル機能(compilation semantics)コンパイルします18

ほとんどのワードにはデフォルトのコンパイル機能(compilation semantics)があります。 つまり実行機能(execution semantics)(スタック効果 ( -- ))をコンパイルします。 ただし、多くのワードが他のコンパイル機能(compilation semantics)を持っていて、 その個々のワードについては文書化されています(スタック効果を含む)。

標準では、 実行機能(execution semantics)についても述べています。 標準では、 両方が定義されている場合はインタープリター機能(interpretation semantics)と異なることはありませんが、 一方が定義されていない、 または、 両方とも定義されていない場合もあります。 Gforth ではインタープリター機能(interpretation semantics)と実行機能(execution semantics)には違いがないため、 これらの用語は同じ意味で使用されます。

Gforth (1.0 以降)では、 すべてのワードに インタープリター機能/実行機能 が定義されています。 標準でインタープリター機能も実行機能も定義されていない多くのワード(if など)については、 Gforth の インタープリター機能/実行機能 がコンパイル機能を実行します。

標準では、 実行機能はデフォルトでインタープリター機能とコンパイル機能を定義するために使用されます。 デフォルトでは、ワードのインタープリター機能はその実行機能を execute し、 ワードのコンパイル機能はその実行機能を compile, します19

名前無しワード(see Anonymous Definitions)は、 テキスト・インタープリターまたはチック(tick)または postpone では検出できません。 このようなワードはその xt (see Tokens for Words) によって表され、 この xt が execute されたときは、 その実行機能が呼び出されます。

あなたは、 最後に定義されたワードの機能(semantics)を変更できます:

immediate ( ) core “immediate”

ワードのコンパイル機能を、 その実行機能を実行するように設定します。

compile-only ( ) gforth-0.2 “compile-only”

最後の定義をコンパイル専用としてマークします。 その結果、 テキスト・インタプリタと ' は、 そのようなワードに遭遇すると警告を発します。

restrict ( ) gforth-0.2 “restrict”

compile-only の同義語(synonym)

慣習により、 デフォルト以外のコンパイル機能を持つワード(即実行ワードなど)は、多くの場合、 名前が括弧(brackets;角括弧)で囲まれています(例: ['] see Execution token)。

注意: compile-only のワードにチック(tick)(')を付けると、警告(“<word> is compile-only”)が表示されることに注意してください。


6.11.1 Combined Words

Gforth を使用すると、 複合ワード(combined words)、 つまりインタープリター機能(interpretation semantics)とコンパイル機能(compilation semantics)を任意に組み合わせたワードを定義できます。

interpret/compile: ( interp-xt comp-xt "name" –  ) gforth-0.2 “interpret/compile:”

この機能は、 TOS" を実装するために導入されました。 このようなワードは、 たとえ小さなかわいいものであっても定義しないことをお勧めします。 なぜなら、 ワードによっては、 いくつかの文脈でワードの両方の部分を理解するのが困難になるからです。 たとえば、 あなたがコンパイル部分の実行トークンを取得したいとすると、 代わりに、 2つのワードを定義します。 1つはインタープリター機能部分を具体化するもで、 もう一つはコンパイル機能部分を具体化するものです。 それら完成したら、 あなたのユーザーの便宜のために interpret/compile: を使用して、 それらを組み合わせた複合ワード(combined word)を定義できます。

この機能を使用して、 ワードのデフォルトのコンパイル機能(compilation semantics)の最適化実装を提供してみるとしましょう。 たとえば、 以下のように定義します:

:noname
   foo bar ;
:noname
   POSTPONE foo POSTPONE bar ;
interpret/compile: opti-foobar

上記は以下の最適化バージョンです:

: foobar
    foo bar ;

残念ながら、 これは [compile] では正しく動きません。 なぜなら、 [compile] では、 すべての interpret/compile: ワードのコンパイル機能がデフォルトでは無いと想定しているためです。 つまり、 [compile] opti-foobar はコンパイル機能をコンパイルしますが、 [compile] foobar はインタープリター機能をコンパイルします。

state-smart ワードを使用して、interpret/compile: によって提供される機能をエミュレートしようとする人もいます(実行途中に STATE をチェックする場合、 ワードは state-smart になります)。 たとえば、foobar を以下のようにコーディングしようとします:

: foobar
  STATE @
  IF ( compilation state )
    POSTPONE foo POSTPONE bar
  ELSE
    foo bar
  ENDIF ; immediate

これは foobar がテキスト・インタープリターによってのみ処理される場合には機能しますが、 他のコンテキスト('POSTPONE など)では機能しません。 たとえば、 ' foobar は、 元の foobar のインタープリター機能ではなく、 state-smart ワードの実行トークンを生成します。 この実行トークンを(EXECUTE で直接、 または COMPILE, で間接的に)コンパイル状態において実行(execute)すると、 結果は期待したものになりません(つまり、 foo bar は実行されません)。 state-smart ワードは良くないアイディアです。 対策は、 こんなの書かない事!です20


6.12 Tokens for Words

このセクションでは、 ワードを表すトークンの作成とその使用について説明します。


6.12.1 Execution token

実行トークン(execution token)(xt)はワードの振る舞いを表します。 execute を使用して xt で表される振る舞いを呼び出すことができ、 そして、 compile, (see Macros) を使用してそれを現在の定義にコンパイルできます。 その他の使用法としては、 defer された ワード(see Deferred Words) があります。

特に、 ワードのインタープリター機能(interpretation semantics)(別名 実行機能(execution semantics))を表す「実行トークン」(execution token)があります21

名前付きワード x の場合、 `x(訳注:アポストロフィではなくてバックスラッシュ)を使用してその実行トークンを取得できます:

5 `. ( n xt )
execute ( )      \ execute the xt (i.e., ".")
: foo `. execute ;
5 foo

しかしながら、 ` プレフィックスは Gforth 拡張であるため、 以下の標準 Forth のワードを使用する方が好ましいです:

' ( "name" – xt  ) core “tick”

xtname のインタープリター機能(interpretation semantics)を表します。 ワードにインタープリター機能が無い場合、 -14 throw を実行します。

['] ( compilation. "name" – ; run-time. – xt  ) core “bracket-tick”

xtname のインタープリター機能(interpretation semantics)を表します。 ワードにインタープリター機能が無い場合、 -14 throw を実行します。

これらはパース・ワード(parsing words)であるため(なお、 `x は認識器(recognizer)によってリテラルとして扱われます)、 通訳(interpret)されたりコンパイルされたコードの振る舞いが直感的ではない場合があります:

5 ' .   ( n xt ) 
execute ( -- )      \ execute the xt of .
\ 以下は意図したとおりに動きません:
\ : foo ' . ;
\ 5 foo execute
\ その代わりに以下のようにします:
: foo ['] . ;
5 foo execute    \ execute the xt of .
\ コロン定義内で ' を使った場合:
: bar ' execute ;
5 bar .          \ execute the xt of .
\ 訳注: 一見普段と変わらないように見えるが、
\       テキスト・インタープリターが . を実行するのではなく、
\       bar が その後ろのワードの xt を execute している。
\       よって bar の後ろを省略すると下記のようにエラーとなる
5 bar 
*the terminal*:10:6: error: Attempt to use zero-length string as a name
5 bar>>><<<

' は実行時にパースするため、 bar のようにコロン定義に置くと、 コロン定義内の次のワードを消費せずに実行時に次のワードが消費されます。 `x と書かずにリテラル xt をコロン定義に入れたい場合は、 ['] x と書きます。

Gforth の `x'['] は、 compile-only ワードで使用すると警告(warn)します。 そのような使用法が、 異なる Forth システム間で移植できない可能性があるためです。

ワードの immediate のバリエーションを定義することで、 この警告(warning)と移植性の問題を回避できます。 例:

: if postpone if ; immediate
: test [ ' if execute ] ." test" then ;

結果として得られる実行トークンが execute されたときは、 if のコンパイル機能(compilation semantics)を実行します。

xt を取得するもう一つ方法は、 :noname または latestxt です(see Anonymous Definitions)。 匿名のワードの場合、 :noname または latestxt により、 そのワードが持つ唯一の振る舞い(実行機能(execution semantics)) の xt が得られますが、 名前付きワードの定義の後ろで latestxt を使用して、 その xt を取得することもできます。

:noname ." hello" ;
execute

xt は 1 つのセルを占有し、 他のセルと同様に操作できます。

標準 Forth では、 xt は単なる抽象データ型です(つまり、 xt を生成または消費する操作によって定義されます)。 (Gforth 1.0 以降の)具体的な実装は、 ワードの本体アドレス(昔は PFA だった)です。 Gforth 0.7 以前では、 xt はコード・フィールド・アドレス(CFA は PFA の 2 セル前)として実装されていました。

execute ( xt – ) core “execute”

実行トークン xt によって表される機能(semantics)を実行します。

execute-exit ( compilation – ; run-time xt nest-sys –  ) gforth-1.0 “execute-exit”

xt を実行し、 末尾呼び出し最適化法(tail-call-optimized way)で現在の定義から戻ります。 戻りアドレス nest-sys とローカル変数(local)は、 xt を execute する前に割り当て解除(deallocate)されます。

perform ( a-addr – ) gforth-0.2 “perform”

@ execute.

Noop は、 しばしば実行トークンのプレースホルダーとして使用されます:

noop ( ) gforth-0.2 “noop”

6.12.2 Name token

Gforth は、名前付きワードを「名前トークン」name token(nt)で表します。 名前トークンは、 引数または、 以下のワードの結果として発生するセル・サイズの抽象データ型です。

Gforth 1.0 以降、 ほとんどのワードの nt の具体的な実装は、 xt と同一アドレスです(これは xt のための基本 nt (primary nt)です)。 ただし、 synonym と、 aliase と、 interpret/compile: で定義されたワードは、 別のワードから xt を取得しますが、 (xt とは異なり)依然として自分独自の nt を持ちます。 したがって、 Gforth 1.0 固有のコードを作成する準備ができている場合でも、 xt と nt を同一の意味で使用することはできません。 >name を使用してこれら自分独自の nt を持つワードの xt から代替 nt を取得することはできません。

``x (Gforth 1.0 以降) または、 以下を使用して、 ワード x の nt を取得します

find-name ( c-addr u – nt | 0  ) gforth-0.2 “find-name”

現在の検索順序スタック(the search order)で名前 c-addr u を見つけます。 nt が見つかった場合はそれを返し、 それ以外の場合は 0 を返します。

find-name-in ( c-addr u wid – nt | 0  ) gforth-1.0 “find-name-in”

wid で識別されるワードリストで、 c-addr u の文字列で指定された定義を検索します。 nt が見つかった場合はそれを返し、 それ以外の場合は 0 を返します。

latest ( – nt  ) gforth-0.6 “latest”

nt は、 最後に定義されたワードの名前トークンです。 最後のワードに名前がない場合は 0 です。

latestnt ( – nt  ) gforth-1.0 “latestnt”

nt は、 最後に定義されたワードの名前トークンです(訳注: 名前が無い場合でも nt を返す。 その nt を name>string すると、 長さ 0 の文字列(addr u) を返す)

>name ( xt – nt|0  ) gforth-0.2 “to-name”

xt で表されるワードの基本名前トークン(primary name token) nt を取得します。 xt が ぢつは xt でない場合(非 xt を xt と誤認する可能性が低いヒューリスティック・チェックを使用)、 または基本 nt (primary nt)が名前無しワードである場合は 0 を返します。 Gforth 1.0 の時点では、 すべての xt に基本 nt がありますが、 他の名前付きワードも同一のインタープリター機能(interpretation sematics) xt を持つ可能性があります。

xt>name ( xt – nt  ) gforth-1.0 “xt-to-name”

xt の基本 nt (primary nt)を生成します。 xt が、 ぢつは xt でない場合、 nt は nt であるとは保証されません。

以下を使用すると、 ワードリスト内のすべての nt を取得できます

traverse-wordlist ( ... xt wid – ...  ) tools-ext “traverse-wordlist”

f が false になるかワードリストがなくなるまで、 ワードリスト wid 内のワード nt ごとに xt ( ... nt – f ... ) を 1 回実行します。 xt はスタックその下側を自由に使用できます(訳注: つまり TOS に f さえ返せばスタックに何が積まれても(積まれなくても)気にしない)

nt を使用すると、 ワードのインタープリター機能(interpretation semantics)とコンパイル機能(compilation semantics)や、 その名前や、 ワードリスト内の次のワードのワード、 にアクセスできます:

name>interpret ( nt – xt  ) tools-ext “name-to-interpret”

xt はワード nt のインタープリター機能(interpretation semantics)を表します。

name>compile ( nt – w xt  ) tools-ext “name-to-compile”

w xt はワード nt のコンパイル・トークン(compilation token)です(訳注: コンパイル・トークン CT は 2セル構成)。

name>string ( nt – addr u  ) tools-ext “name-to-string”

addr count は、 nt で表されるワードの名前です。

id. ( nt –  ) gforth-0.6 “i-d-dot”

nt で表されるワードの名前を出力します。

.id ( nt –  ) gforth-0.6 “dot-i-d”

id. の F83 での名前です。

name>link ( nt1 – nt2 / 0  ) gforth-1.0 “name-to-link”

ワード nt1 の場合、 同じワードリスト内の1つ前のワード nt2 を返します。 前のワードがない場合は 0 を返します。

名前なしワードは通常、 インタープリター機能(interpretation compilation)も無く、 コンパイル機能(compilation semantics)も無く、 名前も無く、 単語リストにもありません。 しかし、Gforth (1.0 以降) ではすべてのワードは等しいため、 名前無しのワードにも nt があります(ただし、 ワードリストには含まれません)。 その nt は latestnt で取得でき、 nt を消費する上記のワード群はこれらの nt に対して適切な処理を行います。

使用例として、 以下のコードは、デフォルト以外のコンパイル機能(compilation semantics)を持つ forth-wordlist 内のすべてのワードをリストします:

: ndcs-words ( wid -- )
  [: dup name>compile ['] compile, <> if over id. then 2drop true ;]
  swap traverse-wordlist ;

forth-wordlist ndcs-words

このコードは、 コンパイル・トークンの xt 部分が compile, の xt である場合、 ワードがデフォルトのコンパイル機能(compilation semantics)を持っていると仮定しています。

いにしえの Forth システムの nt に最も近いのはネーム・フィールド・アドレス(NFA)ですが、 Gforth とは顕著な違いがあります。 いにしえの Forth システムでは、 各ワードに一意の NFAとLFAとCFAとPFA(または、LFAとNFAとCFAとPFAの順)があり、 あるワードから次のワードへと進むためのワードがありました。 これとは対照的に、 Gforth では、 いくつかの nt が name>interpret xt からは同一の xt を取得できます。 名前トークンによって識別される構造体にリンク・フィールドがありますが、 通常、 検索にはこれらの構造体の外部にあるハッシュ・テーブルが使用されます。 Gforth の名前はセル幅のカウントおよびフラグ・フィールド(cell-wide count-and-flags field)がありますが、 nt はそのカウント・フィールドへのアドレスとしては実装されていません。


6.12.3 Compilation token

名前付きワードのコンパイル機能(compilation semantics)は、 2 つのセル w xt で構成されるコンパイル・トークン(compilation token)によって表されます。 一番上のセル xt は実行トークンです。 コンパイル・トークンによって表されるコンパイル機能は、 execute で実行できます。 これは、 コンパイル・トークン全体を消費し、 コンパイル・トークンが表すコンパイル機能(compilation semantics)によって決定される追加のスタック効果を伴います。

現時点では、 コンパイル・トークンの w 部分は実行トークンで、 xt 部分は execute または compile, のいずれかを表します22。 ただし、 必要な場合を除き、 この知識に頼らないでください。 Gforth の将来のバージョンでは、 普通でない(unusual)なコンパイル・トークン(リテラルのコンパイル機能を表すコンパイル・トークンなど)が導入される可能性があります。

たとえば、 if のコンパイル・ークンは、 name>compile を使用した標準的な方法(例: `if name>compile)で取得できますが、 コンパイル・トークンを取得するためのパース・ワードもあります:

[COMP'] ( compilation "name" – ; run-time – w xt  ) gforth-0.2 “bracket-comp-tick”

コンパイル・トークン w xt は、 name のコンパイル機能(compilation semantics)を表します。

COMP' ( "name" – w xt  ) gforth-0.2 “comp-tick”

コンパイル・トークン w xt は、 name のコンパイル機能(compilation semantics)を表します。

execute を使用して、 コンパイル・トークンによって表されるコンパイル機能(compilation semantics)を実行できます。 postpone, を使用してコンパイル機能(compilation semantics)をコンパイルできます。 つまり、 ``x name>compile postpone,postpone x と同等です。

postpone, ( w xt –  ) gforth-0.2 “postpone-comma”

コンパイル・トークン w xt で表されるコンパイル機能(compilation semantics)をコンパイルします。


6.13 Compiling words

他のほとんどの言語とは異なり、 Forth にはコンパイルと実行時(run-time)の間に厳密な境界がありません。 たとえば、 ワードの定義中(または constant のようなワードの定義によって使用されるデータのための計算)に任意のコードを実行できます。 さらに、 immediate (see Interpretation and Compilation Semantics) や [...] (下記参照)を使用すると、 コロン定義のコンパイル中に任意のコードを実行できます(例外: ディクショナリー空間で割り当てしてはいけません)。


6.13.1 Literals

最も単純で最も頻繁に使用される例は、 コンパイル中にリテラルを計算することです。 たとえば、 以下の定義は、 文字列の配列を 1 行に 1 つの文字列で出力します:

: .strings ( addr u -- ) \ gforth
    2* cells bounds U+DO
        cr i 2@ type
    2 cells +LOOP ;  

Gforth のような愚かで低能なコンパイラーを使用すると、 ループ反復ごとに 2 cells が計算されます。 あなたは、 この値をコンパイル時に一度だけ計算し、 定義にコンパイルできます。 以下のようにします:

: .strings ( addr u -- ) \ gforth
    2* cells bounds U+DO
        cr i 2@ type
    [ 2 cells ] literal +LOOP ;  

[ は、 テキスト・インタープリターをインタープリター状態に切り替えます(この例を対話的に入力し、 [] の間に改行を挿入すると、 ok プロンプトが表示されます)。 2 cells のインタープリター機能(interpretation semantics)を実行します。 ここでは数値を計算します。 ] は、 テキスト・インタープリターをコンパイル状態に戻します。 次に、 この数値を現在のワードにコンパイルする Literal のコンパイル機能(compilation semantics)を実行します。 see .strings を使用してワードを逆コンパイルすると、 コンパイルされたコードへの影響を確認できます。

この方法で 2* cells[ 2 cells ] literal * に最適化することもできます。

[ ( ) core “left-bracket”

インタープリター状態にします。 即実行ワードです。

] ( ) core “right-bracket”

コンパイル状態にします。

Literal ( compilation n – ; run-time – n  ) core “Literal”

コンパイル機能(compilation semantics): 実行機能(run-time semantics)をコンパイルします。 実行機能(run-time semantics): n をスタックにプッシュします。 インタープリター機能(interpretation semantics): 未定義です。

ALiteral ( compilation addr – ; run-time – addr  ) gforth-0.2 “ALiteral”

literal と同様に機能しますが、 (クロス・コンパイルされたコードで使用される場合、)リテラルがアドレスであることをクロス・コンパイラーに伝えます。

]L ( compilation: n – ; run-time: – n  ) gforth-0.5 “]L”

] literal と同等。

単一セル以外のデータ型をリテラルとしてコンパイルするためのワードもあります:

2Literal ( compilation w1 w2 – ; run-time  – w1 w2  ) double “two-literal”

実行時に w1 w2 がスタックに配置されるように、 適切なコードをコンパイルします。 インタープリター機能(interpretation semantics)は未定義です。

FLiteral ( compilation r – ; run-time – r  ) floating “f-literal”

実行時に r が(浮動小数点)スタックに配置されるように、 適切なコードをコンパイルします。 インタープリター機能(interpretation semantics)は未定義です。

SLiteral ( Compilation c-addr1 u ; run-time – c-addr2 u  ) string “SLiteral”

コンパイル時: c-addr1, u で指定された文字列を現在の定義にコンパイルします。 実行時: 文字列のアドレスと長さを記述する c-addr2 u を返します。

データ・スタック上のコロン定義の外側から内側にデータを渡したいと思うかもしれません。 : がコロン colon-sys をプッシュし、 colon-sys より下のモノのにアクセスできなくなるため、 これは機能しません。 たとえば、 以下は機能しません:

5 : foo literal ; \ error: "unstructured"

代わりに、 変数など、 他の方法で値を渡す必要があります:

variable temp
5 temp !
: foo [ temp @ ] literal ;

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
;

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

  • (xt: を指定する) defer フレーバーのローカル変数 xt を使用しています。 ]]...[[ 内でローカル変数 xt に遭遇すると、 ローカル変数 xt を compile, します。
  • ]]...[[ 内でリテラル 1 を使用します。 これにより、 1 が postpone (延期)されます。 つまり、 compile-map-array の実行時にコンパイルされます。
  • compile-map-array が実行されると、 1 cells がコンパイルされ、 Gforth の定数折りたたみによって #8 に最適化(optimize)されます。

注意: 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言語などのマクロとの重要な違いは以下のとおりです:

  • マクロの定義と処理には、 別個の前処理用の言語や処理器ではなく、 同一の言語を使用します。
  • したがって、 Forth の全機能をマクロ定義で利用できます。 たとえば、 任意の複雑な計算を実行したり、 条件付きまたはループで異なるコードを生成したりできます (例: see Advanced macros)。 この能力は、 パーサ・ジェネレータや、 その他のコード生成ソフトウェアを作成するときに非常に役立ちます。
  • postpone などを使用して定義されたマクロは、 文字列よりも高いレベルで言語を扱います。 名前との結び付け(name binding)はマクロ定義時に行われるため、 C言語のマクロで発生する可能性のある名前の衝突の落とし穴を回避できます。 もちろん、 Forth は自由主義の言語(liberal language)であり、 以下のようなテキスト解釈マクロ(text-interpreted macros)を使用して自分自身を攻撃することもできます
    : [compile-+] s" +" evaluate ; immediate
    

    マクロ使用時に名前を結びつける(bind)だけでなく、 evaluate を使用すると、 あなたの定義は state-smart (see state-smartness) になります。

マクロで数値をワードにコンパイルすることが必要な場合があります。 これを行うためのワードは 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] は冗長です)。


6.14 The Text Interpreter

テキスト・インタープリター(text interpreter)は、 現在の入力デバイスからの入力を処理する無限ループです。 インタープリターの実装上でコンパイルされた Forth コードを実行する内部インタープリター(inner interpreter)(see Engine)とは対照的に、 これは外部インタープリター(outer interpreter)とも呼ばれます24

テキスト・インタープリターは、 インタープリター状態(interpret state) と コンパイル状態(compile state)の 2 つの状態のいずれかで動作します。 現在の状態は、 それにふさわしい名前の変数である state によって定義されます。

このセクションでは、 テキスト・インタープリターがインタープリター状態にあるときに、 ユーザー入力デバイス(キーボード)からの入力処理についてどのように振る舞うかについて説明することから始めます。 これは Forth システムが動き始めた時のモードです。

テキスト・インタープリターは、 入力バッファー(input buffer)25と呼ばれるメモリー領域で動作します。 この領域には、 RET キーを押したときにキーボードからの入力が保存され、 入力バッファーの先頭から開始して、 先頭の空白文字達(デリミタ(delimiters)と呼ばれる)をスキップし、 空白文字、 またはバッファーの終わりに達するまで文字列(空白以外の文字のシーケンス)をパースします。 文字列をパースした後、 以下の2つの試行を行います:

  • 定義のディクショナリー(dictionary)内でその文字列を検索します。 その文字列が見つかった場合、 その文字列は、 とある定義(definition)(ワード(word)とも呼ばれる)を指し示していて、 そして、 ディクショナリー検索機能は、 テキスト・インタープリターがそのワードのインタープリター機能(interpretation semantics)を実行できるようにするための情報を返します。 ほとんどの場合、 これは、 単にそのワードが execute されることを意味します。
  • 文字列がディクショナリーに見つからない場合、 テキスト・インタープリターは Number Conversion で説明されているルールを使用して、 文字列を数値として処理しようと試みます。 文字列が現在の基数で有効な数値を表す場合、 その数値はパラメーター・スタック(整数の場合はデータ・スタック、 浮動小数点数の場合は浮動小数点スタック)にプッシュされます。

上記両方の試行が失敗した場合、 テキスト・インタープリターは入力バッファーの残りを破棄し、 そして、 エラー・メッセージを発行して、そして、 さらなる入力を待ちます。 いずれかの試行が成功すると、 テキスト・インタープリターは入力バッファー全体が処理されるまでパース処理を繰り返し、 全体が処理された時点でステータス・メッセージ “ ok” を出力し、 そして、 さらなる入力を待ちます。

テキスト・インタープリターは、 >IN (“to-in”(トゥーイン)と発音します)と呼ばれる変数を更新することにより、 入力バッファー内の位置を追跡します。 >IN の値は 0 で始まり、 入力バッファーの先頭からのオフセットが 0 であることを示します。 オフセット >IN @ から入力バッファーの末尾までの領域は、 パース領域(parse area)と呼ばれます26。 以下の例は、 テキスト・インタープリターが入力バッファーをパースする際に >IN がどのように変化するかを示しています:

: remaining source >in @ /string
  cr ." ->" type ." <-" ; immediate 

1 2 3 remaining + remaining . 

: foo 1 2 3 remaining swap remaining ;

これらの結果はそれぞれ以下のようになります:

->+ remaining .<-
->.<-5  ok

->swap remaining ;-<
->;<-  ok

>IN の値は、 テキスト・インタープリターによって実行される入力バッファー内のワードによって変更することもできます。 これは、 ワードがテキスト・インタプリタをだまして、 入力バッファーの一節をスキップしたり27、 一節を 2 回パースしたりすることができることを意味します。 例:

: lat ." <<foo>>" ;
: flat ." <<bar>>" >IN DUP @ 3 - SWAP ! ;

flat が実行されると、 以下の出力が生成されます28(訳注:ユーザーが flat と打ち込と、 入力バッファーには flat があり、 ワード flat の実行が初まったときは >IN は flatの t の次を指している。 そこから 3 戻ると >IN が 指すのは flat の l で、 そこからテキスト・インタープリターがパースを再開すると、 テキスト・インタープリターに見えるのは lat である。 よって lat が実行されて <<foo>> が出力される):

<<bar>><<foo>>

この手法を使用すると、 ワードのパースにおける相互運用性の問題(interoperability problems)の一部を回避できます。 もちろん、 ワードのパースは可能な限り避けた方が良いです。

テキスト・インタープリターの振る舞いに関する 2 つの重要な注意事項:

  • 入力バッファーからの追加の文字達をパースする前に、 各入力文字列の処理を完了させます。
  • 入力バッファーを読み取り専用領域(region)として扱います(そして、 コードも同様に読み取り専用でなければなりません)。

テキスト・インタープリターがコンパイル状態にある場合、 その振る舞いは以下のように変化します:

  • パースされた文字列がディクショナリー内で見つかった場合、 テキスト・インタープリターはそのワードのコンパイル機能(compilation semantics)を実行します。 ほとんどの場合、これは単にそのワードの実行機能(execution semantics)が現在の定義に追加されることを意味します。
  • 数値が検出されると、 パラメーター・スタックにプッシュされるのではなく、 現在の定義に(リテラルとして)コンパイルされます。
  • エラーが発生した場合、 state が変更され、 テキスト・インタープリターはインタプリタ状態に戻ります。
  • キーボードから行が入力されるたびに、 Gforth は “ ok” ではなく “ compiled” を出力します。

テキスト・インタープリターがキーボード以外の入力デバイスを使用している場合、 その振る舞いは以下のように変化します:

  • パース領域が空の場合、 テキスト・インタープリターは入力ソースから入力バッファーを再充填(refill)しようと試みます。 入力ソースを使い尽くすと、 入力ソースは以前の入力ソースに戻ります。
  • パース領域が空になるたびに “ ok” や “ compiled” メッセージは出力されません。
  • エラーが発生した場合、 入力ソースはユーザー入力デバイスに戻されます。

これについて詳しくは、 Input Sources をご覧ください。

>in ( – addr  ) core “to-in”

uvar 変数 – a-addr は、 入力バッファーの先頭からパース領域の先頭までの char オフセットを格納するセルへのアドレスです。

source ( – addr u  ) core “source”

現在の入力バッファーのアドレス addr と長さ u を返します

tib ( – addr  ) core-ext-obsolescent “t-i-b”
#tib ( – addr  ) core-ext-obsolescent “number-t-i-b”

uvar 変数 – a-addr は、 端末の入力バッファー内の文字数を含むセルのアドレスです。 時代遅れ(OBSOLESCENT): source はこのワードの機能を置き換えます。

interpret ( ... – ...  ) gforth-0.2 “interpret”

6.14.1 Input Sources

デフォルトでは、 テキスト・インタープリターは、 Forth の起動時にユーザー入力デバイス(キーボード)からの入力を処理します。 テキスト・インタープリターは、 以下のいずれかの入力ソースからの入力を処理できます:

  • ユーザー入力デバイス – つまり、キーボード。
  • ファイル。 Forth source files で説明されているワードを使用。
  • ブロック。 Blocks で説明されているワードを使用。
  • テキスト文字列。 evaluate 使用。

プログラムは、 source-idblk の値から現在の入力デバイスを識別できます。

source-id ( – 0 | -1 | fileid  ) core-ext,file “source-i-d”

戻り値: 0 (入力ソースはユーザー入力デバイス)または、 -1 (入力ソースは evaluate によって処理されている文字列)または、 fileid (入力ソースは fileid で指定されたファイル)です。

blk ( – addr  ) block “b-l-k”

uvar 変数 – このセルには現在のブロック番号(現在の入力ソースがブロックでない場合は 0)が格納されています。

save-input ( – x1 .. xn n  ) core-ext “save-input”

n 個のエントリ xn 〜 x1 は、 restore-input で使用できるプラットフォーム依存の方法で、 入力ソース仕様(input source specification)の現在の状態を記述します。

restore-input ( x1 .. xn n – flag  ) core-ext “restore-input”

入力ソース仕様(input source specification)を n 個のエントリ xn 〜 x1 で記述された状態に復元しようとします。 復元が失敗した場合は flag が true になります。 新しい入力プログラムを使用した Gforth では、 再度 throw するために使用できるフラグがある場合にのみ失敗します。 異なるアクティブな入力ストリーム間で保存および復元することも可能です。 注意: 入力ストリームを閉じる場合は、 開いたときとは逆の順序で行う必要がありますが、 その間はすべて許可されます。

evaluate ( ... addr u – ...  ) core,block “evaluate”

現在の入力ソース仕様(input source specification)を保存します。 そして、 -1source-id に保存し、 かつ、 0blk に保存します。 それから、 >IN0 に設定し、 文字列 c-addr u を入力ソースおよび入力バッファーにし、 通訳(interpret)します。 そしてパース領域が空になったら、 入力ソース仕様を復元します。

query ( ) core-ext-obsolescent “query”

ユーザー入力デバイスを入力ソースにします。 入力を端末入力バッファー(Terminal Input Buffer)で受け取ります。 >IN をゼロに設定します。 時代遅れ(OBSOLESCENT): accept で置き換えられました。


6.14.2 Number Conversion

テキスト・インタープリターが数値入力をどのように変換するかの概要は Literals in source code にあります。 このセクションでは、 関連するいくつかのワードについて説明します。

デフォルトでは、 整数変換に使用される基数は変数 base の内容によって与えられます。 注意: 予期しない base の値によって多くの混乱が生じる可能性があることに注意してください。 base を変更する場合、 必ず古い値を保存し、 後で復元してください。 さらに良いのは、これを自動的に実行する base-execute を使用することです。 一般に、 base を 10 進数のままにし、 一般的な 10 進数以外の基数については Literals in source code で説明されている接頭辞を使用することをお勧めします。

base-execute ( i*x xt u – j*x  ) gforth-0.7 “base-execute”

BASE の内容を u にして xt を実行し、 その後元の BASE を復元します。

base ( – a-addr  ) core “base”

User 変数 – a-addr は、 入出力時の数値変換にデフォルトで使用される基数を格納するセルへのアドレスです。 base に保存せず、 代わりに base-execute を使用してください。

hex ( ) core-ext “hex”

base を &16 (16 進数) に設定します。 hex を使用せず、 代わりに base-execute を使用してください。

decimal ( ) core “decimal”

base を &10 (10 進数) に設定します。 10 進数 を使用せず、 代わりに base-execute を使用してください。

dpl ( – a-addr  ) gforth-0.2 “Decimal-PLace”

User 変数 – a-addr は、 最新の数値変換における小数点の位置を格納するセルのアドレスです。 -1 に初期化されます。 小数点を含まない数値を変換すると、 dpl は -1 になります。 2. の変換後は 0 が保持されます。 234123.9 の変換後は 1 が保持されます。

数値変換は、 不注意な人にとっては多くの罠があります:

  • コード・シーケンス base @ . を使用して現在の基数を測定することはできません。 これは現在の基数では常に 10 と表示されます。 代わりに、 base @ dec. なととしてください。
  • bin というワードがありますが、 これは基数を設定するものではありません(see General files)。
  • 標準 Forth では、 2倍長整数の . が文字列の最後の文字である必要があります。 Gforth では . をどこにでも置くことができます。
  • 数値変換プロセスではオーバーフローはチェックされません。

Line input and conversion で説明されているワードを使用して、 プログラムに数値を読み込むことができます。


6.14.3 Interpret/Compile states

標準のプログラムでは state を明示的に変更することは許可されていません。 ただし、[] というワードを使用して、 state を暗黙的に変更できます。 [ が実行されると、 state がインタープリター状態に切り替わり、 テキスト・インタープリターが通訳(interpret)を開始します。 ] が実行されると、 state がコンパイル状態に切り替わり、 テキスト・インタープリターはコンパイルを開始します。 これらのワードの最も一般的な使用法は、コロン定義内でインタープリター状態に切り替えたり、 元の状態に戻したりすることです。 この手法は、 リテラルのコンパイル(see Literals の例参照)または条件付きコンパイル(例: see Interpreter Directives の例を参照)に使用できます。


6.14.4 Interpreter Directives

以下のワード群は通常、 インタープリター状態で使用されます。 通常は、 ソース・ファイルのどの部分がテキスト・インタープリターによって処理されるかを制御します。 これらは、 標準 Forth のワードには少数しかありませんが、 Gforth では、 非即実行バージョンがコンパイル状態でのみ使用できるという事実を補うために、 豊富な即実行制御構造ワードのセットでこれらを補っています(see Control Structures)。 一般的な使用法は以下のとおりです:

[undefined] \G [if]
  : \G ... ; immediate
[endif]

これは、 システムが \G を定義していない場合は、 幾つかの置き換えプログラムをコンパイルします(機能が制限される可能性があります)。

[IF] ( flag –  ) tools-ext “bracket-if”

flag が TRUE の場合は何もしません(したがって、 後続のワードは通常どおり実行されます)。 flag が FALSE の場合、 対になる [ELSE] または [THEN] がパースされて破棄されるまで、 [IF].. [ELSE].. [THEN][IF].. [THEN] の入れ子になったモノを含むパース領域(入力バッファー/パース領域 は、 必要に応じて REFILL を使用して再充填します)からワードをパースして破棄します。 即実行ワードです。

[ELSE] ( ) tools-ext “bracket-else”

対になる [THEN] がパースされて破棄されるまで、 [IF].. [ELSE].. [THEN][IF].. [THEN] の入れ子になったモノを含むパース領域(入力バッファー/パース領域 は、 必要に応じて REFILL を使用して再充填します)からワードをパースして破棄します。 FALSE の場合、[IF][ELSE] をパースして破棄し、後続のワードは通常どおり実行されたままになります。 即実行ワードです。

[THEN] ( ) tools-ext “bracket-then”

何もしない。 パースして破棄する他のワードのマーカーとして使用されます。 即実行ワードです。

[ENDIF] ( ) gforth-0.2 “bracket-end-if”

何もしません。 [THEN] の同義語(synonym)です。

[defined] ( "<spaces>name" – flag  ) tools-ext “bracket-defined”

現在の検索順序スタック(the search order)で名前が見つかった場合は true を返します

[undefined] ( "<spaces>name" – flag  ) tools-ext “bracket-undefined”

現在の検索順序スタック(the search order)で名前が見つかった場合は false を返します

[IFDEF] ( "<spaces>name" –  ) gforth-0.2 “bracket-if-def”

現在の検索順序スタック(the search order)で name が見つかった場合は、 TRUE フラグを伴った [IF] のように振る舞いし、 そうでない場合は、 FALSE フラグを伴った [IF] のように動作します。 即実行ワードです。

[IFUNDEF] ( "<spaces>name" –  ) gforth-0.2 “bracket-if-un-def”

現在の検索順序スタック(the search order)で name が見つからない場合は、 TRUE フラグを伴った [IF] のように動作し、 それ以外の場合は、FALSE フラグを伴った [IF] のように動作します。 即実行ワード。

[?DO] ( n-limit n-index –  ) gforth-0.2 “bracket-question-do”
[DO] ( n-limit n-index –  ) gforth-0.2 “bracket-do”
[LOOP] ( ) gforth-0.2 “bracket-loop”
[+LOOP] ( n –  ) gforth-0.2 “bracket-question-plus-loop”
[FOR] ( n –  ) gforth-0.2 “bracket-for”
[NEXT] ( n –  ) gforth-0.2 “bracket-next”
[I] ( run-time – n  ) gforth-0.2 “bracket-i”

実行時、 [I] はテキスト・インタープリター時の [do] の反復(iteration)のループ・インデックスをプッシュします。 インタープリター時にインデックスを処理したい場合は、 [I] を対話的に通訳(interpret)するか、 または INT-[I] を使用します。

INT-[I] ( – n  ) gforth-1.0 “int-bracket-i”

テキスト・インタープリター時に [do] 反復のループ・インデックスをプッシュします。

[BEGIN] ( ) gforth-0.2 “bracket-begin”
[UNTIL] ( flag –  ) gforth-0.2 “bracket-until”
[AGAIN] ( ) gforth-0.2 “bracket-again”
[WHILE] ( flag –  ) gforth-0.2 “bracket-while”
[REPEAT] ( ) gforth-0.2 “bracket-repeat”

#line を使用すると、 現在のソース行番号とソース・ファイルに関する Gforth の認識を変更できます。 これは、 Forth ファイルが他のソース・コード・ファイルから生成されており、 たとえば、 オリジナルのソース・コードを参照するエラー・メッセージなどを取得したい場合に便利です。 その場合、 Forth コード・ジェネレーターは、 必要に応じて Forth コードに #line 行を挿入する必要があります。

#line ( "u" "["file"]" –  ) gforth-1.0 “#line”

行番号を u に設定し、 (存在する場合)ファイル名を file に設定します。 行の残りの部分を消費します。


6.14.5 Recognizers

テキスト・インタープリターはソース・コードを処理するときに、 コードを空白で区切られた文字列に分割し、 認識器群(recognizers)の中からどれか1つの認識器が文字列を識別(identify)(認識(recognize))するまで認識器群を呼び出して、 それらをワードや数値などとして識別します。 その文字列が認識されない場合、 テキスト・インタプリタはエラー(undefined word)を報告します。

認識器を取り扱う通常の方法は、 認識器の 1 つを識別(identify)するコードを記述するだけです(see Default Recognizers)。 ただし、 それらを操作したり(see Dealing with existing Recognizers)、 新しい認識器を定義したりすることもできます(see Defining Recognizers)。


6.14.5.1 Default Recognizers

標準の Forth テキスト・インタープリターは、 検索順序スタック(the search order)内のワード(rec-nt) と、 整数(rec-num)と、 浮動小数点数(rec-float)を認識します。 デフォルトでは、 Gforth は 以下の構文も認識します

  • 文字列 例: "mystring" (rec-string)
  • 混合数値(complex numbers) 例: 0e+1ei (rec-complex)
  • value への格納または defered word の変更 例: ->myvalue (rec-to)
  • ワードのインタープリター機能(interpretation semantics)表す xt です。 例: `dup (rec-tick)
  • ワードの nt です。 例: ``mysynonym (rec-dtick)
  • ワードのボディのアドレス 例: <myarray+8> (rec-body)
  • オペレーティング・システムの環境変数へのアクセス 例: ${HOME} (rec-env)
  • ボキャブラリー内のワード 例: myvoc1:myvoc2:myword (rec-scope)
  • 指定の認識器(recognizer)使用して何かを認識する(訳注: recognizer? というプレフィックスが付いたワードは、 rec-recognizer によって処理され、 認識器の曖昧さを排除します。 例: hex num?cafe num?add rec-num としてのみパースさせる、 float?1. rec-floatとしてパースさせる)(rec-meta)

locate (see Locating source code definitions) を使用して、 ソース・コードの一片を、 どの認識器(recognizer)が認識するか調べられます。 例:

defer mydefer
locate ->mydefer

これは rec-to->mydefer を認識したことを示すハズです。 ただし、 認識器がディクショナリーのワードを認識する場合(スコープ認識機能など)、 locate はそのワードを表示します。

以下を使用すると、 使用されている認識器と認識器の順序を確認できます

.recognizers ( ) gforth-experimental “dot-recognizers”

(.order とは異なり、)最初に検索された認識器を左端にして、 現在の認識器の順序を出力します。 すべての認識器に共通のプレフィックスである rec- の代わりに、 反転表示の ~ が表示されます。

通常、 認識器は、 他の認識器と同一の文字列との一致を避けるように設計されています。 たとえば、rec-env (環境変数認識器)には、 $ADD のような入力文字列の数値認識器との競合を避けるために波括弧(braces;{})が必要です。 ただし、 このポリシーにはいくつかの例外があります:

  • ワード名には任意の名前を付けることができるため、 他の認識器と競合する可能性があります(また、 検索順序スタック(the search order)は他の認識器よりも前に検索されます)。

    ただし、 名前は 0 (ゼロ)で始まらない傾向がある(そして 0 (ゼロ) で始まる場合は特殊文字が含まれる傾向がある)ため、 base が hex の場合は、 数値を 0 (ゼロ)で始めることをお勧めします。

    これまで私たちが見てきた幾多のコードでは、 ワードを ' (クォート。 別名ティック)で始める方が、 ` (バック・クォート。別名バック・ティック) で始めるよりもはるかに一般的であるため、 xt と nt の認識器は ` (バック・クォート)を使用して競合を減らしてください。

  • 整数認識器 rec-num と、 浮動小数点認識器 rec-float は、 どちらも、 たとえば 1. を認識します。 しかし、 rec-num が(デフォルトでは)先にあるため、 1. は2倍長セルの整数として認識されます。 rec-float を最初に使用するように認識順序を変更すると、 1. は浮動小数点数として認識されますが、 標準 Forth で書かれたコードを読み込むと、 非標準的な振る舞いになる可能性があります。

    いずれの場合も、 以下のようにして、 あなた独自のコード内では、 この競合を回避することをお勧めします。 つまり、 常に数値プレフィックスを付けて2倍長セル整数を記述します(例: #1.)。 また、 浮動小数点数は常に e を使用して記述します(例: 1e)。

  • -> で始まるワードをいくつか見てきました。 to myvalue または to?->myvalue (訳注: rec-meta 構文で rec-to を指定の上で ->myvalue を評価)を使用すると競合を回避できます(後者は postpone で機能します)。

6.14.5.2 Dealing with existing Recognizers

認識器(recognizer)は、 あなたが文字列を渡すワードです。 認識器が文字列を認識すると、 通常はデータとそのデータを処理するためのワードの xt を返します。 このワードはトランスレーター(translator)と呼ばれます。 認識器が文字列を認識しない場合は、 notfound の xt を返します。

すべての認識器(recognizers)のスタック効果は ( c-addr u – ... xt ) です。

認識器は文字列を受け取り、 いくつかのデータと、 そのデータを通訳(interpret)するためのトランスレーター(翻訳器)を返します。 Gforth はそのトランスレーターを xt として実装します(これを実行すると、 現在の state でその文字列を処理する適切なアクションが実行されます)。 しかし、 他の Forth システムでは、 内部に 3 つの xt を含む実際のテーブルとして実装する可能性があります。 最初の xt は 通訳時(interpretation)/実行時(run-time) xt で、 データのインタープリター機能(interpretation semantics)を実行します(通常はデータをスタックに置くだけです)。 2 番目の xt はコンパイル機能(compilation semantics)を実行し、 データと実行時機能(run-time semantics)の xt を取得します。 3 番目の xt は postpone 機能(postpone semantics)を実行し、 データと実行時機能の xt も取得します。 >postpone を使用すると、 実行時機能の xt を postpone できます。

認識器群はスタックとして編成されるため、 ボキャブラリー・スタックと同じ方法で認識器群のシーケンスを配置できます。 認識器スタックはそれ自体が認識器群です。 つまり、 認識器スタックは実行可能であり、 文字列を受け取り、 トランスレーターを返します。

notfound ( state –  ) gforth-experimental “notfound”

認識器の認識が失敗すると、 notfound の認識器トークン(訳注: 現状では notfound の xt )を返します

rec-nt ( addr u – nt translate-nt | notfound  ) gforth-experimental “rec-nt”

名前トークンを認識します

rec-num ( addr u – n/d table | notfound  ) gforth-experimental “rec-num”

数値を 1倍長/2倍長 整数に変換します

rec-float ( addr u – r translate-float | notfound  ) gforth-experimental “rec-float”

浮動小数点数を認識します

rec-string ( addr u – addr u’ r:string | rectype-null  ) gforth-experimental “rec-string”

二重引用符(double quotes)で囲まれた文字列(strings)を文字列リテラルに変換します。 エスケープは S\" と同様に扱われます。

rec-to ( addr u – xt n r:to | rectype-null  ) gforth-experimental “rec-to”

-> で始まるワードは、 TO が前にあるものとして扱い、 +> で始まるワードは +TO が前にあるものとして扱い、 '> で始まるワードは ADDR が前にあるものとして扱い、 @> で始まるワードは ACTION-OF が前にあるものとして扱い、 => で始まるワードは IS が前にあるものとして扱います。

rec-tick ( addr u – xt rectype-num | rectype-null  ) gforth-experimental “rec-tick”

` (バッククォート)の接頭辞が付いたワードはその xt を返します。 例: `dup は dup の xt を返します

rec-dtick ( addr u – nt rectype-num | rectype-null  ) gforth-experimental “rec-dtick”

`` (バッククォート2つ)で始まるワードは、 その nt を返します。 例: ``S"S" の nt を返します。

rec-body ( addr u – xt translate-tick | translate-null  ) gforth-experimental “rec-body”

'<' '>' で囲まれたワードはその本体(body)を返します。 例: <dup> は dup の本体(body)を返します

get-recognizers ( – xt1 .. xtn n  ) gforth-experimental “get-recognizers”

認識器スタックの内容(content)をスタックにプッシュします。 n は xt の個数です。

set-recognizers ( xt1 .. xtn n –  ) gforth-experimental “set-recognizers”

スタック上の内容(content)を認識器スタックに設定します。 n は xt の個数です。

recognize ( addr u rec-addr – ... rectype  ) gforth-experimental “recognize”

訳注: 認識器スタック rec-addr の認識器を順に文字列 addr u に適用し、 認識したら、 そのトランスレーター rectype (の xt ) を得ます。 何も適用できなかった場合はトランスレーター notfound (の xt )を返します

recognizer-sequence: ( xt1 .. xtn n "name" –  ) gforth-experimental “recognizer-sequence:”

訳注: xt1 .. xtn n という内容で "name" という名前の認識器スタックを作成します。

forth-recognize ( c-addr u – ... translate-xt  ) recognizer “forth-recognize”

システム認識器スタックです。

forth-recognizer ( – xt  ) gforth-experimental “forth-recognizer”

Matthias Trute recognizer API との下位互換性があります。 この構造は、 defer されたワードを value のようなワードに変換します。

set-forth-recognize ( xt –  ) recognizer “set-forth-recognize”

システム認識器スタックを変更する

translate: ( int-xt comp-xt post-xt "name" –  ) gforth-experimental “translate:”

name という名前のトランスレーター(トランスレーター・テーブル)を作成し、 トランスレーターのインタプリンタ機能アクション int-xt と、 トランスレーターのコンパイル機能アクション comp-xt と、 トランスレーターの postpone 機能アクション post-xt を格納します。 加えて拡張機能(extensions)用に 7 つのスロットを用意します(拡張機能スロット初期値は no.extensions の xt で埋めます)。 実行時: name の 実行時は state の値に応じてアクションが実行されます。 state が 0 (インタプリンタ状態)ならばインタプリンタ機能アクションを実行し、 stete が -1 (コンパイル状態) ならばコンパイル機能アクションを実行し、 state が -2 (通常無い値。このために特別にセットする。アクション実行後は以前の値復元の事) ならばpostpone 機能アクションを実行します。

translate-nt ( i*x nt – j*x  ) gforth-experimental “translate-nt”

名前トークンのトランスレーター

translate-num ( x – | x  ) gforth-experimental “translate-num”

数値のトランスレーター

translate-dnum ( dx – | dx  ) gforth-experimental “translate-dnum”

2倍長整数のトランスレーター

doc-translate-float(訳注: まだ説明書いて無いっぽい)

try-recognize ( addr u xt – results | false  ) gforth-experimental “try-recognize”

入れ子(nested)になった認識器(recognizers)用です: addr u の認識を試み、 xt を実行して結果が望ましいかどうかを確認します。 xt が false を返した場合は、 認識器の副作用をすべてクリーンアップして false を返します。 それ以外の場合は、 xt 呼び出しの結果をスタックに返し、 そのTOSはゼロ以外でなければなりません。

>interpret ( translator –  ) gforth-experimental “>interpret”

指定のトランスレーターのインタプリンタ機能アクションを実行します。

>compile ( translator –  ) gforth-experimental “>compile”

指定のトランスレーターのコンパイル機能アクションを実行します。

>postpone ( translator –  ) gforth-experimental “>postpone”

指定のトランスレーターの postpone 機能アクションを実行します

translate-method: ( "name" –  ) gforth-experimental “translate-method:”

新しいトランスレーター・メソッドを作成し、 トランスレーター・テーブルを拡張します。 xt rectype to translator を使用して、 xt を既存のrectypeに割り当てることができます(訳注: >interpret>compile>postpone 定義用。 あなたが更に トランスレーター・メソッドを定義した場合はこれ以降の新規 state 値に対するアクションとなる。 現時点で 7 つまで追加可能)

translate-state ( xt –  ) gforth-experimental “translate-state”

xt として渡された translate-method に一致するトランスレーターのアクションを実行するように、 システムの現在の state を変更します。


6.14.5.3 Defining Recognizers

doc-defining-recognizers(訳注: まだ説明書けて無いっぽい)


6.14.6 Text Interpreter Hooks

before-line ( ) gforth-1.0 “before-line”

テキスト・インタープリターが次の行をパースする前に呼び出される defer されたワード(訳注: 初期値 noopinterpret 実行開始時にも呼び出される)

before-word ( ) gforth-0.7 “before-word”

テキスト・インタープリターが次のワードをパースする前に呼び出される deferd されたワード(訳注: 初期値 noop)

line-end-hook ( ) gforth-0.7 “line-end-hook”

ファイルからテキスト通訳(text-interpreting)するときに行末ごとに呼び出される defer されたワード(訳注: 初期値 noop )


6.15 The Input Stream

テキスト・インタープリターは、 複数のソース(see Input Sources)が利用可能な入力ストリームから読み取ります。 いくつかのワード、特に定義ワードや ' のようなワードは、 スタックからではなく入力ストリームからパラメーターを読み込みます。

このようなワードは入力ストリームをパース(parse)するため、 パース・ワードと呼ばれます。 パース・ワードの使用は困難です。 なぜならプログラムで生成されたパラメーターを入力ストリームを通じて渡すのが難しいからです。 また、 素朴な実装をすると、 インタープリター機能(interpretation semantics)とコンパイル機能(compilation semantics)の組み合わせが直感的でないことが多く、 より直感的な振る舞いを実現しようとする様々なアプローチが生まれています(see Combined Words)。

ワードをパースするのは悪い考えであることは、 最早明らかです。 あなたが、 利便性のためにパース・ワードを実装したいなら、 非パースのバリエーション、 つまり、 パースを行わずスタック上のパラメーターを受け取るワードも提供してください。 もし、 その上でパース・ワードを実装するなら、 以下のワード群を使用できます:

parse ( xchar "ccc<xchar>" – c-addr u  ) core-ext,xchar-ext “parse”

パース領域で xchar で区切られた ccc をパースします。 c-addr u は、 パース領域内でパースされた文字列を指します。 パース領域が空の場合、 u は 0 です(訳注: bl parse ccc type ccc ok 。 直後でENTERキーを押して行を終了すると(parse ENTER)、 長さ0の文字列をスタックに積む)

string-parse ( c-addr1 u1 "ccc<string>" – c-addr2 u2  ) gforth-1.0 “string-parse”

文字列 c-addr1 u1 で区切られた ccc をパース領域でパースします。 c-addr2 u2 は、 パース領域内のパースされた文字列を指します。 パース領域が空の場合、 u2 は 0 です(訳注: " " string-parse ccc type ccc ok)

parse-name ( "name" – c-addr u  ) core-ext “parse-name”

入力バッファーから次の単語(word)を取得します(訳注: parse-name ccc type ccc ok)

parse-word ( – c-addr u  ) gforth-obsolete “parse-word”

parse-name の古い名前。 このワード語は、他の一部のシステムでは矛盾する振る舞いをします。

name ( – c-addr u  ) gforth-obsolete “name”

parse-name の古い名前

word ( char "<chars>ccc<char>– c-addr  ) core “word”

先頭の区切り文字をスキップします。 パース領域で char で区切られた ccc をパースします。 c-addr は、 カウンタ付き文字列形式で、 パースされた文字列を格納する一時領域のアドレスです。 パース領域が空であるか、 区切り文字以外の文字が含まれてい無い場合、 結果の文字列の長さはゼロになります。 プログラムは、 カウンタ付き文字列内の文字を置き換える場合があります。 時代遅れ(OBSOLESCENT): カウンタ付き文字列の末尾に、 その長さに含まれないスペースがあります。

refill ( – flag  ) core-ext,block-ext,file-ext “refill”

入力ソースにより入力バッファーを満たすことを試みます。 入力ソースがユーザー入力デバイスの場合、 端末入力デバイスからの入力の受け取りを試みます。 成功した場合は、 結果を入力バッファーにし、 >IN を 0 に設定して true を返します。 それ以外の場合は false を返します。 入力ソースがブロックの場合、 BLK の値に 1 を加算して、 次のブロックを入力ソースかつ現在の入力バッファーにし、 >IN を 0 に設定します。 BLK の新しい値が有効なブロック番号の場合は true を返し、 それ以外の場合は false を返します。 入力ソースがテキスト・ファイルの場合、 ファイルから次の行を読み取ろうと試みます。 成功した場合は、 結果を現在の入力バッファーにし、 >IN を 0 に設定して true を返します。 それ以外の場合は false を返します。 いずれの場合も、 成功した結果には、0 文字を含む行の受け取りが含まれます。

非パースなバリエーションが無いパース・ワードを取り扱う必要がある場合、 execute-parsing を使用して(スタック経由で)文字列を渡すことができます:

execute-parsing ( ... addr u xt – ...  ) gforth-0.6 “execute-parsing”

addr u を現在の入力ソースにして xt ( ... -- ... ) を実行してから、 以前の入力ソースを復元します。

Example:

5 s" foo" ' constant execute-parsing
\ これは、以下と同等です
5 constant foo

標準 Forth でのこのワードの定義は compat/execute-parsing.fs で提供されます。

ファイルに対してワードのパースを実行したい場合は、 以下のワードが役に立ちます:

execute-parsing-file ( i*x fileid xt – j*x  ) gforth-0.6 “execute-parsing-file”

fileid を現在の入力ソースにして、 xt ( i*x -- j*x ) を実行してから、 以前の入力ソースを復元します。


6.16 Word Lists

ワードリスト(wordlist)は名前付きワードのリストです。 新しいワードを追加したり、 ワードを名前で探したりできます(マーカー(markers)を使用して制限された方法でワードを削除することもできます)。 全ての名前付き(および reveal された)ワードは 1 つのワードリスト内にあります。

テキスト・インタープリターは、 検索順序スタック(the search order;ワードリストのスタック)に存在するワードリストをTOSから下へ検索します。 各ワードリスト内では、 概念的には最新のワードから検索が開始されます。 つまり、 ワードリスト内に同一の名前の 2 つのワードがある場合、 新しいワードが検索にヒットします。

新しいワードは「コンパイル・ワードリスト」(compilation wordlist)(現在のワードリスト(current wordlist)とも呼ばれます)に追加されます。

ファイルがファイル・ハンドルによって識別されるのとほぼ同じ方法で、 ワードリストはセル・サイズのワードリスト識別子(word list identifier; wid)によって識別されます。 wid の数値には(移植可能な)意味はなく、 セッションごとに変わる可能性があります。

標準 Forth の “Search order” ワード・セットは、 さまざまな異なるスキームの実装を可能にする低レベル・ツールのセットを提供することを目的としています。 Gforth は、 伝統的な Forth ワードである vocabulary も提供します。 compat/vocabulary.fs は、 標準 Forth での実装を提供します。

forth-wordlist ( – wid  ) search “forth-wordlist”

定数(Constant) – wid は、Gforth が提供するすべての標準のワードを含むワードリストを識別します。 Gforth が呼び出されると、 このワードリストがコンパイル・ワードリストとなり、 検索順序スタック(the search order)のTOSになります。

definitions ( ) search “definitions”

現在検索順序スタック(the search order)のTOSにあるワードリストをコンパイル・ワードリストにします(訳注: : definitions context  current ! ; )

get-current ( – wid  ) search “get-current”

wid は、 現在のコンパイル・ワードリストのワードリスト識別子です。

set-current ( wid –  ) search “set-current”

wid で識別されるワードリストをコンパイル・ワードリストに設定します。

get-order ( – widn .. wid1 n  ) search “get-order”

検索順序スタック(the search order)の内容をデータ・スタックにコピーします。 現在の検索順序スタックには n 個のエントリがあり、 そのうち wid1 は最初に検索されるワードリスト(検索順序スタックのTOSにあるワードリスト) を表し、 widn は最後に検索されるワードリストを表します。

set-order ( widn .. wid1 n –  ) search “set-order”

n=0 の場合、 検索順序スタック(the search order)を空(empty)にします。 n=-1 の場合、 検索順序スタックを実装定義で最小化します (Gforth の場合、 これはワードリスト Root です)。 それ以外の場合は、 n 個の wid エントリを持ち、 wid1 が最初に検索されるワードリストを表し、 widn が最後に検索されるワードリストを表すように、 既存の検索順序スタックを置き換えます。

wordlist ( – wid  ) search “wordlist”

wid で表される新しい空(empty)のワードリストを作成します。

table ( – wid  ) gforth-0.2 “table”

検索テーブル(lookup table)を作成します(英大文字と小文字を区別し、 警告なし)。

cs-wordlist ( – wid  ) gforth-1.0 “cs-wordlist”

英大文字小文字を区別するワードリストを作成する。

cs-vocabulary ( "name" –  ) gforth-1.0 “cs-vocabulary”

英大文字小文字を区別するボキャブラリーを作成する

>order ( wid –  ) gforth-0.5 “to-order”

検索順序スタック(the search order)というワードリストのスタックにワードリスト wid をプッシュする(訳注: wid が 検索順序スタックの TOS になる)。

previous ( ) search-ext “previous”

検索順序スタック(the search order)というワードリスト・スタックのTOSを捨てる

also ( ) search-ext “also”

検索順序スタック(the search order)でTOSを DUP するかのように振る舞います(訳注: : also context  >order ;)。 通常はボキャブラリーの前で使います(例えば also Forth)。 これにより、 ボキャブラリーによって表されるワードリストが検索順序スタックへプッシュされるという複合的な効果が得られます(訳注:検索順序スタックのTOSを指定のボキャブラリーで置き換えるのではなくて、 検索順序スタックのTOSにさらにプッシュする形にして、 ボキャブラリーの次が以前の検索順序スタックのTOSになるようにする)

Forth ( ) search-ext “Forth”

検索順序スタック(the search order)の先頭にある wid を、 ワードリスト forth-wordlist に関連付けられた wid に置き換えます。

Only ( ) search-ext “Only”

検索順序スタック(the search order)を実装定義で最小化します(Gforth の場合、 これはワードリスト Root です)。

order ( ) search-ext “order”

検索順序スタック(the search order)とコンパイル・ワードリストを出力します。 検索順序スタックのワードリストは検索される順序で出力されます(従来のスタック表示方法とは逆になります)。 続けて、 コンパイル・ワードリストが最後に表示されます。

.voc ( wid –  ) gforth-0.2 “dot-voc”

wid で表されるワードリストの名前を出力します。 vocabulary または wordlist constant で定義された名前のみ出力できます。 それ以外の場合はアドレスを出力します。

find ( c-addr – xt +-1 | c-addr 0  ) core,search “find”

カウンタ付き文字列 c-addr によって指定された定義の名前を現在の検索順序スタック(the search order)内の全てのワードリストで検索します。 定義が見つからない場合、 0 を返します。 定義が見つかった場合は、 1 (定義にデフォルト以外のコンパイル機能(compilation semantics)ある場合)または、 -1 (定義にデフォルトのコンパイル機能(compilation semantics)がある場合)を返します。 インタープリター状態で返される xt はインタープリター機能(interpretation semantics)を表します。 コンパイル状態で返される xt は、 コンパイル機能(デフォルト以外のコンパイル機能の場合)を表すか、または、 コンパイル機能が compile, (デフォルトのコンパイル機能の場合)となる実行時機能(run-time semantics)を表します。 ANS Forth 標準では、 返される xt が何を表すかを明確に規定していない(または、 デフォルト以外のコンパイル機能ではなく即実行性についても言及している)ため、 このワードは移植可能なプログラムでは問題があります。 移植性がなくても問題ない場合には、 find-name とその友達の方が優れています(see Name token)。

search-wordlist ( c-addr count wid – 0 | xt +-1  ) search “search-wordlist”

wid で識別されるワードリスト内で、 c-addr count の文字列で指定された定義を検索します。 定義が見つからない場合は 0 を返します。 定義が見つかった場合は、 xt とともに 1 (定義が即実行である場合)または、 -1 (定義が即実行でない場合)を返します。 Gforth では、返される xt はインタープリター機能(interpretation semantics)を表します。 ANS Forth では、 xt が何を表すか明確に規定していません。

words ( ) tools “words”

検索順序スタック(the search order)の先頭にあるワードリスト内のすべての定義のリストを表示します。

vlist ( ) gforth-0.2 “vlist”

WORDS の古い(Forth-83 より前の)名前。

wordlist-words ( wid –  ) gforth-0.6 “wordlist-words”

ワードリスト wid の内容を表示します(訳注: ワードリストに含まれているワードをリストする)。

mwords ( ["pattern"] –  ) gforth-1.0 “mwords”

オプションのパラメーター pattern にマッチするすべてのワードをリストします。 指定しない場合、 すべてのワードが一致します。 ワードは古い方から新しい方へとリストされます。 search のようなパターン・マッチ(デフォルト)ですが、 ' mword-filename-match is mword-match を使用してワイルドカード(globbing)に切り替えることができます(訳注: mwords ENTER で全部リスト、 例えば mwords value で value を含む名前をリスト)。

Root ( ) gforth-0.2 “Root”

root ワードリストを検索順序スタック(the search order)に追加します。 このボキャブラリは最小の検索順序を構成し、 search-order のワードのみが含まれます。

Vocabulary ( "name" –  ) gforth-0.2 “Vocabulary”

"name" の定義を作成し、 それに新しいワードリストを関連付けます。 "name" の実行時の効果は、 検索順序スタック(the search order)のTOSにある wid を、 "name" に関連付けられた wid に置き換えることです。

seal ( ) gforth-0.2 “seal”

現在検索順序スタック(the search order)のTOSにあるワードリスト以外のすべてのワードリストを検索順序スタックから削除します。

vocs ( ) gforth-0.2 “vocs”

システムで定義されているボキャブラリーとワードリストをリストします。

current ( – addr  ) gforth-0.2 “current”

変数(Variable) – コンパイル・ワードリストの wid を保持。

context ( – addr  ) gforth-0.2 “context”

context @ すると、 検索順序スタック(the search order)のTOSにあるワードリストの wid が得られます。

map-vocs ( ... xt – ...  ) gforth-1.0 “map-vocs”

システム内のすべてのワードリスト(テーブルと cs-wordlist を含む)に対して xt ( ... wid – ...) を実行します。


6.16.1 Vocabularies

以下は、 標準の Forth ワード群を使用して新しいワードリストを作成および使用する例です:

wordlist constant my-new-words-wordlist
: my-new-words get-order nip my-new-words-wordlist swap set-order ;

\ add it to the search order
also my-new-words

\ alternatively, add it to the search order and make it
\ the compilation word list
also my-new-words definitions
\ type "order" to see the problem

この例での問題は、 order には my-new-words という名前をワードリスト の wid に関連付ける方法がないことです(Gforth では、 ordervocs において、 名前が関連付けられていない wid では wid そのものが表示されます)。 名前を wid に関連付ける標準の方法はありません。

Gforth では、 この例は、 以下のように、 名前を wid に関連付ける vocabulary を使用して再コーディングできます:

vocabulary my-new-words

\ add it to the search order
also my-new-words

\ alternatively, add it to the search order and make it
\ the compilation word list
my-new-words definitions
\ type "order" to see that the problem is solved

6.16.2 Why use word lists?

人々がワードリストを使用する理由は以下のとおりです:

  • 一連のワードが、 それらが有効なコンテキスト外で使用されるのを防ぐため。 この典型的な 2 つの例が、 統合エディター(すべての編集コマンドは別個のワードリストで定義されます。 エディターの起動時に検索順序スタック(the search order)がエディターのワードリストに設定されます。 エディターが終了すると古い検索順序スタックが復元されます)。 )と、 統合されたアセンブラー(マシンのオペコードは、CODE ワードが定義されるときに使用される別のワードリストで定義されます)。
  • アプリケーションまたはライブラリーのワードを、 ユーザーに表示される組(forth-wordlist または、 他の一般的なワードリスト内)と、 実装のためだけに使用されるヘルパー・ワードの組(別のワードリストに隠されている)に編成します。 これにより、 words の出力が少なくなり、 実装とインターフェイスが分離され、 共通のワードリスト内で名前が競合する可能性が減ります。
  • 同じ名前を持つ複数の定義間の名前空間の衝突を防ぐため。 たとえば、 クロス・コンパイラを構築する場合、 ターゲット・システムの条件付きコードを生成するワード IF が存在する場合があります。 この定義を別のワードリストに配置すると、 検索順序スタック上のワードリストの順序を制御することで、 ホスト・システムの IF またはターゲット・システムの IF を特定の文脈で使用するかどうかを制御できます。

ワードリストを使用する場合の欠点は以下のとおりです:

  • デバッグがさらに面倒になります。
  • ワードリストを使用して回避された名前の競合は依然として存在しており、 望ましい結果を得るには検索順序を慎重に調整する必要があります。 これを怠ると、 見つけにくいエラーが発生します(コンパイラーとは異なる方法でコードを読み取る場合と同様に、 see は、 そのような場合に、 名前がいくつかの考えられるワードのうちのどれに解決(resolve)されるかを確認するのに役立ちます)。 see はワードの名前だけを表示し、 ワードがどのワードリストに属しているかを表示しないため、 誤解を招く可能性があります。 一意の名前を使用することは、 名前の競合を避けるためのより良いアプローチです。
  • あなたは、 検索順序スタック(the search order)の変更を明示的に元に戻す必要があります。 多くの場合、 これは暗黙的に行われた方が便利です。 Gforth は現在そのような機能を提供していませんが、 将来的には提供される可能性があります。

6.16.3 Word list example

以下の例は garbage collector からのもので、 ワードリストを使用してパブリック・ワードとヘルパー・ワードを分離しています:

get-current ( wid )
vocabulary garbage-collector also garbage-collector definitions
... \ define helper words
( wid ) set-current \ restore original (i.e., public) compilation wordlist
... \ define the public (i.e., API) words
    \ they can refer to the helper words
previous \ restore original search order (helper words become invisible)

6.17 Environmental Queries

Forth-94 は、システム上で実行されているプログラムがシステムの特定の特性を判断する方法として「環境問い合わせ」(“environmental query”)という概念を導入しました。 標準では、 システムが認識できるたくさんの文字列と、 それらを問い合わせする方法が指定されています。

environment? ( c-addr u – false / ... true  ) core “environment-query”

文字列 c-addr, u を指定します。 文字列が認識されない場合は、 false フラグを返します。 それ以外の場合は、true フラグと、 問い合わせした文字列に関する(それ固有の)情報を返します。

注意: 例えば ADDRESS-UNIT-BITS のドキュメントではスタック上に 1 つのセルを返すことが示されていますが、 environment? を使用して問い合わせすると、 文字列が認識されたことを示す true フラグという追加の項目が返されることに注意してください。 ADDRESS-UNIT-BITS を問い合わせた場合、 environment? のスタック効果は ( c-addr u -- n true ) です。

いくつかの環境問い合わせ(environmental query)はシステムの制限を取り扱います:

ADDRESS-UNIT-BITS ( – n  ) environment “ADDRESS-UNIT-BITS”

1 つのアドレス単位のサイズ(ビット単位)。

MAX-CHAR ( – u  ) environment “MAX-CHAR”

文字セット内の任意の文字の最大値

/COUNTED-STRING ( – n  ) environment “slash-counted-string”

カウンタ付き文字列の最大サイズ(文字単位)。

/HOLD ( – n  ) environment “slash-hold”

表示数値文字列の出力バッファーのサイズ(文字単位)。

/PAD ( – n  ) environment “slash-pad”

PAD が指すスクラッチ領域のサイズ(文字数)。

CORE ( – f  ) environment “CORE”

完全なコア・ワード・セットが存在する場合は true。 Gforth では常に当てはまります。

CORE-EXT ( – f  ) environment “CORE-EXT”

完全なコア拡張ワード・セットが存在する場合は true。 Gforth では常に当てはまります。

FLOORED ( – f  ) environment “FLOORED”

true ならば、 / などで、 フロア除算(floored division)を実行します。

MAX-N ( – n  ) environment “MAX-N”

使用可能な符号付き整数の最大値。

MAX-U ( – u  ) environment “MAX-U”

使用可能な符号なし整数の最大値。

MAX-D ( – d  ) environment “MAX-D”

使用可能な符号付き2倍長整数の最大値。

MAX-UD ( – ud  ) environment “MAX-UD”

使用可能な符号なし2倍長整数の最大値。

return-stack-cells ( – n  ) environment “return-stack-cells”

リターン・スタックの最大サイズ(セル単位)。

stack-cells ( – n  ) environment “stack-cells”

データ・スタックの最大サイズ(セル単位)。

floating-stack ( – n  ) environment “floating-stack”

n はゼロ以外で、 Gforth が深さ n の別個の浮動小数点スタックを維持していることを示しています。

#locals ( – n  ) environment “number-locals”

定義内のローカル変数の最大数

wordlists ( – n  ) environment “wordlists”

検索順序スタック(the search order)で使用できるワード・リストの最大数

max-float ( – r  ) environment “max-float”

使用可能な浮動小数点数の最大値(Gforth では最大の有限数(finite number)として実装)

XCHAR-ENCODING ( – addr u  ) environment “XCHAR-ENCODING”

エンコーディングを表す印刷可能なASCII文字列を返し、 (あれば)、 優先されるMIME名、 または “ISO-LATIN-1” や “UTF-8” のような http://www.iana.org/assignments/character-sets の名前を使用します。 ただし、“ASCII” の場合は、 エイリアスの “ASCII” を優先します。

MAX-XCHAR ( – xchar  ) environment “MAX-XCHAR”

xchar の最大値。 これはエンコーディングによって異なります。

XCHAR-MAXMEM ( – u  ) environment “XCHAR-MAXMEM”

1 つの xchar によって消費される最大メモリーをアドレス単位で返します。

Forth-94 バージョンのワードセットの存在を確認するための環境問い合わせ(environemtal query)がいくつかあります。 当該文字列が存在する場合、 それらはすべて ( -- f ) のスタック効果を持ちます(つまり、 これらの問い合わせの environment? のスタック効果は ( c-addr u -- false / f true ) です)。

block block-ext double double-ext exception exception-ext facility facility-ext file file-ext floating floating-ext locals locals-ext memory-alloc memory-alloc-ext tools tools-ext search-order search-order-ext string string-ext

上記ワードセットの問い合わせは、 ほとんど使用および実装されなかったため、 Forth-2012 ではこれらのワードセットの Forth-2012 バリエーションを問い合わせする方法が導入されませんでした。 代わりに、 [defined] (see Interpreter Directives) を使用するというアイディアです。

Forth-200x (次の標準に取り組むグループ。 このグループが作成するドキュメントも Forth-200x と呼ばれます)は、 提案された拡張機能(extension proposals)の変更が完了すると(CfV 段階)、 提案された拡張機能に対する拡張機能問い合わせ(extension query)を定義します。 そのため、 これらの提案を採用するプログラムは、 提案された拡張機能が適切かどうかを確認できます。 システムにはそれらがあり、 おそらくリファレンス実装(存在する場合)をロードします。 environment? がそのような問い合わせを見つけた場合、 www.forth200x.org 上の対応する提案がシステムに実装されます(ただし、 environment? の場合と同様、 存在しないモノは何も分かりません)。 これらの問い合わせにはスタック効果 ( -- ) があります。 つまり、 問い合わせに対して environment? ではスタック効果 ( c-addr u -- false / true ) があり、 これはワードセット問い合わせよりも便利です。。 これらの提案の多くは Forth-2012 に組み込まれています。 拡張機能問い合わせも Forth システム実装者の間で特に人気があるわけではないため、 [define] を使用する方が良いアプローチである可能性があります。 とにもかくにも、 Gforth は以下の拡張機能問い合わせ(extension query)を実装します:

X:2value X:buffer X:deferred X:defined X:ekeys X:escaped-strings X:extension-query X:fp-stack X:ftrunc X:fvalue X:locals X:n-to-r X:number-prefixes X:parse-name X:required X:s-escape-quote X:s-to-f X:structures X:synonym X:text-substitution X:throw-iors X:traverse-wordlist X:xchar

さらに、 Gforth は以下の Gforth 固有の問い合わせを実装します:

gforth ( – c-addr u  ) gforth-environment “gforth”

Gforth のこのバージョン(バージョン > 0.3.0)のバージョン文字列を表す文字列。 さまざまなバージョンのバージョン文字列は、 辞書順(lexicographically)に並べることができることが保証されています。

os-class ( – c-addr u  ) gforth-environment “os-class”

ホスト・オペレーティング・システムを説明する文字列。

os-type ( – c-addr u  ) gforth-environment “os-type”

$host_os に等しい文字列

標準の問い合わせでは、 環境問い合わせ(environmental query)に使用されるヘッダー・スペース(header space)が、 定義に使用されるヘッダー・スペースとは異なることが要求されます。

通常、 Forth システムは、 環境問い合わせ(environmental query)「だけ」に使用されるワードリスト内に定義を作成することにより、 環境問い合わせをサポートします。 それが Gforth のやっていることです。 認知されている環境問い合わせのセットに定義を追加する標準的な方法はありませんが、 Gforth や、 ワードリスト・メカニズムを使用するその他のシステムでは、 環境問い合わせを受け入れるために使用されるワードリストは、 他のワードリストと同様に操作できます。

environment-wordlist ( – wid  ) gforth-0.2 “environment-wordlist”

wid は、 環境問い合わせ(environmental query)によって検索されるワードリストを識別します(SwiftForth および VFX に存在)。

environment ( ) gforth-0.6 “environment”

environment-wordlist のボキャブラリー (Win32Forth および VFX に存在)。

以下に、 環境問い合わせ(environmental query)の使用例をいくつか示します:

s" address-unit-bits" environment? 0=
[IF]
     cr .( environmental attribute address-units-bits unknown... ) cr
[ELSE]
     drop \ ensure balanced stack effect
[THEN]

\ 標準で throw を使うときにプログラムの冒頭でありそうなコード断片
\ 訳注: throw をサポートしていなければ abort" に置き換え
s" exception" environment? [IF]
   0= [IF]
      : throw abort" exception thrown" ;
   [THEN]
[ELSE] \ we don't know, so make sure
   : throw abort" exception thrown" ;
[THEN]

s" gforth" environment? [IF] .( Gforth version ) TYPE
                        [ELSE] .( Not Gforth..) [THEN]

\ a program using v*
s" gforth" environment? [IF]
  s" 0.5.0" compare 0< [IF] \ v* is a primitive since 0.5.0
   : v* ( f_addr1 nstride1 f_addr2 nstride2 ucount -- r )
     >r swap 2swap swap 0e r> 0 ?DO
       dup f@ over + 2swap dup f@ f* f+ over + 2swap
     LOOP
     2drop 2drop ; 
  [THEN]
[ELSE] \ 
  : v* ( f_addr1 nstride1 f_addr2 nstride2 ucount -- r )
  ...
[THEN]

以下は、 environment ワードリストに定義を追加する例です:

get-current environment-wordlist set-current
true constant block
true constant block-ext
set-current

以下のようにして、 environment ワードリストどのような定義があるかを確認できます:

environment-wordlist wordlist-words

6.18 Files

Gforth は、 ホスト・オペレーティング・システムのファイル・ システムに保存されているファイルにアクセスするための機能を提供します。 Gforth によって処理されるファイルは、 以下の 2 つのカテゴリに分類できます:

  • テキスト・インタープリターによって処理される Forth ソース・ファイル(Forth source files)
  • 他のプログラムによって処理される一般ファイル(general files)。

6.18.1 Forth source files

ファイルの内容を通訳(interpret)する最も簡単な方法は、 以下の 2 つの形式のいずれかを使用することです:

include mysource.fs
s" mysource.fs" included

通常、 ファイルをインクルードする必要があるのは、 そのファイルがまだインクルードされていない場合(たとえば、 別のソース・ファイルなど)です。 その場合、 以下の 3 つの形式のいずれかを使用できます:

require mysource.fs
needs mysource.fs
s" mysource.fs" required

ソース・ファイルを通訳(interpret)してもスタックが変更されないようにソース・ファイルを作成することをお勧めします。 この方法で設計されたソース・ファイルは、 required やそのファミリーと一緒に問題なく使用できます。 例:

1024 require foo.fs drop

ここでは、 引数 1024 (バ​​ッファ・サイズなど) を foo.fs に渡しています。 foo.fs の通訳(interpret)にはスタック効果 ( n – n ) があり、 require での使用が可能になります。 もちろん、 require されるファイルにこのようなパラメーターを指定する場合は、 最初の require がすべての用途に適合することを確認する必要があります(つまり、 マスター・ロード・ファイルの早い段階で require することになります)。

include-file ( i*x wfileid – j*x  ) file “include-file”

ファイル wfileid の内容を通訳(interpret)します(テキスト・インタープリターを使用して処理します)。

included ( i*x c-addr u – j*x  ) file “included”

文字列 c-addr u で指定される名前のファイルを include-file します。

included? ( c-addr u – f  ) gforth-0.2 “included?”

ファイル c-addr u が以前にインクルードされたファイルのリストにある場合にのみ true。 ファイルがロードされている場合、 たとえば foo.fs として指定されたのが Forth 検索パスのどこかで見つかった可能性があります。 include? から true を返すには、 (たとえ ./foo.fs であっても、 )ファイルへの正確なパスを指定する必要があります。

include ( ... "file" – ...  ) file-ext “include”

fileinclude-file する。

required ( i*x addr u – i*x  ) file-ext “required”

既に include (または required ) されてないなら、 addr u で指定した名前のファイルを include-file します。 現状では、 これは(パス(path)付きの)ファイル名を以前にインクルードしたファイルの名前と比較することによって機能します。

require ( ... "file" – ...  ) file-ext “require”

file がまだインクルードされていない場合のみ、 include-file します。

needs ( ... "name" – ...  ) gforth-0.2 “needs”

require のエイリアス。 他のシステム(Win32Forth など)に存在します。

\\\ ( ) gforth-1.0 “\\\”

ソース・ファイルの残りをEOFまでスキップする

.included ( ) gforth-0.5 “.included”

インクルードされたファイルの名前をリストします。

sourcefilename ( – c-addr u  ) gforth-0.2 “sourcefilename”

現在入力ソースとなっているソース・ファイルの名前。 結果は、 ファイルがロードされている間のみ有効です。 現在の入力ソースが(ストリーム)ファイルでない場合、 結果は未定義です。 Gforth では、 結果はセッション全体で有効です(ただし、 savesystem などを跨いで有効ではありません)。

sourceline# ( – u  ) gforth-0.2 “sourceline-number”

(ストリーム)ファイルから現在通訳(interpret)されている行の行番号。 最初の行は番号 1 です。 現在の入力ソースが(ストリーム)ファイルでない場合、 結果は未定義です。

required の標準 Forth の定義は compat/required.fs で提供されます。


6.18.2 General files

ファイルは名前と種類によって開かれたり作成されたりします。 以下のファイル・アクセス・メソッド(FAM)が認識されます:

r/o ( – fam  ) file “r-o”
r/w ( – fam  ) file “r-w”
w/o ( – fam  ) file “w-o”
bin ( fam1 – fam2  ) file “bin”
+fmode ( fam1 rwxrwxrwx – fam2  ) gforth-1.0 “plus-f-mode”

ファイル・アクセス・モードを fam に追加 - create-file のみ(訳注: rwxrwxrwx は chmod と同様 777 とか 666 。 ただし gforth には直接 8進数記述する方法が無い)

ファイルを開いたり作成したりすると、 他のすべてのファイル・コマンドに使用されるファイル識別子 wfileid が返されます。 すべてのファイル・コマンドは、 ステータス値 wior も返します。 これは、 操作が成功した場合は 0 を返し、 エラーの場合は実装で定義されたゼロ以外の値を返します。

open-file ( c-addr u wfam – wfileid wior ) file “open-file”
create-file ( c-addr u wfam – wfileid wior ) file “create-file”
close-file ( wfileid – wior ) file “close-file”
delete-file ( c-addr u – wior ) file “delete-file”
rename-file ( c-addr1 u1 c-addr2 u2 – wior ) file-ext “rename-file”

ファイル c_addr1 u1 のファイル名を新しい名前 c_addr2 u2 に変更します(rename)

read-file ( c-addr u1 wfileid – u2 wior ) file “read-file”

ファイル wfileid から u1 文字を c_addr からのバッファーに読み取ります。 ゼロ以外の wior はエラーを示します。 U2は読み出したデータの長さを示します。 ファイルの終わりはエラーではなく、 u2$<$u1 かつ wior=0 によって示されます。

read-line ( c_addr u1 wfileid – u2 flag wior  ) file “read-line”

wfileid から c_addr u1 のバッファーに行を読み取ります。 Gforth は、 LF と CR と CRLFの 3 つの一般的な行終端文字をすべてサポートします。 ゼロ以外の wior はエラーを示します。 false の flag は、ファイルの最後(end of the file)で read-line が呼び出されたことを示します。 u2 は行の長さ(ターミネータなし)を示します。 u2<u1 は行の長さが u2 文字であることを示します。 u2=u1 は、 行が少なくとも u1 文字長であり、 バッファーの u1 文字がその行の文字で埋められており、 そして、 その行の次のスライスは次の read-line で読み取られます。 行の長さが u1 文字の場合、最初の read-lineu2=u1 を返し、 次の read-line は u2=0 を返します。

key-file ( wfileid – n  ) gforth-0.4 “key-file”

wfileid から 1 文字 n を読み取ります。 このワードは wfileid のバッファリングを無効にします。 あなたが端末から非標準モード(non-canonical mode)(RAWモード)で文字を読み取りたい場合は、(C言語インターフェイスを使用して)自分で端末を非標準モード(non-canonical mode)にする必要があります。 例外は stdin で、 この場合 Gforth は自動的に非正規モード(non-canonical mode)に設定します。

key?-file ( wfileid – f ) gforth-0.4 “key-q-file”

f は、 ブロッキング無しに wfileid から少なくとも 1 文字を読み取ることができる場合に true になります。 ファイルに対して read-file または read-line も使用したい場合は、 最初に key?-file または key-file を呼び出す必要があります(これら 2 つのワードはバッファリングを無効にします)。

file-eof? ( wfileid – flag ) gforth-0.6 “file-eof-query”

wfileid のファイル終了インジケーター(end-of-file indicator)がセットされている場合、 Flag は true です。

write-file ( c-addr u1 wfileid – wior ) file “write-file”
write-line ( c-addr u wfileid – ior  ) file “write-line”
emit-file ( c wfileid – wior ) gforth-0.2 “emit-file”
flush-file ( wfileid – wior ) file-ext “flush-file”
file-status ( c-addr u – wfam wior ) file-ext “file-status”
\ 訳注: wior <> 0 ;ファイルが存在しない(wfamは未定義の値)、
\       wior=0 かつ  ( wfam=2 r/w 、 wfam=0 r/o 、 wfam=4 w/o 、
\       wfam=1 r/o bin いずれのacessモードチェックもエラーになった時。
\       便宜的に読み込み専用かつバイナリとして返す)
file-position ( wfileid – ud wior ) file “file-position”
reposition-file ( ud wfileid – wior ) file “reposition-file”
file-size ( wfileid – ud wior ) file “file-size”
resize-file ( ud wfileid – wior ) file “resize-file”
slurp-file ( c-addr1 u1 – c-addr2 u2  ) gforth-0.6 “slurp-file”

c-addr1 u1 はファイル名、 c-addr2 u2 はファイルの内容です(訳注: slurp;音を立ててすする の意味)

slurp-fid ( fid – addr u  ) gforth-0.6 “slurp-fid”

addr u はファイル fid の内容です(訳注: slurp;音を立ててすする の意味)

stdin ( – wfileid ) gforth-0.4 “stdin”

Gforth プロセスの標準入力ファイル。

stdout ( – wfileid ) gforth-0.2 “stdout”

Gforth プロセスの標準出力ファイル。

stderr ( – wfileid ) gforth-0.2 “stderr”

Gforth プロセスの標準エラー出力ファイル。


6.18.3 Redirection

typeemit の出力と、 それらを使用するすべてのワード(明示的なターゲット・ファイルを持たないすべての出力用ワード)を、 outfile-execute を使用して任意のファイルにリダイレクトできます。 以下のように使用します:

: some-warning ( n -- )
    cr ." warning# " . ;

: print-some-warning ( n -- )
    ['] some-warning stderr outfile-execute ;

これは、 some-warning を execute した後、 元の出力先を復元します。 この構造は例外に対して安全です。 同様に、 key からの入力と、 それを利用した入力(明示的にファイルを指定しない入力用ワード)をリダイレクトするための infile-execute があります。

outfile-execute ( ... xt file-id – ...  ) gforth-0.7 “outfile-execute”

type などの出力を file-id にリダイレクトして xt を execute します。

outfile-id ( – file-id  ) gforth-0.2 “outfile-id”

File-id は、 emit や、 type や、 入力として file-id を受け取らない出力用ワードによって使用されます。 outfile-execute で変更しない限り、 デフォルトでは outfile-id は実行中のプロセスの stdout を生成します。

infile-execute ( ... xt file-id – ...  ) gforth-0.7 “infile-execute”

key などの入力を file-id にリダイレクトして xt を実行します。

infile-id ( – file-id  ) gforth-0.4 “infile-id”

File-id は、 key や、 ?key や、 「ユーザー入力デバイス」(user input device)を参照するものすべてによって使用されます。 デフォルトでは、 infile-execute で変更しない限り、infile-id は実行中のプロセスの stdin を生成します。

あなたが、 入力または出力をファイルにリダイレクトしたくない場合は、 keyemittype が defer されたワードであるという事実を利用することもできます(see Deferred Words)。 ただし、 その場合は、 復元と、 例外からの保護について、 自分で心配する必要があります。 また、 この方法で出力をリダイレクトするには、 emittype の両方をリダイレクトする必要があることに注意してください。


6.18.4 Directories

あなたは、 ファイル名をディレクトリとベース・コンポーネントに分割できます:

basename ( c-addr1 u1 – c-addr2 u2  ) gforth-0.7 “basename”

ファイル名が c-addr1 u1 の場合、 c-addr2 u2 は、 先頭のディレクトリ・コンポーネントが削除された部分です(訳注: "os-class" environment? type unix ok な環境では動いたが、 Windows系では不明(’/’ を区切り文字としてハードコーディングしてあるっぽい)。

dirname ( c-addr1 u1 – c-addr1 u2  ) gforth-0.7 “dirname”

C-addr1 u2 は、 ファイル名 c-addr1 u1 のディレクトリ名部分で、 末尾の / も含まれます。 caddr1 u1/ が含まれていない場合、 u2=0 です(訳注: pathの区切り文字として / をハードコーディングしているので、 Windows系で動くかどうか不明)。

ファイルと同様に、 ディレクトリを開いて読み取ることができます。 読むと、 一度に 1 つのディレクトリ・エントリが得られます。 これを、 (ワイルドカードを使用して)ファイル名とマッチングさせることができます。

open-dir ( c-addr u – wdirid wior ) gforth-0.5 “open-dir”

c-addr, u で指定されたディレクトリを開き、 さらにそこにアクセスするために dir-id を返します。

read-dir ( c-addr u1 wdirid – u2 flag wior ) gforth-0.5 “read-dir”

dir-id で指定されたディレクトリから、 アドレス c-addr にある長さ u1 のバッファーへ、 次のエントリの読み取りを試みます。 これ以上エントリがないために試行が失敗した場合は、 ior=0 かつ flag=0 かつ u2=0 となり、 バッファーは変更されません。 他の理由で次のエントリの読み取りに失敗した場合は、 ior<>0 を返します。 試行が成功した場合、 ファイル名を c-addr からのバッファーに保存し、 ior=0 かつ flag=true かつ ファイル名のサイズに等しい u2 を返します。 ファイル名の長さが u1 より長い場合は、 ファイル名の最初の u1 文字をバッファーに格納し、 ior はエラー "name too long" を示し(訳注: ior= -548 日本語環境では "ファイル名が長すぎます")、 かつ flag=true かつ u2=u1 です。

close-dir ( wdirid – wior ) gforth-0.5 “close-dir”

dir-id で指定されたディレクトリを閉じます。

filename-match ( c-addr1 u1 c-addr2 u2 – flag ) gforth-0.5 “match-file”

ファイル名 c_addr1 u1 とパターン c_addr2 u2 のマッチングを行います。 パターンは、複数の文字 (’*’) または 1 つの文字 (’?’) のワイルドカードである特殊文字 ’*’ と ’?’ を除き、 文字ごとにマッチします。 マッチングできた場合は true

get-dir ( c-addr1 u1 – c-addr2 u2 ) gforth-0.7 “get-dir”

c-addr1, u1で指定されたバッファーにカレント・ディレクトリのパス(path)を格納します。 バッファーサイズが十分でない場合は、 0 0 を返します。

set-dir ( c-addr u – wior ) gforth-0.7 “set-dir”

現在のディレクトリを c-addr, u に変更します。 これが不可能な場合はエラーを返します(訳注: 例えば ior = -514 ; "そのようなファイルやディレクトリはありません")

=mkdir ( c-addr u wmode – wior ) gforth-0.7 “equals-mkdir”

モード wmode でディレクトリ c-addr u を作成します。

mkdir-parents ( c-addr u mode – ior  ) gforth-0.7 “mkdir-parents”

ディレクトリ c-addr u とそのすべての親をモード mode (umask が掛けられます)で作成します(訳注: フルパスで指定したディレクトリを作成する。 aaa/bbb/ccc というディレクトリを指定すればそれを一気に作る。 ここで、 ccc というファイルが既にある場合は何もせず ior=-529 「ファイルが存在します」エラーとなる。 注意: aaa または bbb がファイルの場合は「削除」してからディレクトリを作成するので注意。 ior=-525 なら「許可がありません」(aaa , aaa/bbb , aaa/bbb/ccc のいずれで許可が無かったのかは不明)。 注意: gforth には8進数リテラルが無いので注意。 2進数(%0111111101 ; -rwxrwxr-x )がおすすめ。 ディレクトリの作成であるので実行権限(x)の付与を行うこと)


6.18.5 Search Paths

あなたが、 included や、 そのファミリーのために、 直接ファイル名を指定する場合(つまり、 /~ で始まるファイル名、 または 2 番目の位置に : が付くファイル名 (‘C:...’ など))、 そのファイルは、 あなたの期待どおりにインクルードされます。

ファイル名が ./ で始まる場合、 これは「現在の」ファイルがインクルードされたディレクトリを指します。 これにより、 (現在の作業ディレクトリや絶対位置に関係なく)ファイル自体の位置を基準にして他のファイルを含めることができます。 この機能は、 ファイルにライブラリーの他のファイルが含まれる可能性がある、 複数のファイルで構成されるライブラリーにとって不可欠です。 C言語の #include "..." に相当します。 現在の入力ソースがファイルでない場合、 . はインクルードされた最も内側のファイルのディレクトリを参照します。 インクルードされたファイルがない場合は、 現在の作業ディレクトリからインクルードされます。

(./ で始まらない)相対ファイル名の場合、 Gforth は検索パス(a search path)を使用します。 検索パスに書かれた各ディレクトリ内で、 指定のファイル名を検索し、 最初に見つかったファイル名をインクルードします。 Forth のソース・ファイルと一般ファイルには別個の検索パスがあります。 検索パスにディレクトリ . が含まれている場合、 ファイルが ./ で指定されているかのように、 「現在のファイルのディレクトリ」または作業ディレクトリを参照します。

~+ を使用して、 (bash の Tilde Expansion ~+/foo$PWD/foo のように)現在の作業ディレクトリを参照します(訳注: "~+/my-mkdir-parents.fs" file-status .s <2> 2 0 ok 2 )

absolute-file? ( addr u – flag  ) gforth-1.0 “absolute-file?”

ファイル名が / または ~ (チルダ展開) で始まる場合、 または ./* 拡張正規表現: ^[/~]|./ の形式である場合、 または 2 番目の文字としてコロンが含まれる場合(C:...)、 絶対(absolute)ファイル名です( true を返す)。 パスが / を含んでいるだけでは絶対ファイル名ではありません(訳注: 厳密にファイル名として成立するかどうかチェックしている訳では無い事に注意、 例えば "~ccc.txt" absolute-file? . -1 ok 、また ./ 形式を絶対と見なすことも注意。 "./aaa/bbb/ccc.txt" absolute-file? . -1 ok )


6.18.5.1 Source Search Paths

検索パス(the search path)は、 Gforth の起動時に初期化されます(see Invoking Gforth)。 fpath を一般的なパス処理ワードと組み合わせて使用​​すると、これを表示したり変更したりできます。

fpath ( – path-addr  ) gforth-0.4 “fpath”
.fpath ( ) gforth-0.4 “.fpath”

Forth 検索パスの内容を表示します。

file>fpath ( addr1 u1 – addr2 u2  ) gforth-1.0 “file>fpath”

fpathc-addr1 u1 という名前のファイルを検索します。 成功した場合、 c-addr u2 は絶対ファイル名または現在の作業ディレクトリからの相対ファイル名になります。 ファイルを開けない場合(ファイルが見つからない場合)は例外を throw します。

fpathrequire の使用例を以下に示します:

fpath path= /usr/lib/forth/|./
require timer.fs

6.18.5.2 General Search Paths

あなたのアプリケーションでは、 included のように、 複数のディレクトリ内のファイルを探さなければならない場合があります。 これを容易にするために、 Gforth では、 Forth 検索パスを一般化した等価なモノを提供することで、 あなた独自の検索パスを定義して使用できます。

open-path-file ( addr1 u1 path-addr – wfileid addr2 u2 0 | ior  ) gforth-0.2 “open-path-file”

パス path-addr で、 指定のファイル addr1 u1 を探します。 見つかった場合(ior=0)は、 結果のパス(addr2 u2)と、 (読み取り専用の)ファイル・デスクリプタ(open file descriptor)(wfileid)を返します。 ファイルが見つからない場合、 ior は(現在の実装では)ファイルを開こうとした最後の試行時点で返されたものです(訳注:ファイルが見つからない場合、 つまり ior<>0 の場合、返されるのは ior のみです ( addr1 u1 path-addr – ior ) )。

file>path ( c-addr1 u1 path-addr – c-addr2 u2  ) gforth-1.0 “file>path”

path-addr に保存されたパス内で c-addr1 u1 という名前のファイルを検索します。 成功した場合、c-addr u2 は絶対ファイル名または現在の作業ディレクトリからの相対ファイル名になります。 ファイルを開けない場合は例外をスローします。

clear-path ( path-addr –  ) gforth-0.5 “clear-path”

パス path-addr を空(empty)にします。

also-path ( c-addr len path-addr –  ) gforth-0.4 “also-path”

ディレクトリ c-addr lenpath-addr に追加します。

.path ( path-addr –  ) gforth-0.4 “.path”

検索パス path-addr の内容を表示します。

path+ ( path-addr  "dir" –  ) gforth-0.4 “path+”

ディレクトリ dir を検索パス path-addr に追加します(訳注: 検索パスの後ろに追加する)。

path= ( path-addr "dir1|dir2|dir3" –  ) gforth-0.4 “path-equals”

パス path-addr に、 完全に新しい検索パスを作成します。 パス区切り文字は | です。

カスタム検索パスの作成例を以下に示します:

variable mypath \ no special allocation required, just a variable
mypath path= /lib|/usr/lib \ assign initial directories
mypath path+ /usr/local/lib \ append directory
mypath .path \ output:"/lib /usr/lib /usr/local/lib"

ファイルを検索し、 結果のパスを表示します(訳注: file>path だと内部で throw しちゃうので、代わりに opne-path-file を使った例…だと思う):

s" libm.so" mypath open-path-file throw type close-file \ output:"/lib/libm.so"

6.19 Blocks

今どきのデスクトップ・コンピューターで Gforth を実行すると、 特定のサービスを提供するオペレーティング・システムの制御下で Gforth が実行されます。 これらのサービスの 1 つはファイル・サービスです。 これにより、Forth のソース・コードとデータをファイルに保存し、 Gforth に読み込むことができます(see Files)。

伝統的に、 Forth は、 オペレーティング・システムを介さずに基盤となるハードウェアと直接インターフェイスするシステム上で有用なプログラミング言語でした。 Forth は、 そのようなシステム上で大容量ストレージにアクセスするためのブロック(blocks)と呼ばれるメカニズムを提供します。

ブロックは 1024 バイトのデータ領域であり、 データまたは Forth ソース・コードを保持するために使用できます。 ブロックの内容に構造は課されません。 ブロックはその番号によって識別されます。 ブロックには、1 から実装定義の最大値まで連続した番号が付けられます。

ブロックを使用するが、 オペレーティング・ステムを使用しない一般的なシステムでは、 大容量ストレージとして 1 台のフロッピー・ディスク・ドライブを使用し、 ディスクは 256 バイトのセクターを提供するようにフォーマットされます。 ブロックは、 ディスクの容量の制限まで、 ディスクの最初の 4 セクタをブロック 1 に割り当て、 次の 4 セクタをブロック 2 に割り当てることによって実装されます。 ディスクにはファイル・システム情報は含まれず、 ブロック達のみが含まれます。

ファイル・サービスを提供するシステムでは、 ブロックは通常、単一のブロック・ファイル内に一連のブロックを格納することによって実装されます。 ブロック・ファイルのサイズは、 含まれるブロックの数に対応する 1024 バイトの正確な倍数になります。 これが Gforth が使用するメカニズムです。

一度に開くことができるブロック・ファイルは 1 つだけです。 ブロック・ファイルを指定せずにブロック・ワードを使用すると、 Gforth はデフォルトでブロック・ファイル blocks.fb を使用します。 Gforth は、 ブロック・ファイルを見つけようとするときに Forth 検索パスを使用します(see Source Search Paths)。

プログラム制御下でブロックの読み取りと書き込みを行う場合、 Gforth は中間ストレージとして多数のブロック・バッファー(block buffers)を使用します。 load を使用してブロックの内容を解釈する場合は、 これらのバッファーは使用されません。

ブロック・バッファーの動作はキャッシュ(cache)の動作に似ています。 各ブロック・バッファーには 3 つの状態(state)があります:

  • 未割り当て(unassigned)
  • 割り当て済・未変更(assigned-clean)
  • 割り当て済・変更中(assigned-dirty)

最初は、すべてのブロック・バッファーは「未割り当て」(unassigned)です。 ブロックにアクセスするには、 ブロック(ブロック番号で指定)をブロック・バッファーに割り当てる必要があります。

ブロック・バッファーへのブロックの割り当ては、 block または buffer によって行われます。 ブロックの既存の内容を変更する場合は、 block を使用します。 ブロックの既存の内容を気にしない場合は、 buffer を使用します29

block または buffer を使用してブロックをブロック・バッファーに割り当てると、 そのブロック・バッファーが「現在のブロック・バッファー」(current block buffer)になります。 データは「現在のブロック・バッファー」内でのみ操作(読み取りまたは書き込み)できます。

「現在のブロック・バッファー」の内容が変更されている場合は、 「block または buffer を再度呼び出す前に、」(何もせずに)変更を破棄するか、 update を使用して、 ブロックを変更済みとしてマークする必要があります(割り当て済・変更中;assigned-dirty)。 update を使用してもブロック・ファイルは変更されません。 ブロック・バッファーの状態を「割り当て済・変更中」(assigned-dirty)に変更するだけです。 そのブロックは、 そのブロック・バッファーが別のブロックで必要なときに暗黙的に書き込まれるか、 または、 flushsave-buffers によって明示的に書き込まれます。

ワード flush は、 すべての「割り当て済・変更中」(assigned-dirty)の ブロックをディスク上のブロック・ファイルに書き込みます。 bye を指定して Gforth を終了するときは、 flush も実行されます。

Gforth では、blockbufferdirect-mapped アルゴリズムを使用してブロック・バッファーをブロックに割り当てます。 つまり、 特定のブロックは、 (特定の操作に対して)「いけにえ・バッファー」(victim buffer)と呼ばれる 1 つの特定のブロック・バッファーにのみ割り当てることができます。 いけにえ・バッファーが「未割り当て」(unassigned)状態または「割り当て済・未編集」(assigned-clean)状態の場合、 直ちに新しいブロックが割り当てられます。 「割り当て済・編集中」(assigned-dirty)の場合、 その現在の内容は、 新しいブロックが割り当てられる前に、 ディスク上のブロック・ファイルに書き戻されます。

ブロックの内容に構造は課されていませんが、 伝統的に内容は 1 行当たり 64 文字の 16 行として表示します。 一つのブロックは、 単一の連続した入力ストリームを提供します(たとえば、単一のパース領域として機能します) – ブロック内に行末文字はなく、 ブロックの末尾にファイル終端文字もありません。 これは以下の 2 つの結果をもたらします:

  • ある行の最後の文字は、そのまま次の行の最初の文字に折り返される(訳注: 繋がっています)。
  • \ というワード – 行末までのコメント – は特別な処理を行います。 ブロックでは、 現在の 64 文字の「1行分」の終わりまでのすべての文字が無視されます。

Gforth では、 存在しないブロック番号を指定して block を使用すると、 現在のブロック・ファイルが適切なサイズに拡張され、 ブロック・バッファーがスペースで初期化されます。

Gforth にはシンプルなブロック・エディターが含まれています(詳細については use blocked.fb 0 list と入力してください)。 ただし、ブロックの使用は推奨されません。 このメカニズムは下位互換性のためにのみ提供されています。

ブロックを取り扱うときに使用される一般的な手法は以下のとおりです:

  • Forth 環境を離れずにブロックを編集できるスクリーン・エディター。
  • シャドウ・スクリーン。 ここで、 すべてのコード・ブロックには、 コメントを含むブロックが関連付けられています(例: 奇数のブロック番号のコード、 偶数のブロック番号のコメント)。 通常、 ブロック・エディターは、 コードとコメントを切り替える便利なメカニズムを提供します。
  • ロード・ブロック。 単一のブロック(通常はブロック 1) には、 アプリケーション全体を load する多数の thru コマンドが含まれています。

ブロックが Forth プログラミング環境にどの程度うまく統合できるかを確認するには、 Frank Sergeant の Pygmy Forth を参照してください。

open-blocks ( c-addr u –  ) gforth-0.2 “open-blocks”

c-addr u で指定された名前のファイルをブロック・ファイルとして使用します(訳注: エラーの場合はその時点で throw されます)。

use ( "file" –  ) gforth-0.2 “use”

file をブロック・ファイルとして使用します。

block-offset ( – addr  ) gforth-0.5 “block-offset”

最初のブロックの番号を含むユーザー変数(0.5.0 以降のデフォルト: 0)。 0.5.0 より前のバージョンの Gforth で作成されたブロックファイルのオフセットは 1 からです。 これらのファイルを使用する場合は、次のことができます。 1 offset ! または、 使用されるすべてのブロック番号に 1 を加算します。 または、 ファイルの先頭に 1024 文字追加します。

get-block-fid ( – wfileid  ) gforth-0.2 “get-block-fid”

現在のブロック・ファイルのファイル ID を返します。 まだブロック・ファイルが開かれていない場合は、 blocks.fb をデフォルトのブロック・ファイルとして open-blocks し、 そのファイルID(現在のブロック・ファイルのファイルID)を返します。

block-position ( u –  ) block “block-position”

ブロック・ファイル内のファイル位置をブロック番号 u のブロックの先頭に合わせます。

list ( u –  ) block-ext “list”

ブロック番号 u のブロックの内容を表示します。 Gforth では、ブロックは 1行 64 文字からなる 16 行の行番号付きで表示されます。

scr ( – a-addr  ) block-ext “s-c-r”

このユーザー変数には、 list によって最後に処理されたブロックのブロック番号が含まれます。

block ( u – a-addr  ) block “block”

ブロック番号 u のブロックにブロック・バッファーが既に割り当てられている場合、 そのブロック・バッファーが更新(「割り当て済・変更中」(assigned-dirty)とマークされているか)されているなら、 そのブロック・バッファー内容を当該ブロック(大容量ストレージ)に上書きしてから、 当該ブロックの内容をそのブロック・バッファーに読み込み、 そのブロック・バッファーを「割り当て済・未変更」(assigned-clean)とマークして、 そのブロック・バッファーの開始アドレス a-addr を返します。 それ以外の場合は、 ブロック u にブロック・バッファーを新たに割り当て、 その新たに割り当てたブロック・バッファーの開始アドレス a-addr を返します。 Gforth では、 buffer は単に block を呼び出します。

buffer ( u – a-addr  ) block “buffer”

Gforth では、 buffer は単に block を呼び出します。

empty-buffers ( ) block-ext “empty-buffers”

すべてのブロック・バッファーを「未割り当て」(unassigned)としてマークします。 (update によって)「割り当て済・変更中」(assigned-dirty)としてマークされているブロックがある場合、 それらのブロックへの変更は失われます。

empty-buffer ( buffer –  ) gforth-0.2 “empty-buffer”
update ( ) block “update”

現在のブロック・バッファー(current block buffer)の状態を「割り当て・ダーティ」(assigned-dirty)としてマークします。

updated? ( n – f  ) gforth-0.2 “updated?”

ブロック番号 n のブロックが更新されている(割り当て済・編集中」(assigned-dirty)としてマークされている)なら true を返す。

save-buffers ( ) block “save-buffers”

更新されている(「割り当て済・変更中」(assigned-dirty)とマークされている)各ブロック・バッファーの内容を大容量ストレージに転送(上書き)し、 すべてのブロック・バッファーを「割当済み・未編集」(assigned-clean)としてマークします。

save-buffer ( buffer –  ) gforth-0.2 “save-buffer”
flush ( ) block “flush”

save-buffers の機能を実行してから empty-buffers を実行します。

load ( i*x u – j*x  ) block “load”

ブロック番号 u のブロックをテキスト通訳(Text-interpret)します。 ブロック番号 0 のブロックは load できません。

thru ( i*x n1 n2 – j*x  ) block-ext “thru”

ブロック n1n2 を順番に load します。

+load ( i*x n – j*x  ) gforth-0.2 “+load”

現在のブロック番号に n を足した番号のブロックをロードします。 ブロック内で使います。

+thru ( i*x n1 n2 – j*x  ) gforth-0.2 “+thru”

現在のブロック番号 + n1 の番号 〜 現在のブロック + n2 の番号の、 ブロックの範囲をロードします。 ブロック内で使います。

--> ( ) gforth-0.2 “chain”

ブロック番号 n のブロックのロード中にこのシンボルが見つかった場合、 そのブロックの残りを破棄してブロック n+1 をロードします。 複数のブロックを単一のロード可能なユニットとして連鎖させるために使用されます。 これは ロードの独立性が損なわれるため、 お勧めできません。 代わりに(標準の) thru または +thru を使用してください。

block-included ( a-addr u –  ) gforth-0.2 “block-included”

load によって処理されるブロック内で使用します。 現在のブロック・ファイル仕様を保存し、 a-addr u で指定されたブロック・ファイルを開き、 そのファイルからブロック番号 1 のブロックを load します(これにより、 他のブロックがチェーンまたはロードされる可能性があります)。 そして最後に、 ブロック・ファイルを閉じて、 元のブロック・ファイルを復元します。


6.20 Other I/O


6.20.1 Simple numeric output

最も単純な出力機能は、 データ・スタックからの数値を表示する機能です。 数値は、 base に保存されている基数(別名 radix)で表示されます。

. ( n –  ) core “dot”

(符号付きの1倍長整数である) n を自由形式(free-format)で表示し、 その後に空白1つを続けます。

dec. ( n –  ) gforth-0.2 “dec.”

n を符号付き 10 進数として表示し、 その後に空白1つを続けます。

h. ( u –  ) gforth-1.0 “h.”

u は、 先頭に ‘$‘ を付けた符号なし 16 進数として表示し、 その後に空白1つ続けます。

hex. ( u –  ) gforth-0.2 “hex.”

u を符号なし 16 進数として表示し、 先頭に $ を付け、 その後に空白1つを付けます。 この単語の別名は h. で、 他のいくつかのシステムには存在しますが、 1.0 より前の Gforth には存在しません。

u. ( u –  ) core “u-dot”

(符号なしの1倍長整数の) u を自由形式で表示し、 その後に空白1つを続けます。

.r ( n1 n2 –  ) core-ext “dot-r”

n1n2 文字幅のフィールドに右揃えで表示します。 数値を表示するために n2 を超える文字が必要な場合は、 すべての桁が表示されます。 必要に応じて、 n2 には先頭に ‘-‘ の文字分の幅を含める必要があります。

u.r ( u n –  ) core-ext “u-dot-r”

符号なし整数 un 文字幅のフィールドに右揃えで表示します。 数値を表示するために n 文字を超える文字が必要な場合は、すべての桁が表示されます。

dec.r ( u n –  ) gforth-0.5 “dec.r”

符号なし整数 un 文字幅のフィールドに符号なし 10 進数として表示します。

d. ( d –  ) double “d-dot”

(符号付き2倍長整数の) d を自由形式で表示します。 その後に空白1つが続きます。

ud. ( ud –  ) gforth-0.2 “u-d-dot”

(符号付無し2倍長整数の) ud を自由形式で表示し、 その後に空白1つを続けます。

d.r ( d n –  ) double “d-dot-r”

2倍長整数 dn 文字幅のフィールドに右揃えで表示します。 数値を表示するために n を超える文字が必要な場合は、 すべての桁が表示されます。 必要に応じて、n には先頭に ‘-‘ 文字分の幅を含める必要があります。

ud.r ( ud n –  ) gforth-0.2 “u-d-dot-r”

符号無し2倍長整数 udn 文字幅のフィールドに右揃えで表示します。 数値を表示するために n 文字を超える文字が必要な場合は、すべての桁が表示されます。


6.20.2 Formatted numeric output

Forth は伝統的に、 整数の書式設定された出力に「表示数値出力」(pictured numeric Output) と呼ばれる手法を使用しています。 この手法では、 数値から数字桁(digits)が抽出され(base で定義された現在の出力基数を使用 see Number Conversion)、 ASCII コードに変換され、 メモリーのスクラッチパッド領域(see Implementation-defined options)に構築される文字列の先頭に付加されます。 抽出プロセス中に、 文字列の先頭に任意の文字を追加できます。 完成した文字列はアドレスと長さによって指定され、 プログラム制御の下で操作(TYPE や、 コピーや、 変更)できます。

前のセクションで説明したすべての整数出力ワード (see Simple numeric output) は、 Gforth では表示数値出力(pictured numeric output)を使用して実装されています。

表示数値出力(pictured numeric output)について覚えておくべき 3 つの重要な点:

  • 常に2倍長整数を処理します。 1倍長整数を表示するには、 まず最初に2倍長に変換します (これを行う方法は see Double precision)。
  • 2倍長整数は常に符号無しであるかのように扱われます。 下記の例は、 符号付き数値を出力する方法を示しています。
  • 文字列は右から左に構築されます。 最下位桁が最初です。

標準 Forth は、 <# で空にして初期化し、 #> で結果文字列を取得する単一の出力バッファー(別名ホールド領域;hold area)をサポートします。

Gforth はさらに、 このバッファーの入れ子になった使用をサポートしており、 たとえば、 ホールド領域を処理するコード内でデバッグ・トレーサー ~~ からの出力を入れ子にすることができます。 <<# は新しい入れ子を開始し、 #> は結果文字列を生成し、 #>> は入れ子を解除します。 入れ子のホールド領域が再利用され、#> は次に外側の入れ子の文字列を生成します。 Gforth の高レベルの数値出力ワードはすべて <<# ... #> ... #>> を使用し、 ホールド領域の他のユーザー内に入れ子にできます。

<# ( ) core “less-number-sign”

表示数値出力文字列を 初期化/クリア します(訳注: 表示数値出力用のポインターをホールド領域の末尾(初期値)に戻すだけ。 中身は消さない。)

<<# ( ) gforth-0.5 “less-less-number-sign”

#>> で終わるホールド領域を開始します。 相互に入れ子にすることも、 <# で入れ子にすることもできます。 注: <<##>> を一致させないと、 最終的にホールド領域が不足します。 <# を使用してホールド領域を空にリセットできます。

# ( ud1 – ud2  ) core “number-sign”

<<##> の間で使用されます。 ud1 の最下位桁(base による)を、 表示数値出力文字列の先頭に追加します。 ud2ud1/base、 つまり残りの桁を表す数値です。

#s ( ud – 0 0  ) core “number-sign-s”

<<##> の間で使用されます。 ud のすべての数字を表示数値出力文字列の先頭に追加します。 #s は少なくとも 1 つの数字を変換します。 したがって、 ud が 0 の場合、 #s は表示数値出力文字列の先頭に ‘0‘ を追加します。

hold ( char –  ) core “hold”

<<##> の間で使用されます。 表示数値出力文字列の前に文字 char を追加します。

holds ( addr u –  ) core-ext “holds”

<<##> の間で使用されます。 表示数値出力文字列の前に文字列 addr u を追加します。

sign ( n –  ) core “sign”

<<##> の間で使用されます。 n (1倍長整数) が負の場合、 表示数値出力文字列の先頭に - を追加します。

#> ( xd – addr u  ) core “number-sign-greater”

変換対象(変換残りの)数値 xd を破棄し、 フォーマットされた文字列のアドレスと長さを示す addr u を返すことで、 表示数値出力文字列を完成させます。 標準のプログラムでは、 文字列内の文字を変更する場合があります。 ホールド領域は解放されません。 #>> を使用して <<# で始まるホールド領域を解放するか、 <# を使用してすべてのホールド領域を解放します。

#>> ( ) gforth-0.5 “number-sign-greater-greater”

<<# で始まるホールド領域を解放します。

以下に、 表示数値出力の使用例をいくつか示します:

: my-u. ( u -- )
  \ PNS(Pictured Number String)の最も単純な使用法。標準の u. のように振る舞います。
  0              \ 上記 u を 2倍長にする
  <<#            \ 変換開始
  #s             \ 全桁を変換
  #>             \ 変換終了
  TYPE SPACE     \ 表示、続けて空白1つ
  #>> ;          \ ホールド領域を開放

: cents-only ( u -- )
  0              \ 上記 u を 2倍長整数に変換
  <<#            \ 変換開始
  # #            \ 最下位と最下位からの次の 2 桁のみ変換
  #>             \ 変換完了。他の桁は破棄
  TYPE SPACE     \ 表示、続けて空白1つ
  #>> ;          \ ホールド領域を開放

: dollars-and-cents ( u -- )
  0              \ 上記 u を符号無し2倍長整数に変換
  <<#            \ 変換開始
  # #            \ 下位2桁を変換
  '.' hold       \ 小数点を打つ
  #s             \ 残りの桁を変換
  '$' hold       \ 通貨記号を打つ
  #>             \ 変換完了
  TYPE SPACE     \ 表示、続けて空白1つ
  #>> ;          \ ホールド領域を開放

: my-. ( n -- )
  \ 負数も処理する標準の . のように振る舞う
  s>d            \ 符号付き2倍長整数に変換
  swap over dabs \ 符号バイトを別途保存して数値は符号無し2倍長に
  <<#            \ 変換開始
  #s             \ 全桁を変換
  rot sign       \ 符号チェック。必要なら "-" 付加
  #>             \ 変換完了
  TYPE SPACE     \ 表示、続けて空白1つ
  #>> ;          \ ホールド領域を開放

: account. ( n -- )
  \ (会計風出力)会計士はマイナス記号が嫌いで、
  \ 負の数には括弧を使用します
  s>d            \ 符号付き2倍長整数に変換
  swap over dabs \ 符号バイトを別途保存して数値は符号無し2倍長に
  <<#            \ 変換開始
  2 pick         \ 符号バイトのコピーを得る
  0< IF ')' hold THEN \ これが(あれば)出力の右端の文字
  #s             \ 全桁を変換
  rot            \ 符号バイトを得る
  0< IF '(' hold THEN
  #>             \ 変換完了
  TYPE SPACE     \ 表示、続けて空白1つ
  #>> ;          \ ホールド領域を開放

これらのワードの利用例をいくつか示します:

1 my-u. 1  ok
hex -1 my-u. decimal FFFFFFFFFFFFFFFF  ok
1 cents-only 01  ok
1234 cents-only 34  ok
2 dollars-and-cents  $0.02  ok
1234 dollars-and-cents  $12.34  ok
123 my-. 123  ok
-123 my-. -123  ok
123 account. 123  ok
-456 account. (456)  ok


6.20.3 Floating-point output

浮動小数点数出力は常に基数 10 を使用して表示されます。

f. ( r –  ) floating-ext “f-dot”

(浮動小数点数) r を指数なしで表示し、 その後に空白1つ続けます。

fe. ( r –  ) floating-ext “f-e-dot”

r を工学表記(3 で割り切れる指数) で表示し、 その後に空白1つ続けます。

fs. ( r –  ) floating-ext “f-s-dot”

r を科学表記(指数付き)で表示し、 その後に空白1つ続けます。

fp. ( r –  ) floating-ext “f-p-dot”

r を SI 接頭辞表記(3 で割れる指数を使用し、 可能な場合は SI 接頭辞に変換)で表示し、 その後に空白1つ続けます。

数値 1234.5678E23 をさまざまな浮動小数点数出力形式で出力する例を以下に示します。

1234.5678E23 f. 123456780000000000000000000.  ok
1234.5678E23 fe. 123.456780000000E24  ok
1234.5678E23 fs. 1.23456780000000E26  ok
1234.5678E23 fp. 123.456780000000Y  ok

出力桁幅は以下の影響を受けます:

precision ( – u  ) floating-ext “precision”

u は、 f.fe.fs.fp. で現在使用されている有効桁数です。

set-precision ( u –  ) floating-ext “set-precision”

f.fe.fs.fp. で現在使用されている有効桁数を u に設定します。

以下のコマンドを使用して、 出力をより詳細に制御できます:

f.rdp ( rf +nr +nd +np –  ) gforth-0.6 “f.rdp”

浮動小数点数 rf を書式化して表示します。 出力の合計幅は nr です。 固定小数点表記の場合、 小数点以下の桁数は +nd、 有効桁数の最小値は np です。 Set-precisionf.rdp には影響しません。 固定小数点表記は、 有効桁数が少なくとも np の場合で、 かつ、 小数点以下の桁数が収まる場合に、 使用されます。 固定小数点表記が使用されない場合は指数表記が使用され、 それでも適合しない場合はアスタリスクが出力されます。 数値がまったく適合しないリスクを避けるために、 nr>=7 を使用することをお勧めします。 f.rdp が指数表記に切り替わるケースを避けるために、 nr>=np+5 をお勧めします。 どうしてかというと、 固定小数点表記でも有効桁数が少なすぎるのに、 指数表記では有効桁数が更に少なくなるためです。 一部の数値を固定小数点表記しなければならない場合は、 nr>=nd+2 をお勧めします。 np の値が小さいほど、 より多くの場合で固定小数点表記で表示されます (固定小数点表記に有効数字がほとんどまたはまったく残っていない場合)。 すべての数値を指数表記したい場合は、np>nr をお勧めします。

出力にどのような影響を与えるかをより直感的に理解できるように、 パラメーターの組み合わせの例をいくつか示します。 各行内は同じ数値が出力されていて、 各列には同じパラメーターの組み合わせが出力に使用されています:

    12 13 0    7 3 4   7 3 0   7 3 1   7 5 1   7 7 1   7 0 2  4 2 1
|-1.234568E-6|-1.2E-6| -0.000|-1.2E-6|-1.2E-6|-1.2E-6|-1.2E-6|****|
|-1.234568E-5|-1.2E-5| -0.000|-1.2E-5|-.00001|-1.2E-5|-1.2E-5|****|
|-1.234568E-4|-1.2E-4| -0.000|-1.2E-4|-.00012|-1.2E-4|-1.2E-4|****|
|-1.234568E-3|-1.2E-3| -0.001| -0.001|-.00123|-1.2E-3|-1.2E-3|****|
|-1.234568E-2|-1.2E-2| -0.012| -0.012|-.01235|-1.2E-2|-1.2E-2|-.01|
|-1.234568E-1|-1.2E-1| -0.123| -0.123|-.12346|-1.2E-1|-1.2E-1|-.12|
|-1.2345679E0| -1.235| -1.235| -1.235|-1.23E0|-1.23E0|-1.23E0|-1E0|
|-1.2345679E1|-12.346|-12.346|-12.346|-1.23E1|-1.23E1|   -12.|-1E1|
|-1.2345679E2|-1.23E2|-1.23E2|-1.23E2|-1.23E2|-1.23E2|  -123.|-1E2|
|-1.2345679E3|-1.23E3|-1.23E3|-1.23E3|-1.23E3|-1.23E3| -1235.|-1E3|
|-1.2345679E4|-1.23E4|-1.23E4|-1.23E4|-1.23E4|-1.23E4|-12346.|-1E4|
|-1.2345679E5|-1.23E5|-1.23E5|-1.23E5|-1.23E5|-1.23E5|-1.23E5|-1E5|

以下を使用して、 数値を表示する代わりに文字列を生成できます:

f>str-rdp ( rf +nr +nd +np – c-addr nr  ) gforth-0.6 “f>str-rdp”

rfc-addr nr の文字列に変換します。 nr +nd np の変換規則と意味は f.rdp と同じです。 結果は表示数値出力(pictured numeric output)バッファーに格納され、 そのバッファを破壊するものによって破壊されます(訳注: このバッファーは、 他の 表示数値出力(pictured numeric output) と共用であるということ)。

f>buf-rdp ( rf c-addr +nr +nd +np –  ) gforth-0.6 “f>buf-rdp”

rfc-addr nr の文字列に変換します。 nr nd np の変換規則と意味は f.rdp と同じです。

以下のような、 高レベルの FP-to-string ワードを実装するために使用されるプリミティブもあります:

represent ( r c-addr u – n f1 f2 ) floating “represent”

r の 10 進仮数部(別名 mantissa)をバッファ c-addr u 内の文字列に変換します。 n は指数で、r が負の場合は f1 が true、 r が有効(Gforth の有限数)の場合は f2 が true です(訳注: 仮数部が u の桁数になるようそれ以下を四捨五入するっぽい)。


6.20.4 Miscellaneous output

cr ( ) core “c-r”

(ホスト OS の好みの種類の)改行(newline)を出力します。 注意: Forth コマンド・ライン・インタープリターの改行(newline)挿入のクセのため、 cr をテキストの先頭で使用することをお勧めします。 例: cr ." hello, world".

space ( ) core “space”

空白を1つ表示する。

spaces ( u –  ) core “spaces”

u 個の空白を表示します。

out ( – addr  ) gforth-1.0 “out”

addr には、 ユーザー出力デバイス上の現在行内のカーソルの位置を指定しようと試みる数値が含まれています。 cr で 0 にリセットされ、 type によって文字数ずつ増加、 emit で増加、 backspaces で減少します。 残念ながら、 タブ文字や、マルチバイト文字や、 幅 0 と 幅2 のユニコード文字の存在は考慮されていないため、 単純な場合にのみ機能します。

.\" ( compilation ’ccc"’ – ; run-time –  ) gforth-0.6 “dot-backslash-quote”

." と似ていますが、 C言語のようなバックスラッシュによるエスケープ・シーケンス(\-escape-sequences)を認識します(詳しくは S\" 参照)。

." ( compilation ’ccc"’ – ; run-time –  ) core “dot-quote”

コンパイル時: ‘"‘ (二重引用符)で区切られた文字列 ccc をパースします。 実行時、 その文字列を表示します。 このワードのインタープリター機能(interpretation semantics)は、 標準 Forth では定義されていません。 Gforth でのインタープリター機能(interpretation semantics)は、 その文字列を表示することです。

.( ( compilation&interpretation "ccc<paren>" –  ) core-ext “dot-paren”

コンパイル時(compilation semantics)とインタープリター時(interpretation semantics): )(右括弧)で区切られた文字列 ccc をパースします。 その文字列を表示します。 これは、 コンパイル中に進行状況情報を表示するためによく使用されます。 下記の例を参照してください。

あなたが .( hello) を使用するべきか ." hello" を使用するべきかを気に掛けたくない場合は、 "hello" type と書くことができます。 これにより、 通常必要なものが得られます(ただし、他の Forth システムへの移植性は低くなります)。

例として、 ファイル test.fs に保存されている以下のテキストについて考えてみましょう:

.( text-1)
: my-word
  ." text-2" cr
  .( text-3)
  "text-4" type
;

." text-5"
"text-6" type

このコードを Gforth にロードすると、 以下の出力が生成されます:

include test.fs RET text-1text-3text-5text-6 ok
  • .( は即実行ワードであるため、 メッセージ text-1text-3 が表示されます。 コロン定義の内側でも外側でも、 全く同じに振る舞います。
  • ." に対する Gforth の追加インタープリター機能(interpretation semantics)により、 メッセージ text-5 が表示されます。
  • "text-6" type が通訳(interpret)され、 メッセージ text-6 が表示されます。
  • テキスト・インタープリターが my-word の定義内で ." のコンパイル機能(compilation semantics)を実行するため、 メッセージ text-2 は「表示されません」。
  • "text-4" typemy-word 内にコンパイルされるため、 メッセージ text-4 は「表示されません」。

6.20.5 Displaying characters and strings

type ( c-addr u –  ) core “type”

u>0 なら、 c-addr からに格納されている文字列から u 文字を表示します(訳注: 日本語(UTF-8)の出力も対応)。

xemit ( xc –  ) xchar “x-emit”

端末に xchar を出力します。

emit ( c –  ) core “emit”

バイト値 c を現在の出力に送信します。 ASCII 文字の場合、 emitxemit と同等です。

typewhite ( addr n –  ) gforth-0.2 “typewhite”

type と似ていますが、 文字の代わりに空白が表示されます(訳注: 指定の文字数 n だけ空白出すだけなので日本語(UTF-8)だと表示幅がズレる)。


6.20.6 Terminal output

端末に出力している場合、 カーソル位置を制御することができます:

at-xy ( x y –  ) facility “at-x-y”

カーソルを位置 x y に置きます。 ディスプレイの左上角は 0 0 です。

at-deltaxy ( dx dy –  ) gforth-0.7 “at-deltaxy”

現在の位置を x y として、 カーソルを x+dx y+dy に置きます。

カーソルをどこに置くかを知るには、 以下のように、 画面(screen)のサイズを知っておくと役立つことがよくあります:

form ( – nlines ncols  ) gforth-0.2 “form”
\ 訳注:画面のサイズを得る

また、 以下のようなものを使用したい場合もあります:

page ( ) facility “page”

画面(screen)をクリアする

注意: ターミナル以外では、フォーム・フィード(form feed;FF)を取得するには page ではなく 12 Emit を使用する必要があることに注意してください。

6.20.6.1 Color output

以下のワード群は、 意味・理由ごとに色を変えるために使用されます。 更に細かい設定は、 ワードによって指定された色とスタイルで生成されます。 実際の色とスタイルはテーマによって異なります(下記を参照)。

default-color ( ) gforth-1.0 “default-color”

システムのデフォルト用の色

error-color ( ) gforth-1.0 “error-color”

エラー用の色: (通常は)赤

error-hl-inv ( ) gforth-1.0 “error-hl-inv”

エラー用にハイライトとして反転表示するカラーモード

error-hl-ul ( ) gforth-1.0 “error-hl-ul”

エラー用にアンダーラインでハイライトする色変更モード

warning-color ( ) gforth-1.0 “warning-color”

警告(warning)の色: 背景が黒の端末では 青/黄

info-color ( ) gforth-1.0 “info-color”

情報(info)用の色: 黒色の背景の端末では 緑/シアン

success-color ( ) gforth-1.0 “success-color”

成功(success)の色: 緑

input-color ( ) gforth-1.0 “input-color”

ユーザー入力の色: 黒/白 (両方ともボールド)

status-color ( ) gforth-1.0 “status-color”

エラー用にハイライトとして反転表示するカラーモード

6.20.6.2 Color themes

あなたが、 明るい背景を好むか暗い背景を好むかに応じて、 前景の色テーマ(foreground colors-theme)を以下のように変更できます:

light-mode ( ) gforth-1.0 “light-mode”

白背景用の色テーマ

dark-mode ( ) gforth-1.0 “dark-mode”

黒背景用の色テーマ

uncolored-mode ( ) gforth-1.0 “uncolored-mode”

このモードでは色は設定されませんが、 デフォルトの色が使用されます。

magenta-input ( ) gforth-1.0 “magenta-input”

入力色(input color)を認識しやすくします(プレゼンテーションに役立ちます)


6.20.7 Single-key input

単一の印刷可能な文字を取得したい場合は、 key を使用できます。 文字が key に使用できるかどうかを確認するには、 key? を使用できます。

key ( – char  ) core “key”

1 文字 char を受け取ります(ただし表示はされません)。

key-ior ( – char|ior  ) gforth-1.0 “key-ior”

1 文字 char を受け取ります (ただし表示はされません)。 エラーまたは割り込みの場合は、 代わりに、 負数の ior を返します。

key? ( – flag  ) facility “key-question”

文字が key で使用可能かどうかを判断します。 文字が使用可能な場合、flag は true です。 次に key を呼び出すと、 文字が生成されます。 一度 key? が true を返すと、 その後に key または ekey を呼び出す前に key? を呼び出した場合も true が返されます。

xkey? ( – flag  ) xchar “x-key-query”
\ 訳注: UTF-8 な環境では key? は xkey? のエイリアスです。
\ xchar 文字が xkey で使用可能かどうかを判断します(たぶん) 

印刷可能な文字と印刷不可能な文字を組み合わせて処理したい場合は、ekey とそのファミリーを使用して実行できます。 ekey は、 ekey>char で文字に変換するか、 ekey>fkey でキー識別子に変換する必要があるキーボード・イベントを生成します。

ekey を使用するための一般的なコードは以下のようになります:

ekey ekey>xchar if ( xc )
  ... \ do something with the character
else ekey>fkey if ( key-id )
  case
    k-up                                  of ... endof
    k-f1                                  of ... endof
    k-left k-shift-mask or k-ctrl-mask or of ... endof
    ...
  endcase
else ( keyboard-event )
  drop \ just ignore an unknown keyboard event type
then then
ekey ( – u  ) facility-ext “e-key”

キーボード・イベント u を受け取りす(実装定義のエンコーディングです) 。

ekey>xchar ( u – u false | xc true  ) xchar-ext “e-key-to-x-char”

可能であれば、 キーボード・イベント u を xchar xc に変換します。

ekey>char ( u – u false | c true  ) facility-ext “e-key-to-char”

可能であれば、 キーボード・イベント u を文字 c に変換します。 注意: 非 ASCII 文字は、 ekey>charekey>fkey の両方から false が返ることに注意してください。 利用可能な場合は、 ekey>char の代わりに ekey>xchar を使用します。

ekey>fkey ( u1 – u2 f  ) facility-ext “e-key-to-f-key”

u1 が特殊キー(special key)セット内のキーボード・イベントの場合、 キーボード・イベント u1 をキー ID u2 に変換し、 true を返します。それ以外の場合は、 u1 と false を返します。

ekey? ( – flag  ) facility-ext “e-key-question”

キーボード・イベントが利用可能な場合は True。

カーソル・キーのキー識別子は以下のとおりです:

k-left ( – u  ) facility-ext “k-left”
k-right ( – u  ) facility-ext “k-right”
k-up ( – u  ) facility-ext “k-up”
k-down ( – u  ) facility-ext “k-down”
k-home ( – u  ) facility-ext “k-home”

別名 Pos1

k-end ( – u  ) facility-ext “k-end”
k-prior ( – u  ) facility-ext “k-prior”

別名 PgUp

k-next ( – u  ) facility-ext “k-next”

別名 PgDn

k-insert ( – u  ) facility-ext “k-insert”
k-delete ( – u  ) facility-ext “k-delete”

著者の xterm 上ではDEL キーで、 Backspace ではありません

ファンクション・キー(別名キーパッド・キー)のキー識別子は以下のとおりです:

k-f1 ( – u  ) facility-ext “k-f-1”
k-f2 ( – u  ) facility-ext “k-f-2”
k-f3 ( – u  ) facility-ext “k-f-3”
k-f4 ( – u  ) facility-ext “k-f-4”
k-f5 ( – u  ) facility-ext “k-f-5”
k-f6 ( – u  ) facility-ext “k-f-6”
k-f7 ( – u  ) facility-ext “k-f-7”
k-f8 ( – u  ) facility-ext “k-f-8”
k-f9 ( – u  ) facility-ext “k-f-9”
k-f10 ( – u  ) facility-ext “k-f-10”
k-f11 ( – u  ) facility-ext “k-f-11”
k-f12 ( – u  ) facility-ext “k-f-12”

k-f11k-f12 はそれほど広くには利用可能ではないことに注意してください。

これらのキー識別子をさまざまなシフト・キーのマスクと組み合わせることができます:

k-shift-mask ( – u  ) facility-ext “k-shift-mask”
k-ctrl-mask ( – u  ) facility-ext “k-ctrl-mask”
k-alt-mask ( – u  ) facility-ext “k-alt-mask”

ASCII 値を持つキーが多数あるため、 特殊キーとして報告される可能性は低いですが、 以下のキーとシフト・キーの組み合わせは特殊キーとして報告される可能性があります:

k-enter ( – u  ) gforth-1.0 “k-enter”
k-backspace ( – u  ) gforth-1.0 “k-backspace”
k-tab ( – u  ) gforth-1.0 “k-tab”

さらに、 キーおよびその他のイベントには以下のキー・コードがあります:

k-winch ( – u  ) gforth-1.0 “k-winch”

ユーザーがウィンドウ・サイズを変更したときに生成される可能性のあるキー・コード。

k-pause ( – u  ) gforth-1.0 “k-pause”
k-mute ( – u  ) gforth-1.0 “k-mute”
k-volup ( – u  ) gforth-1.0 “k-volup”
k-voldown ( – u  ) gforth-1.0 “k-voldown”
k-sel ( – u  ) gforth-1.0 “k-sel”

Androidでの選択(selections)のキー・コード

k-eof ( – u  ) gforth-1.0 “k-eof”

注意: Forth システムに ekey>fkey とキー識別子のワードがある場合でも、 そのキーが必ずしも利用可能であるとは限らず、 すべてのキーとシフト・マスクとの可能なすべての組み合わせを報告できるとは限らないことに注意してください。 したがって、キーやキーの組み合わせが押せない場合や認識されない場合でも、プログラムが使えるようにプログラムを作成してください。

例: 古いキーボードには F11 キーと F12 キーがないことがよくあります。 xterm で Gforth を実行すると、xterm は多数の組み合わせ(例: Shift-Up)を捕捉しますが、それを Gforth に渡すことはありません。 最後に、Gforth は現在、 複数のシフト・キーの組み合わせを認識して報告しません(そのため、上記の例の shift-ctrl-left のケースは決して入力されません)。

Gforth は、ANSI 端末で利用可能なさまざまなキーを認識します(MS-DOS では、 その動作を実現するには ANSI.SYS ドライバーが必要です)。 これは、 そのようなキーが押されたときに ANSI 端末が送信するエスケープ・シーケンスを認識することによって機能します。 他のエスケープ・シーケンスを送信する端末を使用している場合、 Gforth では有益な結果は得られません。 他の Forth システムは異なる方法で動作する可能性があります。

Gforth には、 ファンクション・キーの名前を出力するためのいくつかのワードも用意されています:

fkey. ( u –  ) gforth-1.0 “fkey-dot”

ファンクション・キー u の文字列表現を出力します。 U はファンクション・キー(おそらく修飾子マスク付き)でなければなりません。 そうでない場合は例外が発生する可能性があります。

simple-fkey-string ( u1 – c-addr u  ) gforth-1.0 “simple-fkey-string”

c-addr u は、ファンクション・キー u1 の文字列名です。 修飾子マスクのない単純なファンクション・キーに対してのみ機能します。 現在、 単純なファンクション・キーでない u1 では例外を生成します。


6.20.8 Line input and conversion

文字列をメモリーに保存する方法については、 String representations を参照してください。

キーボードから一行入力するためのワード群:

accept ( c-addr +n1 – +n2  ) core “accept”

ユーザー入力デバイスから最大 n1 文字の文字列を取得し、 c-addr に保存します。 n2 は受け取った文字列の長さです。 ユーザーは RET を押して終了を指示します。 Gforth は、 accept で、 Forth コマンド・ラインで利用できるすべての編集機能(履歴やワード補完を含む)をサポートしています。

edit-line ( c-addr n1 n2 – n3  ) gforth-0.6 “edit-line”

accept のように振る舞いますが、バッファ c-addr n1 の内容を先頭から長さ n2 まで引用します( n2=0 の場合、 全く引用せず、 空の状態から入力編集開始になります)。 受け取った文字列の長さを n3 に返します。

変換ワード群:

s>number? ( addr u – d f  ) gforth-0.5 “s>number?”

文字列 addr u を 符号付き2倍長整数 d に変換し、 フラグ f が true ならば成功、 false ならば変換失敗です( d の値は保証されません) (訳注: フラグが true の場合でも -15. 基数プレフィックスを付けずに記述した場合、 警告が出力されます。 警告が出ないようにするには基数プレフィックスを付けて下さい #-15.)

s>unumber? ( c-addr u – ud flag  ) gforth-0.5 “s>unumber?”

文字列 c-addr u を 符号なし2倍長整数 ud に変換します。 フラグ f が true ならば成功、 false ならば変換失敗です( d の値は保証されません)。 (訳注: フラグが true の場合でも 15. 基数プレフィックスを付けずに記述した場合、 警告が出力されます。 警告が出ないようにするには基数プレフィックスを付けて下さい #15. 、 注意: 負数も受け付けます; "#-15." s>unumber? .s <3> -15 -1 -1 ok drop -15 -1 ud. 340282366920938463463374607431768211441 ok "#340282366920938463463374607431768211441." s>unumber? .s <3> -15 -1 -1 ok 3 )

>number ( ud1 c-addr1 u1 – ud2 c-addr2 u2  ) core “to-number”

文字列 c-addr1 u1 を現在の基数で符号なし2倍長整数に変換しようと試みます。 符号なし2倍長整数 ud1 に変換結果を積算して ud2 にします。 変換は、 文字列全体が変換されるか、 現在の基数で変換できない文字( ‘+‘ または ‘-‘ を含む)が検出されるまで、 左から右に続行されます。 変換可能な各文字ごとに、 ud1 * base してから、 次にその文字によって表される値を足しこみます( new ud1 = ud1 * base + digit )。 c-addr2 は、 最初の未変換文字の位置です(文字列全体が変換された場合は文字列の末尾以降)。 u2 は、 文字列内の未変換の文字の数です。 最後まで変換出来たときは 0 です。 オーバーフローは検出されません。

>float ( c-addr u – f:... flag ) floating “to-float”

実際のスタック効果: ( c_addr u – r t | f )。 文字列 c-addr u を内部浮動小数点表現に変換しようとします。 文字列が有効な浮動小数点数を表す場合、 r が浮動小数点スタックに配置され、 flag が true になります。 それ以外の場合、flag は false になります(訳注: この場合、 浮動小数点数スタックには何も積まれない)。 空白の文字列は特殊なケースであり、 浮動小数点数 0 を表します。

>float1 ( c-addr u c – f:... flag ) gforth-1.0 “to-float1”

実際のスタック効果: ( c_addr u c – r t | f ) c を小数点として使用して(訳注: ’.’ が小数点とは限らない。ロケールにより異なるため、指定できるようになっている)、 文字列 c-addr u を内部浮動小数点表現に変換しようと試みます。 文字列が有効な浮動小数点数を表す場合、 r が浮動小数点スタックに配置され、 flag が true になります。 それ以外の場合、flag は false になります(この場合、浮動小数点数スタックには何も積まれません)。 空白の文字列は特殊なケースであり、浮動小数点数 0 を表します。

時代遅れ(OBSOLESCENT)の入力ワード群と変換ワード群:

convert ( ud1 c-addr1 – ud2 c-addr2  ) core-ext-obsolescent “convert”

時代遅れ(OBSOLESCENT): >number に置き換えられました。

expect ( c-addr +n –  ) core-ext-obsolescent “expect”

最大 +n 文字の文字列を受け取り、c-addr から始まるメモリーに保存します。 文字列は表示されます。 <return>キーを押すか、 +n 文字を受け取ると入力が終了します。 通常の Gforth 行編集機能が利用可能です。 文字列の長さは span に保存されますが、 <return> 文字は含まれません。 時代遅れ(OBSOLESCENT): accept に置き換えられました。

span ( – c-addr  ) core-ext-obsolescent “span”

変数(variable) – c-addr は、 expect によって最後に受け取った文字列の長さを格納するセルのアドレスです。 時代遅れ(OBSOLESCENT)。


6.20.9 Pipes

他のプロセスによって作成されたパイプライン(see Gforth in pipes)を Gforth で使用することに加えて、 あなたは open-pipe を使用してあなた独自のパイプラインを作成し、 読み書きすることができます。

open-pipe ( c-addr u wfam – wfileid wior ) gforth-0.2 “open-pipe”
close-pipe ( wfileid – wretval wior ) gforth-0.2 “close-pipe”

パイプラインに書き込む場合、 Gforth は broken-pipe-error を throw する可能性があります。 あなたが、 この例外をキャッチしない場合、 Gforth は例外をキャッチして通常は黙って(silently)に終了(exit)します(see Gforth in pipes)。 しかし、 おそらくこれはあなたの望む事ではないので、 open-pipe から close-pipe までのコードを catch または try ブロックで囲むべきです。 そして、 あなた自身で問題を解決し、 通常の処理に戻します。

broken-pipe-error ( – n  ) gforth-0.6 “broken-pipe-error”

壊れたパイプラインのエラー番号


6.20.10 Xchars and Unicode

ASCII は英語(English language)にのみ適しています。 ただし、 ほとんどの西洋言語(western languages)は、それぞれの少数の特殊文字をエンコードするには 1 バイトで十分であるため、 Forth の枠組みにある程度適合します(ただし、常に同じエンコードを使用できるとは限りません。 ただし、 latin-1 が最も広く使用されています)。 他の言語の場合は、 異なる文字セットを使用する必要があり、その一部は可変幅です。 この問題に対処するために、 文字はスタック上ではユニコード・コードポイント(Unicode codepoints)として表され、 メモリー内では UTF-8 バイト文字列として表されることがよくあります。 ユニコード・コードポイントは、 多くの場合、 1 つのアプリケーション・ レベル(one application-level character)の文字を表しますが、 ユニコードは、基本文字(base letter)と結合発音記号(combining diacritical mark)など、 複数のコード・ポイントで構成される分解文字(combining diacritical mark)もサポートします。

ユニコード・コードポイントはメモリー内の複数バイトを消費する可能性があるため、 ここで我々の用語をすり合わせしておきましょう: char はメモリー内の生のバイト、 またはスタック上の 0 ~ 255 の範囲の値です。 xchar (extended char) は 1 つのコードポイントを表します。 これはメモリー内の 1 バイト以上で表され、 スタック上にはより大きな値が存在する可能性があります。 ASCII 文字は char や xchar と同一です。 つまり、0 ~ 127 の範囲の値、 かつ、 メモリー内のその値を含む 1 バイトです。

UTF-8 エンコードを使用する場合、 他のすべてのコードポイントは 1 文字あたり 1 バイト以上必要になります。 ほとんどの場合、 このような文字はメモリー内の文字列として扱うだけでよく、 以下のワード群を使用する必要はありませんが、 個々のコードポイントを処理したい場合は、 以下のワード群が役に立ちます。 現時点では、 分解文字(decomposed characters)を扱うためのワードはありません。

xchar ワード群はいくつかのデータ型を追加します:

  • xc はスタック上の拡張文字(xchar)です。 これは 1 つのセルを占有し、 符号なしセルのサブセットです。 16 ビット・システムでは、 ユニコード文字セットの BMP サブセット (つまり、 コードポイント<65536)のみがスタック上で表現できます。 アプリケーション文字(application characters)を常に文字列として表現すると、 この制限を回避できます。
  • xc-addr はメモリー内の xchar のアドレスです。 アライメント要件は c-addr と同じです。 xchar のメモリー表現はスタック表現とは異なり、 使用されるエンコーディングによって異なります。 xchar はメモリー内で可変数の char を使用できます。
  • xc-addr u はメモリー内の xchar のバッファで、xc-addr で始まり u char (つまり、 xchar ではなくバイト)の長さになります。
xc-size ( xc – u  ) xchar “x-c-size”

xchar xc のメモリー・サイズを char で計算します。

x-size ( xc-addr u1 – u2  ) xchar “x-size”

xc-addr に格納されている最初の xchar のメモリー・サイズを char で計算します。

xc@ ( xc-addr – xc  ) xchar-ext “xc-fetch”

xc-addr1 から xchar xc を取得します。

xc@+ ( xc-addr1 – xc-addr2 xc  ) xchar “x-c-fetch-plus”

xc-addr1 から xchar xc を取得します。 xc-addr2 は、 xc の後ろの最初のメモリー位置を指します。

xc@+? ( xc-addr1 u1 – xc-addr2 u2 xc  ) gforth-experimental “x-c-fetch-plus-query”

文字列 xc-addr1 u1 の最初の xchar xc を取得します。 xc-addr2 u2xc の後ろの残りの文字列です。

xc!+? ( xc xc-addr1 u1 – xc-addr2 u2 f  ) xchar “x-c-store-plus-query”

xchar xc を、 アドレス xc-addr1 で始まり u1 文字分の大きさであるバッファーに格納します。 xc-addr2xc の後の最初のメモリー位置を指し、 u2 はバッファの残りのサイズです。 xchar xc がバッファーに収まった場合、 f は true、 それ以外の場合は f は false で xc-addr2 u2xc-addr1 u1 と等しくなります。 XC!+? はバッファー・オーバーフローに対して安全であるため、 XC!+ よりも推奨されます。

xc!+ ( xc xc-addr1 – xc-addr2  ) xchar “x-c-store”

xchar xcxc-addr1 に保存します。 xc-addr2 は、 バッファ内の次の未使用アドレスです。 これは最大 4 バイトを書き込むため、 アドレスをバッファの末尾と照合するだけの場合は、 有用なデータの上書きを避けるために、 バッファの末尾の後に少なくとも 3 バイトの余裕(パディング)が必要であることに注意してください。

xchar+ ( xc-addr1 – xc-addr2  ) xchar “x-char-plus”

xc-addr1 に格納されている xchar のサイズをこのアドレスに加算し、 xc-addr2 を与えます(訳注: つまり、 次の xchar 文字の位置を返す)

xchar- ( xc-addr1 – xc-addr2  ) xchar-ext “x-char-minus”

xc-addr1 から xchar が見つかるまで逆方向に進み、 この xchar のサイズを xc-addr2 に加算すると xc-addr1 になります。

+x/string ( xc-addr1 u1 – xc-addr2 u2  ) xchar-ext “plus-x-slash-string”

アドレス xc-addr1 サイズ u1 文字で定義されたバッファーで xchar 1つ分だけ進めたアドレスを xc-addr2 に返します。 u2 は xchar 分だけ u1 より減ります(残り文字列長さです)。 u2 が 0 になったら末尾まで到達しています。 注意: 0 になってもそこで止まりません更に進んでしまうので注意(長さが負数になる)

x\string- ( xc-addr u1 – xc-addr u2  ) xchar-ext “x-backslash-string-minus”

バッファの最後から開始して、 アドレス xc-addr とサイズ u1 (char単位) で定義されたバッファー内で 1 xchar ずつ後方に進みます。 xc-addr は変わらず、 u2 は xchar 分ずつ短くなります。 注意: 0 になっても止まりません。そのまま長さが負数になっていきます。

-trailing-garbage ( xc-addr u1 – xc-addr u2  ) xchar-ext “minus-trailing-garbage”

xc-addr u1 というバッファー内の最後の XCHAR を調べます — エンコードが正しく、 完全な文字を表す場合 u2u1 と等しいです。 それ以外の場合は u2 は、 最後の(文字化けした) xchar を除いた文字列を表します。

x-width ( xc-addr u – n  ) xchar-ext “x-width”

アドレス xc-addr 長さ u (char単位)の文字列に対して、同じ幅の等幅 ASCII 文字での文字数を n に得ます。 等幅フォントを想定しています。 つまり文字の幅はいずれも ASCII 文字の幅の整数倍であると仮定します(訳注: 含まれる xchar ごとにその表示幅を調べたのを積算して返します buf $@ type abcあdef ok buf $@len . 9 ok buf $@ x-width . 8 ok)

xkey ( – xc  ) xchar “x-key”

端末から xchar を 1 つ読み取ります。 これにより、xchar の読み取りが完了するまでのすべての入力イベントが破棄されます(訳注: ヒストリ操作は効いたので…全ての入力イベント…?)。

xc-width ( xc – n  ) xchar-ext “x-c-width”

xc の幅は、通常の固定幅グリフ(fixed-width glyph)の幅の n 倍です。

xhold ( xc –  ) xchar-ext “x-hold”

<<##> の間で使用されます。 表示数値出力文字列(pictured numeric output string)の前に xc を追加します。 代替手段としては holds を使用することができます。

xc, ( xchar –  ) xchar “x-c-comma”

6.20.11 国際化(I18n)とローカライズ

エンド・ユーザー向けのプログラムは、 エンド・ユーザーの母国語に対応する必要があります。 このような機能については古くから提案があり、 Xchars (see Xchars and Unicode)や Substitute (see Substitute) などの国際文字セットに関する他の提案から分割されました。 画面に表示されるメッセージは、 開発者の母国語(native language)からユーザーの現地言語(local languages)に翻訳する必要があります(訳注: gforth に最初から組み込まれていないので都度 require i18n.fs 等する必要ある。 本マニュアルに掲載が無いが i18n な 日付用として require i18n-date.fs するのもいいかもしれない)。

翻訳対象の文字列は L" string" で宣言します。 これにより、 ロケール文字列識別子(LSID)が返されます。 LSID は不明瞭なタイプ(opaque types)であり、 スタック上のセルとして扱われます。 LSID はロケールに変換できます。 ロケールは、 言語およびその言語の国固有のバリエーションです。

L" ( "lsid<">" – lsid  ) gforth-experimental “l-quote”

文字列が一意に新しい場合は、 文字列をパースし、 新しい lsid を定義します。 同一文字列は同一の lsid となり、 同一文字列を使用して複数の場所から同じ lsid を参照できます。

LU" ( "lsid<">" – lsid  ) gforth-experimental “l-unique-quote”

文字列がユニークでない場合でも(重複してても)、 文字列をパースし、 常に新しい lsid を定義します。

native@ ( lsid – addr u  ) gforth-experimental “native-fetch”

lsid からネイティブ文字列(native string)を取得します

locale@ ( lsid – addr u  ) gforth-experimental “locale-fetch”

現在の言語 かつ 現在の国(country)でローカライズされた文字列を取得します

locale! ( addr u lsid –  ) gforth-experimental “locale-store”

現在のロケール かつ 現在の国の、 ローカライズされた文字列 addr ulsid に保存します。

Language ( "name" –  ) gforth-experimental “Language”

ロケールを定義します。 そのロケールを実行すると、 それが現在のロケールになります。

Country ( <lang> "name" –  ) gforth-experimental “Country”

現在のロケールのバリエーション(通常: 国;country)を定義します。 そのロケールを実行すると、それが現在のロケールになります。 バリエーションのバリエーションを作成できます(国によってはバリエーションが存在する場合があります。たとえば、 多くの言語で rolls/buns を表す単語が何語あるかを考えてください)。

locale-file ( fid –  ) gforth-experimental “locale-file”

fid から現在のロケールに行(lines)を読み取ります。

included-locale ( addr u –  ) gforth-experimental “included-locale”

ファイル addr u から現在のロケールに行(lines)を読み取ります。

include-locale ( "name" –  ) gforth-experimental “include-locale”

指定のファイル "name" から現在のロケールに行(lines)を読み取ります。

locale-csv ( "name" –  ) gforth-experimental “locale-csv”

カンマ区切り値テーブルをロケールにインポートします。 最初の行にはロケール名が含まれます。 “program” と “default” は特別なエントリです。 generic languages は特定の国(specific countries)向けの翻訳よりも優先しなければなりません。 “program” の下のエントリ(一番左にある必要があります) は、 lsid の検索に使用されます。 空の場合、 行番号 1 は lsid インデックスです。

.locale-csv ( ) gforth-experimental “dot-locale-csv”

ロケール・データベースを CSV 形式で端末に出力します。

locale-csv-out ( "name" –  ) gforth-experimental “locale-csv”

ファイル "name" を作成し、 ロケール・データベースを CSV 形式でファイル "name" に書き込みます。


6.20.12 Substitute

これは単純なテキスト・マクロ置換機能です。 "text %macro% text" の形式のテキストが処理され、'%' で囲まれたマクロ変数が関連する文字列に置き換えられます。 2 つの連続する % は 1 つの % に置き換えられます。 マクロは特定のワードリストで定義され、実行時に文字列を返します。 標準では、 マクロを宣言する方法が 1 つだけ、 replaces のみ定義されています。 これは文字列を返すだけのマクロを作成します。

macros-wordlist ( – wid  ) gforth-experimental “macros-wordlist”

文字列置換マクロのワードリスト

replaces ( addr1 len1 addr2 len2 –  ) string-ext “replaces”

名前が addr2 len2、 内容が addr1 len1 のマクロを作成します。 マクロが既に存在する場合は、 内容を変更するだけです。

.substitute ( addr1 len1 – n / ior  ) gforth-experimental “dot-substitute”

テキスト addr1 len1 内のすべてのマクロを置き換えて、 結果を出力します。 n は置換した数。 または負数の場合は throw 可能な ior です。

$substitute ( addr1 len1 – addr2 len2 n/ior  ) gforth-experimental “string-substitute”

テキスト addr1 len1 内のすべてのマクロを置き換えます。 addr2 len2 が置換結果で、 n は置換の数で、 負数場合は throw 可能な ior です。

substitute ( addr1 len1 addr2 len2 – addr2 len3 n/ior  ) string-ext “substitute”

テキスト addr1 len1 内のすべてのマクロを置き換え、 結果を addr2 len2 にコピーします。 addr2 len3 は置換結果で、 n は置換した数、または負数場合は throw 可能な ior です。

unescape ( addr1 u1 dest – dest u2  ) string-ext “unescape”

addr1 u1 内のすべての区切り文字を2重にすることで、 置換後の結果が元のテキストのままになります。 結果を格納するバッファー dest には長さ指定の必要が無いことに注意してください。 最悪の場合、 必要な文字数は u1 の 2倍です。 dest u2 は結果の文字列の長さです。

$unescape ( addr1 u1 – addr2 u2  ) gforth-experimental “string-unescape”

unescape と同じですが、 $tmp を使用して一時的な結果文字列を作成します。


6.20.13 CSV Reader

CSV(Comma-separated values)は、 データをやり取りするための一般的なテキスト形式です。 Gforth は csv.fs でCSVリーダーを提供します(注意: gforth に最初から組み込まれてはいないので、 都度 require csv.fs 等する必要がある)。

read-csv ( addr u xt –  ) gforth-experimental “read-csv”

CVS ファイル addr u を読み取り、 見つかった項目ごとに xt を実行します。 xt( addr ucol line -- )、 つまり文字列と、 現在の列番号(0 で始まる)と、 現在の行番号(1 で始まる)を受け取ります。


6.21 OS command line arguments

コマンドラインで Gforth プログラムに引数を渡す通常の方法は、 -e オプションを使用することです。 たとえば、

gforth -e "123 456" foo.fs -e bye

ただし、 コマンドライン引数を直接パースしたい場合もあります。 その場合、 next-arg を通じて(イメージ固有の)コマンドライン引数にアクセスできます:

next-arg ( – addr u  ) gforth-0.7 “next-arg”

OS コマンドラインから次の引数を取得し、 それを消費し addr u に返します。 引数が残っていない場合は、 0 0 を返します。

next-arg のプログラム例 echo.fs を以下に示します:

: echo ( -- )
    begin
	next-arg 2dup 0 0 d<> while
	    type space
    repeat
    2drop ;

echo cr bye

これは以下のようにして起動します

gforth echo.fs hello world

そうすると以下のように出力されます

hello world

以下は、 OS コマンドラインを処理する下位レベルのワード群です:

arg ( u – addr count  ) gforth-0.2 “arg”

u 番目のコマンドライン引数の文字列を返します。 u が最後の引数を超えている場合は 0 0 を返します。 0 arg は、 Gforth を起動したプログラム名です。 次の未処理の引数は常に 1 arg で、 その後の引数は 2 arg などです。 システムによってすでに処理された引数はすべて削除されます。 引数を処理した後、 shift-args を使用して引数を削除できます。

shift-args ( ) gforth-0.7 “shift-args”

1 arg が削除され、 後続のすべての OS コマンドライン・パラメーターが 1 だけ左にシフトされ、argc @ が減ります。 このワードは argv @ を変更できます。

最後に、 Gforth は最下位レベルで以下のワード群を提供します:

argc ( – addr  ) gforth-0.2 “argc”

変数(Variable) – コマンドライン引数の数(コマンド名を含む)。 next-argshift-args によって変更されます。

argv ( – addr  ) gforth-0.2 “argv”

変数(Variable) – コマンドライン引数 (コマンド名を含む) へのポインターのベクトル(vector)へのポインター。 各引数は、 C言語スタイルのゼロで終わる文字列として表されます。 next-argshift-args によって変更されます。


6.22 Locals

ローカル変数を使用すると、 Forth プログラミングがより楽しくなり、 Forth プログラムが読みやすくなります。 残念ながら、 標準 Forth の ローカル変数には制限がたくさんあります。 したがって、 標準 Forth のローカル変数ワードセットだけでなく、 Gforth 独自のより強力なローカル変数ワードセットも提供します(標準Forthのローカル変数ワードセットは、 Gforth のローカル変数ワードセットで実装しました)。

このセクションのアイデアは、 M. Anton Ertl, Automatic Scoping of Local Variables, EuroForth ’94 でも公開されています。


6.22.1 Gforth locals

ローカル変数は以下のように定義できます

{: local1 local2 ... -- comment :}

or

{: local1 local2 ... :}

or

{: local1 local2 ... | ulocal0 ulocal1 -- comment :}

例えば、 以下のように使います

: max {: n1 n2 -- n3 :}
 n1 n2 > if
   n1
 else
   n2
 endif ;

ローカル変数定義とスタック・コメントは、 類似するよう意図しています。 ローカル変数定義は、 多くの場合、 ワードのスタック・コメントを置き換えます。 ローカル変数の順序はスタック・コメント内の順序に対応し、 -- 以降はすべて実際はコメントです。

この類似性には 1 つ欠点があります。 ローカル変数宣言とスタック・コメントを混同しやすく、 バグが発生し、 見つけにくくなります。 ただし、 この問題は適切なコーディング規約によって回避できます。 同じプログラム内で両方の表記法を使用しないでください。 そうする場合は、 追加の手段を使用して区別する必要があります。 例えば、 場所によって区別します。

ローカルの名前の前に型指定子を付けることができます。 たとえば、浮動小数点値の場合は F: です:

: CX* {: F: Ar F: Ai F: Br F: Bi -- Cr Ci :}
\ 複素数の掛け算
 Ar Br f* Ai Bi f* f-
 Ar Bi f* Ai Br f* f+ ;

Gforth は現在、 セル型指定子(W:, WA:, W^)や、 2倍長整数型指定子(D:, DA:, D^)や、 浮動小数点数型指定子(F:, FA:, F^)と、 さまざまなフレーバーの xt 型指定子(xt:, xta:)、 をサポートしています:

valueフレーバー(value-flavoured)

(see Values) valueフレーバーのローカル変数(W:D: などで定義)はその値を生成し、 TO で変更できます。

varueフレーバー(vaLue ではなくて vaRue)

(see Varues) varueフレーバーのローカル変数 foo (WA: などで定義) は、 addr foo を使用してアドレスを取得できること(変数のスコープを離れると無効になります)を除いて、 valueフレーバーのローカル変数とまったく同じように振る舞います。 現時点ではパフォーマンスに違いはありませんが、 長期的には、 valueフレーバーのローカル変数はレジスターに常駐できるため、 大幅に高速になるでしょう。

variableフレーバー

(see Variables) variableフレーバーのローカル変数(W^ などで定義)はそのアドレスを生成します(変数のスコープを離れると無効になります)。 たとえば、 標準のワード emit は、 以下のように variableフレーバーのローカル変数(C^ char*) と type で定義できます:

: emit {: C^ char* -- :}
    char* 1 type ;
deferフレーバー(defer-flavoured)

(see Deferred Words) (XT: または XTA: と指定した) defer フレーバーのローカル変数 xt を execute します。 action-of を使用すると、 defer フレーバーのローカル変数から xt を取得できます。 ローカル変数が xta: で定義されている場合、 addr を使用して、 xt が保存されているアドレス(ローカル変数のスコープの終わりまで有効)を取得できます。 たとえば、 標準のワード execute は、 以下のように defer フレーバーのローカル変数で定義できます:

: execute {: xt: x -- :}
  x ;

型指定子のないローカル変数は W: ローカル変数として扱われます。 以下を使用して addr の使用を許可または禁止できます:

default-wa: ( ) gforth-experimental “default-wa:”

型指定子なしで定義されたローカル変数で addr を許可します。 言い換えれば、 型指定子無しでローカル変数定義したときは wa: 型指定子を指定したのと同じです

default-w: ( ) gforth-experimental “default-w:”

型指定子なしで定義されたローカル変数では addr を禁止します。 言い換えれば、 型指定子なしで定義されたローカル変数は w: 型指定子を指定してローカル変数を定義したのと同じです。

| の後ろで定義されたローカル変数を除いて、 ローカル変数の全てのフレーバーは、 データ・スタックの値または、 (FP ローカル変数の場合) FP スタックの値で初期化されます。 Gforth は | の後ろで定義されたローカル変数を 0 に初期化します。 一部の Forth システムでは初期化されないままになっています。

Gforth は、 ローカル・バッファーとデータ構造体のための角括弧(square bracket)表記をサポートしています。 これらのローカル変数は variableフレーバーのローカル変数に似ており、 サイズは定数式として指定します。 宣言は name[ size ] のようになります。 Forth の式 size は宣言中に評価され、 サイズをバイト単位で指定するスタック効果 ( -- +n ) が必要です。 なお、 角括弧 [ は定義された名前の一部です。

ローカルのデータ構造体は、 データ・スタックに渡されたアドレスから size バイトをコピーすることによって初期化されます。 (宣言内の | の後の、)初期化されていないローカルのデータ構造体は消去されず、 以前にローカル・スタックにあったデータをすべて含むだけです。

Example:

begin-structure test-struct
  field: a1
  field: a2
end-structure

: test-local {: foo[ test-struct ] :}
    foo[ a1 !  foo[ a2 !
    foo[ test-struct dump ;

Gforth では、コロン定義内のあらゆる場所でローカル変数を定義できます。 これにより、 以下のような疑問が生じます:


6.22.1.1 Locals definitions words

このセクションでは、 ローカル変数を定義するために使用されるワード群を説明します。 注意: ローカル変数を定義するワード(W: など)の実行時は、 右端のローカル変数定義から左端のローカル変数定義の方向で実行され、 右端のローカル変数がスタックのTOSから得る事になることに注意してください。

{: ( – hmaddr u latest latestnt wid 0  ) local-ext “open-brace-colon”

ローカル変数定義の開始。

-- ( hmaddr u latest latestnt wid 0 ... –  ) gforth-0.2 “dash-dash”

ローカル変数定義中の -- から :} までのすべてが無視されます。 これは通常、 ローカル変数定義をスタック効果の説明としても使えるよう、 一人二役の役割を持たせる場合に使います。

| ( ) gforth-1.0 “bar”

| の後ろで定義されたローカル変数はスタックから初期化されません。 したがって、 W: のようなワードの実行時のスタック効果は ( -- ) に変わります。

:} ( hmaddr u latest latestnt wid 0 xt1 ... xtn –  ) gforth-1.0 “colon-close-brace”

ローカル変数定義の終了。

{ ( – hmaddr u latest latestnt wid 0  ) gforth-0.2 “open-brace”

ローカル変数定義を開始します。 このワードの Forth-2012 標準名は {: です。

} ( hmaddr u latest latestnt wid 0 xt1 ... xtn –  ) gforth-0.2 “close-brace”

ローカル変数定義を終了します。 このワードの Forth-2012 標準名は :} です。

W: ( compilation "name" – a-addr xt; run-time x –  ) gforth-0.2 “w-colon”

valueフレーバーのセル・ローカル変数 name を定義します ( -- x1 )

WA: ( compilation "name" – a-addr xt; run-time x –  ) gforth-1.0 “w-a-colon”

varueフレーバーのセル・ローカル変数 name を定義します ( -- x1 )

W^ ( compilation "name" – a-addr xt; run-time x –  ) gforth-0.2 “w-caret”

variableフレーバーのセル・ローカル変数 name を定義します ( -- a-addr )

D: ( compilation "name" – a-addr xt; run-time x1 x2 –  ) gforth-0.2 “d-colon”

valueフレーバーの2倍長整数ローカル変数 name を定義します ( -- x3 x4 )

DA: ( compilation "name" – a-addr xt; run-time x1 x2 –  ) gforth-1.0 “w-a-colon”

varueフレーバーの2倍長整数ローカル変数 name を定義します ( -- x3 x4 )

D^ ( compilation "name" – a-addr xt; run-time x1 x2 –  ) gforth-0.2 “d-caret”

variableフレーバーの2倍長整数ローカル変数 name を定義します ( -- a-addr )

C: ( compilation "name" – a-addr xt; run-time c –  ) gforth-0.2 “c-colon”

valueフレーバーの char ローカル変数 name を定義します ( -- c1 )

CA: ( compilation "name" – a-addr xt; run-time c –  ) gforth-1.0 “c-a-colon”

varueフレーバーの char ローカル変数 name を定義します ( -- c1 )

C^ ( compilation "name" – a-addr xt; run-time c –  ) gforth-0.2 “c-caret”

variableフレーバーの char ローカル変数 name を定義します ( -- c-addr )

F: ( compilation "name" – a-addr xt; run-time r –  ) gforth-0.2 “f-colon”

valueフレーバーの浮動小数点数ローカル変数 name を定義します ( -- r1 )

FA: ( compilation "name" – a-addr xt; run-time f –  ) gforth-1.0 “f-a-colon”

varueフレーバーの浮動小数点数ローカル変数 name を定義します ( -- r1 )

F^ ( compilation "name" – a-addr xt; run-time r –  ) gforth-0.2 “f-caret”

variableフレーバーの浮動小数点数ローカル変数 name を定義します ( -- f-addr )

XT: ( compilation "name" – a-addr xt; run-time xt1 –  ) gforth-1.0 “x-t-colon”

deferフレーバーのセル・ローカル変数 name を定義します ( ... -- ... )

XTA: ( compilation "name" – a-addr xt; run-time ... – ...  ) gforth-1.0 “x-t-a-colon”

addr を使用できる defer フレーバーのローカル変数 name を定義します。

|--:}} は通常、 検索順序スタック(the search order)に含まれないことに注意してください(これらは locals-types ボキャブラリーに含まれます)。 そしてこれらは必ずしも全ての Forth システムでワードとして存在しているわけではありません。 したがって、 これらは Gforth のワードとして文書化されています。


6.22.1.2 Where are locals visible by name?

ローカル変数をその名前によって可視できるのはどこまででしょうか? – 基本的に、 その答えは、 ローカル変数がブロック構造言語で期待される場所で、 場合によってはもうちょっとだけ長くできます。 ローカル変数のスコープを制限したい場合は、 その定義を SCOPE...ENDSCOPE で囲んで下さい。

scope ( compilation  – scope ; run-time  –  ) gforth-0.2 “scope”
endscope ( compilation scope – ; run-time  –  ) gforth-0.2 “endscope”

これらのワードは制御構造のワードのように動作するため、 CS-PICK および CS-ROLL とともに使用して、 任意の方法で範囲を制限できます。

可視性の質問に対するより正確な答えが必要な場合のために、 ここで基本原則を示します: ローカル変数は、 ローカル変数の定義を通じてのみ到達できるすべての場所で可視です30。 言い換えると、 ローカル変数の定義を経由せずに到達できる場所では不可視です。 たとえば、 IF...ENDIF の中で定義されたローカル変数は ENDIF まで可視で、 BEGIN...UNTIL 内で定義されたローカル変数は UNTIL の後(たとえば、後続の ENDSCOPE まで)で可視です。

このソリューションの背景にある理由は次のとおりです: 私達は、 意味がある限り、 ローカル変数を可視させたいと考えています。 ユーザーは、 明示的なスコープを使用することで、 いつでも可視性を短くすることができます。 ローカル変数の定義によってのみ到達できる場所では、 ローカル変数名の意味は明らかです。 他の場所ではそうではありません。 ローカル変数定義が含まれていない制御フロー・パスでローカル変数はどのように初期化されるのでしょうか? 2 つの独立した制御フロー パスで同一ローカル変数名が 2 回定義されている場合、 それはどちらのローカル変数を意味するのでしょうか?

上記で、 ほぼすべてのユーザーにとって十分詳細であるため、 このセクションの残りの部分はスキップしてかまいません。 本当にすべての血みどろの詳細とオプションを知る必要がある場合は、 以下を読み続けてください。

このルールを実装するには、 コンパイラーはどの場所が到達不能(unreachable)であるかを認識する必要があります。 AHEADAGAINEXITLEAVE の後で、 これが自動的に認識されます。 他の場合(例: ほとんどの THROW の後)、 UNREACHABLE というワードを使用して、 制御フローがその場所に到達しないことをコンパイラーに伝えることができます。 UNREACHABLE が使用できる場所で使用されなかった場合、 唯一の結果は、 一部のローカル変数の可視性が上記のルールに記載されているよりも制限されることです。 UNREACHABLE を使用すべきではない場所で使用すると(つまり、 コンパイラに嘘をついた場合)、 バグのあるコードが生成されます。

UNREACHABLE ( ) gforth-0.2 “UNREACHABLE”

このルールのもう 1 つの問題は、 BEGIN で、 どのローカル変数が incoming back-edge で可視されるかをコンパイラーが認識できないことです。 以下で説明するすべての問題は、 コンパイラーのこの無知が原因です(BEGIN ループを例として使用してこの問題について説明します。 この説明は ?DO および他のループにも当てはまります)。 おそらく最も陰険な例は以下のとおりです:

AHEAD
BEGIN
  x
[ 1 CS-ROLL ] THEN
  {: x :}
  ...
UNTIL

これは、 可視性ルールに従って合法である必要があります。 x の使用には、 定義を介してのみ到達できます。 ただし、 下記に明示した使用法でなければなりません。

この例から、 可視性ルールを完全に実装するには大きな問題が伴うことが明らかです。 私たちの実装は、 一般的なケースを宣伝どおりに扱い、 例外は安全な方法で処理されます。 コンパイラーは、 BEGIN の後に可視できるローカル変数について合理的な推測を行います。 悲観的すぎると、 ローカル変数が定義されていないという偽のエラーがユーザーに表示されます。 コンパイラーが楽観的すぎる場合、 後でこれに気づき、 警告を発行します。 上記の場合、 コンパイラーは x が使用時に未定義であることについて文句を言います。 このセクションのあいまいな例から、 コンパイラーをトラブルに陥らせるには非常に特殊な制御構造が必要であることがわかりますが、 それでもコンパイラーは多くの場合問題なく動作します。

BEGIN がそれより上から到達可能な場合、 最も楽観的な推測は、 BEGIN の前に可視であるすべてのローカル変数も BEGIN の後にも可視であることです。 この推測は、 BEGIN 経由でのみ入るすべてのループ、 特に通常の BEGIN...WHILE...REPEAT および BEGIN...UNTIL ループに対して有効であり、 コンパイラーに実装されています。 BEGIN への分岐が AGAIN または UNTIL によって最終的に生成されると、 コンパイラーは推測をチェックし、 それが楽観的すぎる場合はユーザーに警告します:

IF
  {: x :}
BEGIN
  \ x ? 
[ 1 cs-roll ] THEN
  ...
UNTIL

ここで、 xBEGIN までのみ存続しますが、 コンパイラーは THEN まで存続すると楽観的に想定します。 UNTIL をコンパイルするときにこの違いに気づき、 警告を発行します。 ユーザーは警告を回避し、 明示的なスコープを使用して x が間違った領域で使用されていないことを確認できます:

IF
  SCOPE
  {: x :}
  ENDSCOPE
BEGIN
[ 1 cs-roll ] THEN
  ...
UNTIL

推測は楽観的であるため、 未定義のローカル変数に関する偽のエラー・メッセージは表示されません。

BEGIN がそれより上から到達可能でない場合(たとえば、 AHEAD または EXIT の後)、 BEGIN の後で定義されたローカル変数の可視については、 コンパイラーは楽観的な推測を行うことさえできません。

悲観的に、 制御構造の外側の最新の場所(つまり、 制御フロー・スタック上に何もない場所)で可視であったすべてのローカル変数が可視であると仮定します。 これは以下のことを意味します:

: foo
  IF {: z :} THEN
  {: x :}
  AHEAD
    BEGIN
      ( * )
    [ 1 CS-ROLL ] THEN
    {: y :}
    ...
  UNTIL ;

ここで、 ( * ) でマークされた場所では、 x は可視ですが、 y は不可視です(ただし、 到達可能性ルールによれば、 可視であるべきです)。 z はそこでは不可視で、 可視であるべきではありません。

ただし、 ASSUME-LIVE を使用すると、 最上位の制御フロー・スタック項目が作成された時点と同じローカル変数が BEGIN で可視になるのだと、 コンパイラーに想定させることができます。

ASSUME-LIVE ( orig – orig  ) gforth-0.2 “ASSUME-LIVE”

例えば、 以下のように使います

IF
  {: x :}
  AHEAD
    ASSUME-LIVE
    BEGIN
      x
    [ 1 CS-ROLL ] THEN
    ...
  UNTIL
THEN

ここで、 x のローカル変数定義は制御構造内にあるため、 x を使用した時点では x は可視ではありませんが、 ASSUME-LIVE を使用することで、 プログラマはコンパイラーに AHEAD の時点で可視である、 そのローカル変数が、 BEGIN の時点でも可視であるべきであると伝えます。

BEGIN の前にローカル変数が定義されている他のケースは、 ASSUME-LIVE の前に適切な CS-ROLL を挿入する(そして ASSUME-LIVE の背後にある制御フロー・スタック操作を変更する)ことで処理できます。

ローカル変数が BEGIN の後で定義されている場合(ただし、 BEGIN の直後で可視である必要があります)は、 ループを再配置することによってのみ処理できます。 たとえば、上記の「最も陰険な」例は以下のように整理できます:

BEGIN
  {: x :}
  ... 0=
WHILE
  x
REPEAT

6.22.1.3 How long do locals live?

ローカル変数の生存期間はどのくらいなのか? – 生存期間に関する質問に対する正しい答えは、次のとおりです: ローカル変数は、 少なくともアクセスできる限りは生存します。 valueフレーバーのローカル変数の場合、 これは、 その可視性が終了するまでを意味します。 ただし、 variableフレーバーのローカル変数は、可視性の範囲をはるかに超えてアドレスを通じてアクセスできる可能性があります。 結局のところ、 これはそのようなローカル変数がガベージ・コレクションを受けなければならないことを意味します。 これには、Forth らしくない実装の複雑さが伴うため、 他の言語(C言語など)と同じ引っ込み思案な解決策を採用しました。 つまり、 ローカル変数は、 それが可視である間のみ生存します。その後、そのアドレスは無効になります(そして、 その後そのアドレスにアクセスするプログラムはエラーになります)。


6.22.1.4 Locals programming style

どこでもローカル変数を定義できる自由は、 プログラミング・スタイルを劇的に変える可能性を秘めています。 特に、 中間ストレージにリターン・スタックを使用する必要がなくなります。 さらに、 すべてのスタック操作(実行時に決定される引数を持つ PICKROLL を除く)を排除できます。 スタック項目の順序が間違っている場合は、 すべてのスタック項目のローカル変数定義を記述し、 その次に、 あなたが必要とする順序で項目を書き込むだけです。

これは少し突飛なように思えますし、 スタック操作を排除することが意識的なプログラミング目標になる可能性は低いです。 それでも、 ローカル変数を積極的に使用すれば、 スタック操作の数は大幅に減少します(例: max (see Gforth locals) を max の従来の実装と比較してみましょう)。

これは、 ローカル変数の潜在的な利点の 1 つ、 つまり Forth プログラムを読みやすくすることを示しています。 もちろん、 この利点は、 プログラマがワードを長ったらしく書くための自由度を追加する訳ではなく、 ファクタリング(因数分解)の原則を尊重し続けた場合にのみ実現されます。

TO の使用は可能な限り避けるべきです。 TO がない場合、 すべてのvalueフレーバーのローカル変数には 1 つの代入しかなく、 関数型言語の多くの利点が Forth に当てはまります。 つまり、 プログラムの分析・最適化・読み取りが容易になります。 ローカル変数が何を表すかは定義から明らかであり、 後で別のものに変わることはありません。

たとえば、 TO を使用したローカル変数定義は以下のようになります:

: strcmp {: addr1 u1 addr2 u2 -- n :}
 u1 u2 min 0
 ?do
   addr1 c@ addr2 c@ -
   ?dup-if
     unloop exit
   then
   addr1 char+ TO addr1
   addr2 char+ TO addr2
 loop
 u1 u2 - ;

ここで、 TO は、 ループの反復ごとに addr1addr2 を更新するために使用されます。 strcmp は、TO の使用による可読性の問題の典型的な例です。 strcmp を読み始めると、 addr1 が文字列の先頭を指していると考えるでしょう。 ループの終わり近くになって初めて、 それが何か別のものであることがわかります。

これは、 現在の反復に適切な値で初期化される 2 つのローカル変数をループの開始時に定義することで回避できます。

: strcmp {: addr1 u1 addr2 u2 -- n :}
 addr1 addr2
 u1 u2 min 0 
 ?do {: s1 s2 :}
   s1 c@ s2 c@ -
   ?dup-if
     unloop exit
   then
   s1 char+ s2 char+
 loop
 2drop
 u1 u2 - ;

ここで、 s1 がループの反復ごとに異なる値を持つことは最初から明らかです。


6.22.1.5 Locals implementation

Gforth は追加のローカル変数用スタック(ローカル・スタック)を使用します。 この最も説得力のある理由は、 リターン・スタックが浮動小数点数に整列されていないことです。 この追加のスタックを使用すると、 リターン・スタックをローカル変数用スタックとして使用する場合の問題や制限も解消されます。 他のスタックと同様に、 ローカル・スタックは下位アドレスに向かって成長します。 いくつかのプリミティブにより効率的な実装になっています。 あなたは、 これらを直接使用するべきではありませんが、 see の出力には表示されるため、 以下に文書化しておきます:

@localn ( noffset – w ) gforth-internal “fetch-local-n”
\ 訳注: ローカル・スタックのTOSを0として noffset 番目のコピーをデータ・スタックに積む
f@localn ( noffset – r ) gforth-1.0 “f-fetch-local-n”
lp@ ( – c-addr ) gforth-0.2 “lp-fetch”

C_addr は、 ローカル・スタック・ポインターの現在の値です。

doc-lp+!#(訳注: まだ説明書いて無いっぽい)

lp! ( c-addr – ) gforth-internal “lp-store”
>l ( w – ) gforth-0.2 “to-l”
\ 訳注: w をローカル・スタックにプッシュ
f>l ( r – ) gforth-0.2 “f-to-l”

これらのプリミティブに加えて、 一般的に発生するインライン引数に対するこれらのプリミティブのいくつかの特殊化が、 効率上の理由から提供されています(例: 0 @localn の特殊化として @local0 )。 以下のコンパイル・ワード(compiling words)は、 適切な特殊バージョン、 または一般バージョンを適切にコンパイルします(訳注: @local0 シリーズは、 @local0 ローカル・スタックのTOS(のコピー)をスタックに積む、 @local1 ローカル・スタックの2nd(のコピー)をスタックに積む、 〜 @local4 まである):

compile-lp+! ( n –  ) gforth-0.2 “compile-l-p-plus-store”

?branch-lp+!# のような、 条件分岐と lp+!# の組み合わせ(ローカル・スタック・ポインターは分岐が選択された場合にのみ変更されます)は、 ループの効率と正確性のために提供されています。

ディクショナリー空間内の特別な領域が、 ローカル変数名を保持するために予約されています。 {: はディクショナリー・ポインターをこの領域に切り替え、 :} はそれを元に戻し、 ローカル変数の初期化コードを生成します。 W: などは通常の定義ワードです。 この特別な領域は、 すべてのコロン定義の先頭でクリアされます。

Gforth のディクショナリーの特別な機能は、 型指定子なしでローカルの定義を実装するために使用されます。 すべてのワードリスト(別名ボキャブラリー)には、 検索などのための独自のメソッド(methods)があります (see Word Lists)。 型指定子なしでローカルの定義を実装するという目的のために、 私達は特別な検索メソッドを使用してワードリストを定義しました。 ワードが検索されると、 実際には W: を使用してそのワードが作成されます。 {: は、 最初に :}W: などを含むワードリストで検索し、 次に型指定子のないローカル変数を定義するためのワードリストで検索するよう検索順序スタック(the search order)を変更します。

生存期間ルールは、 コロン定義内のスタック規律(stack discipline)をサポートします。 ローカル変数の生存期間は、 他のローカル変数の生存期間と入れ子になっているか、 あるいは、 他のローカル変数の生存期間と重ならないか、 です。

BEGINIFAHEAD では、 ローカル・スタック・ポインター操作のコードは生成されません。 制御構造のワード間で、 ローカル変数定義はローカル変数をローカル・スタックにプッシュできます。 AGAIN は、 他の 3 つの制御フローワードとの中で最も単純です。 分岐する前に、 対応する BEGIN のローカル・スタックの深さを復元する必要があります。 そのコードは以下のようになります:

lp+!# current-locals-size − dest-locals-size
branch <begin>

UNTIL はもう少し複雑です。 分岐して戻る場合は、 AGAIN と同じようにローカル・スタックを調整する必要があります。 ただし、 戻らずにその後ろへ流れる場合は、 ローカル・スタックを変更してはなりません。 コンパイラーは以下のコードを生成します:

?branch-lp+!# <begin> current-locals-size − dest-locals-size

ローカル・スタック・ポインターは、 分岐が行われた場合にのみ調整されます。

THEN は、 やや非効率なコードを生成する可能性があります:

lp+!# current-locals-size − orig-locals-size
<orig target>:
lp+!# orig-locals-size − new-locals-size

2 番目の lp+!# は、 ローカル・スタック・ポインターを orig 時点のレベルから THEN の後のレベルに調整します。 最初の lp+!# は、 ローカル・スタック・ポインターを現在のレベルから orig 時点のレベルに調整するため、 完全な効果は、THEN の後の現在のレベルから正しいレベルに調整されることになります。

従来の Forth の実装では、 dest 制御フロー・スタック・エントリはターゲット・アドレスにすぎず、 orig エントリはパッチ当てされるアドレスにすぎません。 ローカル変数の実装は、 すべての orig または dest 項目にワードリストを追加します。 これは、 エントリによって記述された時点で可視である(または可視である想定される)ローカル変数のリストです。 私たちの実装では、 エントリの種類を識別するためのタグも追加します。 特に、 生きているのと死んでいるエントリ(到達可能なエントリと到達不可能なエントリ)を区別するためです。

ローカル変数のワードリストに対して、 いくつかの珍しい操作を実行する必要があります:

common-list ( list1 list2 – list3  ) gforth-internal “common-list”
sub-list? ( list1 list2 – f  ) gforth-internal “sub-list?”
list-size ( list – u  ) gforth-internal “list-size”

ローカル変数のワードリスト実装のいくつかの機能により、 これらの操作の実装が簡単になります。 ローカル変数のワードリストはリンクされたリストとして編成されます。 リストに同一のローカル変数が含まれている場合、 これらのリストの末尾は共有されます。 名前のアドレスは、 リスト内でその後ろにある名前のアドレスよりも大きくなります。

もう 1 つの重要な実装の詳細は、 変数 dead-code です。これは、 BEGINTHEN によって直接到達できるか、 解決するブランチ経由でのみ到達できるかを判断するために使用されます。 dead-codeUNREACHABLEAHEADEXIT などによって設定され、 コロン定義の先頭 や BEGIN や 通常は THEN によってクリアされます。.

カウンタ付きループはほとんどの点で他のループと似ていますが、 LEAVE には特別な注意が必要です。 基本的に AHEAD と同じサービスを実行しますが、 制御フロー・スタック・エントリは作成されません。 したがって、 情報は別の場所に保存する必要があります。 従来、 情報は LEAVE によって作成されたブランチのターゲット・フィールドに、 これらのフィールドをリンク・リストに編成することによって格納されていました。 残念ながら、 この巧妙なトリックでは、 拡張制御フロー情報を保存するための十分なスペースが提供されません。 したがって、 別のスタックである Leave スタックを導入します。 これには、 すべての未解決の LEAVE の制御フロー・スタック・エントリが含まれています。

ローカル変数名は、 どの制御フロー経路にも表示されなくなった場合でも、 コロン定義の終わりまで保持されます。 場合によっては、 これによりローカル変数の名前領域に必要な領域が増加する可能性がありますが、 通常はこの領域を再利用するよりもコード量にかかるコストは少なくなります。


6.22.1.6 Closures

Gforth は基礎的なクロージャ(closure)も提供します。 クロージャは、 引用(quotation)(see Quotations)とローカル変数の組み合わせです。 Gforth のクロージャには、 クロージャの実行時に値が入力されるローカル変数があり、 トランポリン xt (trampoline xt)が生成されます。 そのトランポリン xt を execute すると、 ローカル・スタック上のクロージャのローカル変数にアクセスして、 クロージャのコードが実行されます。 クロージャのローカル変数の変更は永続的ではありません。 つまり、 クロージャが EXIT されると、 変更された値は失われます。

[{: ( – hmaddr u latest latestnt wid 0  ) gforth-experimental “start-closure”

クロージャを開始します。 クロージャはまず、 クロージャのために使用するローカル変数フレームを宣言し、 次にそれらのローカル変数で実行されるコードを宣言します。 クロージャは引用(quotations)のように ;] で終わります。 ローカル宣言は、 クロージャ・ローカルが作成される場所に応じて終了します。 実行時、 クロージャは トランポリン xt として作成され、 スタックからローカル変数・フレームの値を埋めます。 xt の実行時に、 ローカル変数・フレームがローカル・スタックにコピーされ、 クロージャのコード内で使用されます。 戻った後、 これらの値はローカル・スタックから削除され、 クロージャ自体は更新されません。

:}l ( hmaddr u latest latestnt wid 0 a-addr1 u1 ... –  ) gforth-1.0 “close-brace-locals”

クロージャ・ローカルの宣言を終了します。 クロージャはローカル・スタックに割り当てられます。

:}d ( hmaddr u latest latestnt wid 0 a-addr1 u1 ... –  ) gforth-1.0 “colon-close-brace-d”

クロージャ・ローカル宣言を終了します。 クロージャはディクショナリーに割り当てられます。

:}h ( hmaddr u latest latestnt wid 0 a-addr1 u1 ... –  ) gforth-1.0 “colon-close-brace-h”

クロージャ・ローカル宣言を終了します。 クロージャーはヒープに割り当てられます。

:}h1 ( hmaddr u latest latestnt wid 0 a-addr1 u1 ... –  ) gforth-1.0 “colon-close-brace-h”

クロージャ・ローカル宣言を終了します。 クロージャーはヒープに割り当てられます。

:}xt ( hmaddr u latest latestnt wid 0 a-addr1 u1 ... –  ) gforth-1.0 “colon-close-brace-x-t”

クロージャ・ローカル宣言を終了します。 クロージャは xt によってスタック上に割り当てられるため、 クロージャの実行時のスタック効果は ( xt-alloc -- xt-closure ) となります。

>addr ( xt – addr  ) gforth-experimental “to-addr”

(free-closure から呼び出されます)ヒープ上のクロージャの xt を addr に変換し、 free に渡すことでクロージャを削除できます。

free-closure ( xt –  ) gforth-internal “free-closure”

ヒープに割り当てられたクロージャを解放(free)します

: foo [{: a f: b d: c xt: d :}d a . b f. c d. d ;] ;
5 3.3e #1234. ' cr foo execute

上記 foo は、 単一セルと浮動小数点数と2倍長整数と xt を含むクロージャをディクショナリー内に作成し、呼び出し時に最初の 3 つの値を出力後に xt を実行します。

これにより、 Algol コンパイラーをテストするために 1964 年にドナルド・クヌースが提案した “Man or boy test” を実装することができます(訳注: 手元ではサッパリ動いてない(0.7.9_20240418, 2024.7))

: A {: w^ k x1 x2 x3 xt: x4 xt: x5 | w^ B :} recursive
    k  0<= IF  x4 x5 f+  ELSE
        B k x1 x2 x3 action-of x4 [{: B k x1 x2 x3 x4 :}L
            -1 k +!
            k  B  x1 x2 x3 x4 A ;] dup B !
        execute  THEN ;
: man-or-boy? ( n -- ) [: 1e ;] [: -1e ;] 2dup swap [: 0e ;] A f. ;

場合によっては、 クロージャを変更するには永続的なストレージが必要です。 複数のクロージャがその永続ストレージを共有する可能性さえあります。 上の例では、 外部プロシージャのローカル変数がこれに使用されていますが、 場合によっては、 クロージャが外部プロシージャよりも長く存続します。 特に、 ディクショナリーまたはヒープ上に割り当てられたクロージャは、 親プロシージャより長く存続するように設計されています。

これらについては、 クロージャのように割り当てられるホーム・ロケーション(home locations)がありますが、 そのコードは作成時に直接実行され、 ホーム・ロケーションのアドレスを提供する必要があります。

: bar ( a b c -- aaddr baddr caddr hl-addr )
    <{: w^ a w^ b w^ c :}h a b c ;> ;

この例では、 ヒープ上に 3 つのセルを持つホーム・ロケーション(home location)を作成し、 3 つのロケーションのアドレスとホーム・ロケーションのアドレスを返します。 このアドレスは、 ホーム・ロケーションが不要になったときに free するために使用できます。

<{: ( – hmaddr u latest latestnt wid 0  ) gforth-experimental “start-homelocation”

ホーム・ロケーション(home location)の開始

;> ( ) gforth-experimental “end-homelocation”

ホーム・ロケーションの終了


6.22.2 Standard Forth locals

Forth-2012 標準では、 Gforth のローカル変数の制限付きバージョンであるローカル変数構文が定義されています:

  • ローカル変数はセル・サイズの値のみにすることができます(型指定子は許可されません)。
  • ローカル変数は制御構造の外側でのみ定義できます。
  • 定義ごとに許可されるローカル変数定義ブロック({::})は 1 つだけです。
  • ローカル変数は、 リターン・スタックの明示的な使用を妨げる可能性があります。 正確な(そして長い)ルールについては、 標準を参照してください。 ローカル変数を使用している定義でリターン・スタックにアクセスするワードを使用しない場合は問題ありません。 このルールの目的は、 リターン・スタックを使ったローカル変数の実装を容易にすることです。
  • ローカル変数定義全体を 1 行に含める必要があります。

標準 Forth ローカル変数ワードセット自体は {: と 以下の 2 つのワードで構成されます:

(local) ( addr u –  ) local “paren-local-paren”

ANS Forth ローカル変数拡張ワードセット(ANS Forth locals extension wordset)は locals| を使用して構文を定義しますが、 これはとても酷い代物なので、 使用しないことを強くお勧めします。 Gforth への移植を容易にするためにこの構文を実装しましたが、 ここでは文書化しません。 この構文の問題は、 ローカル変数が標準のスタック・コメント表記とは逆の順序で定義されているため、 プログラムが読みにくくなり、 読み間違いや書き間違いが起こりやすくなることです。 この構文の唯一の利点は、 ANS Forth ローカル変数ワードセットを使用して実装が簡単であることですが、 {: 構文だって同じくらい実装は簡単ですからね?


6.23 Structures

このセクションでは、 Gforth に付属する構造体パッケージを紹介します。 標準 Forth で実装されたパッケージのバージョンは、 compat/struct.fs で入手できます。 このパッケージは、 1989 年の comp.lang.forth への投稿に触発されました(残念ながら、誰による投稿かは覚えていません。おそらく John Hayes によるものでしょう)。 このセクションのバージョンは、 M. Anton Ertl, Yet Another Forth Structures Package, Forth Dimensions 19(3), pages 13–16 です。 Marcel Hendrix は有益なコメントを提供してくれました。


6.23.1 Why explicit structure support?

複数のフィールドを含む構造体を使用したい場合は、 その構造体用にメモリーを予約し、 アドレス算術演算を使用してフィールドにアクセスするだけです(see Address arithmetic)。 例として、 以下の 3 つのフィールド(a, b, c)を持つ構造体を考えてみましょう

a

これは浮動小数点数(float)です

b

これはセル(cell)です

c

これは浮動小数点数(float)です

構造体の (float 整列された) ベース・アドレスが与えられると、

a

それ以上何もせずに a フィールドが得られます。

b

float+ すると b フィールドが得られます。

c

float+ cell+ faligned すると c フィールドが得られます。

これが非常に疲れる可能性があることは容易にわかります。

さらに加えて、 cell+ を見ても、 どの構造体がアクセスされているか、 どのフィールドがアクセスされているかがわからないため、 あまり読みやすくありません。 何らかの方法で構造体の種類を推測し、 その構造体のどのフィールドがそのオフセットに対応するかをドキュメントで調べる必要があります。

最後に、 この種のアドレス計算はメンテナンスの問題も引き起こします。 構造体の途中にフィールドを追加・削除した場合、 その後フィールドのすべてのアドレス計算を探し出して変更する必要があります。

そこで、 cell+ とそのファミリーを直接使用する代わりに、 以下のようにオフセットを定数に保存してはどうでしょうか?:

0 constant a-offset
0 float+ constant b-offset
0 float+ cell+ faligned c-offset

これで、 x-offset + を使用してフィールド x のアドレスを取得できるようになりました。 これはあらゆる点ではるかに優れています。 もちろん、 フィールドを追加する場合は、 その後のオフセット定義をすべて変更する必要があります。 これは、 以下の方法でオフセットを宣言することで改良できます:

0 constant a-offset
a-offset float+ constant b-offset
b-offset cell+ faligned constant c-offset

オフセット計算にはいつも + を使うので、 定義されたワードのアクションに + を含む定義ワード cfield を使用できます:

: cfield ( n "name" -- )
    create ,
does> ( name execution: addr1 -- addr2 )
    @ + ;

0 cfield a
0 a float+ cfield b
0 b cell+ faligned cfield c

今や、 x-offset + の代わりに、 単に x と書くことができるようになりました。

構造体フィールドのワード群が非常にうまく使用できるようになりました。 ただし、 その定義はまだ少し面倒です。 名前を繰り返す必要があり、 サイズと配置に関する情報はフィールド定義の前後に配置されます。 このセクションで紹介する構造体パッケージは、 これらの問題に対処します。


6.23.2 Structure Usage

以下のコマンドを使用して、 (データのない)リンク・リストの構造体を定義できます(訳注: これは構造体テンプレートを定義するだけです。 構造体変数とするには別途 %alloc 等する必要があります):

struct
    cell% field list-next
end-struct list%

スタック上のリスト・ノードのアドレスを使用して、 list-next を使用して次のノードのアドレスを含むフィールドのアドレスを計算できます。 たとえば、 以下のようにしてリストの長さを決定できます:

: list-length ( list -- n )
\ "list" is a pointer to the first element of a linked list
\ "n" is the length of the list
    0 BEGIN ( list1 n1 )
        over
    WHILE ( list1 n1 )
        1+ swap list-next @ swap
    REPEAT
    nip ;

list% %allot を使用すると、 ディクショナリー内にリスト・ノード用のメモリーを確保でき、 これにより、 リスト・ノードのアドレスがスタック上に残ります。ヒープ上で同様の割り当てを行うには、 list% %alloc を使用できます(または、allocate のようなスタック効果(つまり、ior を使用)が欲しい場合は、 list% %allocate を使用します)。 リスト・ノードのサイズは list% %size で取得でき、 そのセル・アライメントは list% %alignment で取得できます。

注意: 標準 Forth では、 create されたワードの本体は aligned されていますが、 必ずしも faligned されている訳ではない事に注意してください。したがって、 以下のようにすると:

create "name" foo% %allot drop

この場合、 foo% に割り当てられたメモリーは、 foo% に文字フィールドやセル・フィールドや2倍長整数フィールドのみが含まれている場合にのみ、 name の本体から開始されることが保証されます。 したがって、 浮動小数点数が含まれる場合は、以下を使用することをお勧めします

foo% %allot constant "name"

以下のように、 構造体 foo% を別の構造体のフィールドとして含めることができます:

struct
...
    foo% field ...
...
end-struct ...

構造体をいちから構築する代わりに、 既存の構造体を拡張できます。 たとえば、 上記例で定義したような、 データのない単純なリンク・リストはほとんど役に立ちません。 これを以下のように、 整数の値を持つリンク・リストに拡張できます:31

list%
    cell% field intlist-int
end-struct intlist%

intlist% は、 list-nextintlist-int の 2 つのフィールドを持つ構造体です。

以下のように、 n 要素の foo% 型を含む配列型を指定できます:

foo% n *

この配列型は、 通常の型を使用できる場所であればどこでも使用できます(例: field を定義する場所や %allot を使用するとき)。

最初のフィールドは構造体のベース・アドレスにあり、 この、 最初のフィールドのワード(例: list-next)は実際にはスタック上のアドレスを変更しません。 あなたは実行時間と領域の効率を考慮して、 最初のフィールドのワードを取り除きたいとと思うかもしれません。 しかし、 構造体パッケージがこの場合を最適化するため、 そのは必要ありません。 最初のフィールドのワードをコンパイルする場合、 コードは生成されません。 したがって、 読みやすさと保守性を考慮して、 最初のフィールドにアクセスするときにその最初のフィールドのワードは含めるべきです。


6.23.3 Structure Naming Convention

(構造体の命名規則)(私が)思いつくフィールド名は非常に汎用的なものが多く、 使用すると頻繁に名前の衝突が発生します。 たとえば、 多くの構造体にはたいてい counter フィールドが含まれています。 (私の)頭に浮かぶ構造体名は、 多くの場合、 そのような構造体を作成するワードの名前の論理的な選択でもあります。

したがって、 私は以下の命名規則を採用しました:

  • フィールド名の形式は struct-field です。 struct は構造体の基本名、field はフィールドの基本名です。 フィールド・ワードは、 構造体(のアドレス)をフィールド(のアドレス)に変換するものと考えることができます。
  • 構造体の名前は struct% の形式で、 struct は構造体の基本名です。

この命名規則は、 拡張構造体のフィールドではあまり機能しません。 たとえば、 上記例の整数リスト構造体にはフィールド intlist-int がありますが、 intlist-next ではなく list-next があります。


6.23.4 Structure Implementation

この実装の核となるアイデアは、 構築されている構造体に関するデータをグローバル変数ではなくスタックに渡すことです。 この設計上の決定が下されると、 他のすべては自然に配置されます。

スタック上の型の説明は align size の形式です。 サイズをスタックのTOSに維持すると、 配列の処理が非常に簡単になります。

field は、 CreateDOES> を使用する定義ワードです。 フィールドの本体にはフィールドのオフセットが含まれており、 通常の DOES> アクションは以下のようになります:

@ +

つまり、 アドレスにオフセットを加算して、 フィールドのスタック効果 addr1 – addr2 を与えます。

この単純な構造は、 オフセット 0 のフィールドの最適化によって少し複雑になります。 これには、 別の DOES> 部分が必要です(そのようなフィールドがコンパイル中に呼び出された場合、 スタック上に何かがあることに依存できないため)。 したがって、 異なる DOES> 部分を別々のワードに配置し、 オフセットに基づいてどれを呼び出すかを決定します。 ゼロ・オフセットの場合、 フィールドは基本的に noop です。 これは即実行ワードであるため、 コンパイル時にコードは生成されません。


6.23.5 Structure Glossary

%align ( align size –  ) gforth-0.4 “%align”

データ空間ポインターをアラインメント align に整列(align)します。

%alignment ( align size – align  ) gforth-0.4 “%alignment”

構造体のアライメント

%alloc ( align size – addr  ) gforth-0.4 “%alloc”

サイズ size のアドレス・ユニットをアラインメント align で整列(align)して割り当て、 割り当てたデータ・ブロックのアドレスを addr に返します。 成功しなかった場合は負数の iorthrow します。

%allocate ( align size – addr ior  ) gforth-0.4 “%allocate”

allocate と同様に、 サイズ size のアドレス単位をアライメント align で整列(align)して割り当て、 割り当てたデータ・ブロックのアドレスを addr に返します。 成功した場合、 ior=0, 成功しなかった場合は ior<0

%allot ( align size – addr  ) gforth-0.4 “%allot”

データ空間にサイズ size アドレス単位をアラインメント align で割り当てます。 結果のデータ・ブロックのアドレスを addr に返します。

cell% ( – align size  ) gforth-0.4 “cell%”
\訳注: セル1つ分のサイズ size と、 サイズをアライメントした align を返す
char% ( – align size  ) gforth-0.4 “char%”
\訳注: char 1つ分のサイズ size と、 そのサイズをアライメントした align を返します
dfloat% ( – align size  ) gforth-0.4 “dfloat%”
double% ( – align size  ) gforth-0.4 “double%”
\訳注: 2倍長整数1つ分のサイズ size と、サイズをアライメントした align を返します
end-struct ( align size "name" –  ) gforth-0.2 “end-struct”

アライメント align とサイズ size を使用して 構造体/型記述子 name を定義します(sizealign の倍数になるように切り上げられます – size1)。 name 実行時: ( – align size1)

field ( align1 offset1 align size "name" –  align2 offset2  ) gforth-0.2 “field”

オフセット offset1align size で指定された型を持つフィールド name を作成します。 offset2 は次のフィールドのオフセットで、align2 は、 (そこまでの)すべてのフィールドのアライメントです。 name の実行時: ( addr1addr2 ) addr2=addr1+offset1

float% ( – align size  ) gforth-0.4 “float%”
sfloat% ( – align size  ) gforth-0.4 “sfloat%”
%size ( align size – size  ) gforth-0.4 “%size”

構造体のサイズを返す

struct ( – align size  ) gforth-0.2 “struct”

空の構造体。 構造体定義を開始するために使用されます。


6.23.6 Forth200x Structures

Forth 2012 標準では、 やや不便な形式の構造体が定義されています。 一般に field+ を使用する場合は、 自分でアライメントを行う必要がありますが、 アライメント機能を含む便利なワード(例: field:)が多数あります。

典型的な使用例は以下のとおりです:

0
  field:                   s-a
  faligned 2 floats +field s-b
constant s-struct

この構造体を記述する別の方法は以下のとおりです:

begin-structure s-struct
  field:                   s-a
  faligned 2 floats +field s-b
end-structure

以下のように、 同一のフィールドと追加のフィールドを持つ構造体を定義できます:

s-struct
  cfield: t-c
  cfield: t-d
constant t-struct

あるいは、

s-struct extend-structure t-struct
  cfield: t-c
  cfield: t-d
end-structure
begin-structure ( "name" – struct-sys 0  ) facility-ext “begin-structure”
extend-structure ( n "name" – struct-sys n  ) gforth-1.0 “extend-structure”

サイズ n の既存の構造体の拡張として、 新しい構造体 name を開始します。

end-structure ( struct-sys +n –  ) facility-ext “end-structure”

begin-struction で開始された構造体を終了します

+field ( noffset1 nsize "name" – noffset2  ) facility-ext “plus-field”

定義ワード。 name ( addr1 -- addr2 ) を定義します。 ここで、 addr2addr1+noffset1 です。 noffset2noffset1+nsize です。

cfield: ( u1 "name" – u2  ) facility-ext “c-field-colon”

文字サイズのフィールドを定義します

field: ( u1 "name" – u2  ) facility-ext “field-colon”

アライメントされたセル・サイズのフィールドを定義します

2field: ( u1 "name" – u2  ) gforth-0.7 “two-field-colon”

アライメントされた2倍長セル・サイズのフィールドを定義します

ffield: ( u1 "name" – u2  ) floating-ext “f-field-colon”

アライメントされた浮動小数点サイズのフィールドを定義します

sffield: ( u1 "name" – u2  ) floating-ext “s-f-field-colon”

sfaligned された sfloat サイズのフィールドを定義します

dffield: ( u1 "name" – u2  ) floating-ext “d-f-field-colon”

dfaligned された dfloat サイズのフィールドを定義します

wfield: ( u1 "name" – u2  ) gforth-1.0 “w-field-colon”

16 ビット値のアライメントされたフィールドを定義します。

lfield: ( u1 "name" – u2  ) gforth-1.0 “l-field-colon”

32ビット値にアライメントされたフィールドを定義します。

xfield: ( u1 "name" – u2  ) gforth-1.0 “x-field-colon”

64ビット値にアライメントされたフィールドを定義します。


6.24 Object-oriented Forth

Gforth には、オブジェクト指向プログラミング用の 3 つのパッケージ (objects.fsoof.fsmini-oof.fs) が付属しています。 どれも最初から組み込まれていないため、 使用する前にインクルードする必要があります。 これらのパッケージ(および、 その他のパッケージ)の最も重要な違いについては、 Comparison with other object models で説明します。 すべてのパッケージは 標準 Forth で書かれており、 他の 標準 Forth でも使用できます。


6.24.1 Why object-oriented programming?

多くの場合、 いくつかのデータ構造「オブジェクト」(object)を扱わなければなりません。 それらは、 いつくかの側面では同様に扱う必要がありますが、 それ以外の側面では異なる扱いをしなければなりません。 グラフィカル・オブジェクトとは教科書的な例で言えば、 円や三角形や恐竜の絵やアイコン等ですが、 プログラム開発中にさらに追加することもできます。 あなたが、 任意のグラフィカル・オブジェクトにいくつかの操作を適用したいとしましょう。 たとえば、 画面上に表示するための draw 操作です。 しかしながら、 この draw はオブジェクトの種類ごとに異なる処理を行う必要があります。

draw を、 描画されるオブジェクトの種類に依存して適切なコードを実行する、 大きな CASE 制御構造として実装することはできます。 これはあまり洗練されたものではなく、 さらに、 新しい種類のグラフィック・オブジェクト(例えば宇宙船など)を追加するたびに draw を変更する必要があります。

私たちがやりたいことは、 宇宙船を定義するときにシステムに次のように指示することです: 「宇宙船を draw する方法は私たちがこれこれこのとおり書いたので、 それ以外の処理はシステム側でよしなしにしてください」

これは、 (当然ながら、 )オブジェクト指向と呼ばれるすべてのシステムで解決すべき問題です。 ここで紹介するオブジェクト指向パッケージは、 この問題を解決します(それ以外の問題はあんまり解決できません…)。


6.24.2 Object-Oriented Terminology

このセクションは主にリファレンスであるため、 すぐにすべてを理解する必要はありません。 用語は主に Smalltalk からインスピレーションを得たものです:

クラス(class)

いくつかの追加機能を備えたデータ構造定義。

オブジェクト(object)

クラス定義によって記述されたデータ構造の実体(インスタンス;instance)。

インスタンス変数(instance variables)

データ構造のフィールド。

セレクター(selector)

(または「メソッド・セレクター」)さまざまなデータ構造(クラス)に対して操作を実行するワード(例: draw)。 セレクターは、 「何の」(what)操作を行うかを記述します。 C++ 用語では (純粋)仮想関数 と言います

メソッド(method)

特定のクラスのセレクターによって記述された操作を実行する具体的な定義。 メソッドは、 特定のクラスに対して「どのように」(how)操作が実行されるかを指定します。

セレクター呼び出し(selector invocation)

セレクターの呼び出し。 呼び出しの 1 つの引数(TOS(スタックの頂上))は、 どのメソッドが使用されるかを決定するために使用されます。 Smalltalk の用語では、 (セレクターとその他の引数で構成される、)メッセージがオブジェクトに送信される と言います。

受信オブジェクト(receiving object)

セレクターの呼び出しによって実行されるメソッドを決定するために使用されるオブジェクト。 objects.fs モデルでは、 セレクターが呼び出されたときに TOS 上にあるオブジェクトです。 (「受信」という言葉は、 Smalltalk の 「メッセージ」関連用語由来です。)

子クラス(child class)

「親クラス」のすべてのプロパティ(インスタンス変数やセレクターやメソッド)を「継承」(inherit)したクラス。 Smalltalk の用語では、 サブクラスはスーパークラスを継承します、 と言います。 C++ 用語では、 派生クラスは基底クラスから継承します(The derived class inherits from the base class.)、 と言います。


6.24.3 The objects.fs model

このセクションでは、 objects.fs パッケージについて説明します。 この資料は、 M. Anton Ertl, Yet Another Forth Objects Package, Forth Dimensions 19(2), pages 37–43 でも公開されています。

このセクションは、 Structures を読了済であることを前提としています。

このモデルの基礎となっている技術は、 パーサ・ジェネレーター Gray の実装に使用されており、 Gforth でもさまざまな種類のワードリスト(ハッシュの有無や、大文字と小文字の区別の有無や、 ローカル変数用などの特殊用途のワードリスト)を実装するために使用されています)。

Marcel Hendrix は、 このセクションに関して役立つコメントを提供しました。


6.24.3.1 Properties of the objects.fs model

  • オブジェクトをスタックに渡すのは簡単明瞭です。 スタック上でセレクターを渡すのは多少めんどくさいですが、 可能です。
  • オブジェクトはメモリー内の単なるデータ構造であり、 そのアドレスによって参照されます。 constant のような通常の定義ワードを使用してオブジェクトのワードを作成できます。 同様に、 オブジェクトを含むインスタンス変数と他のデータを含むインスタンス変数の間に違いはありません。
  • 遅延結び付け(Late binding)は効率的で使いやすいです。
  • 遅延結び付け(Late binding)によりパースが回避されるため、 状態スマート性(state-smartness)や拡張性の低下に関する問題が回避されます。 便宜上、 いくつかのパース・ワードがありますが、 それらには非パース・ワードもあります。 パースする定義ワードもいくつかあります。 すべての標準の定義ワードは(:noname を除いて)パースするため、 これを避けるのは困難です。 ただし、 このようなワードは状態スマート(state-smart)ではないため、 他の多くのパース・ワードほど悪くはありません。
  • このモデルではすべてを取り入れようとしているわけではありません。 (著者の私見ですけども)このモデルでは、 いくつかのことをうまくやるといった体です。 特に、 このモデルでは情報の隠蔽をサポートするように設計されていません(情報の隠蔽に役立つ可能性のある機能はありますが)。 これを実現するには、また別のパッケージが使用可能です。
  • このモデルは階層構造((layered))になっています。 このモデルを使用するために下位階層まですべての機能を学習して使用する必要はありません。 必要な機能はほんのわずかです((see Basic objects.fs Usage, see The object.fs base class, see Creating objects)。 その他の機能はオプションであり、 互いに独立しています。
  • gforth 固有ではなく、 標準 Forth 用の実装が利用可能です。

6.24.3.2 Basic objects.fs Usage

以下のようにして graphical オブジェクト(図形オブジェクト)のクラスを定義できます:

object class \ クラス名 "object" は親クラスです
  selector draw ( x y graphical -- )
end-class graphical

このコードは、 draw 操作を持つクラス graphical を定義します。 任意の graphical オブジェクトに対して draw 操作を実行できます。例:

100 100 t-rex draw

ここで、 t-rex は、 graphical オブジェクトを生成するワード(定数(constant)など)です。

graphical オブジェクトを作成するにはどうすればよいでしょうか? 現在の定義では、 有用な graphical オブジェクトを作成できません。 クラス graphical は graphical オブジェクト一般を記述しますが、 具体的な graphical オブジェクト・タイプを記述しません(C++ ユーザーはこれを「抽象クラス」(abstract class)と呼びます)。 たとえば、 クラス graphical にはセレクター draw のメソッドがありません。

具体的な graphical オブジェクトのために、 クラス graphical の子クラスを定義します。 例:

graphical class \ 親クラスは graphical
  cell% field circle-radius

:noname ( x y circle -- )
  circle-radius @ draw-circle ;
overrides draw

:noname ( n-radius circle -- )
  circle-radius ! ;
overrides construct

end-class circle

ここでは、 フィールド circle-radius を持つクラス circlegraphical の子として定義しています(フィールド circle-radius は構造体のフィールドと同じように動作します(see Structures)。 セレクター drawconstruct 用の新しいメソッドを定義します(constructgraphical の親クラスの object クラスで定義されています))。

以下のようにして、 ヒープ上(つまり、 allocate されたメモリー)に circle を作成できます:

50 circle heap-new constant my-circle

heap-newconstruct を呼び出し、 フィールド circle-radius を 50 で初期化します。 以下のようにして、 この新しい円を (100,100) の位置に描画(draw)できます:

100 100 my-circle draw

注意: セレクターを呼び出すことができるのは、 TOS 上のオブジェクト(受信オブジェクト) が、 セレクターが定義されたクラス、 またはその子孫の 1 つに属している場合のみです。 たとえば、 draw は、 graphical またはその子孫(例: circle)に属するオブジェクトに対してのみ呼び出すことができます。 end-class の直前の検索順序スタック(the search order)は、 class の直後と同じである必要があります。


6.24.3.3 The object.fs base class

あなたがクラスを定義するときは、 必ず親クラスを指定する必要があります。 では、 (最初の)クラス定義はどのようにすればよいのでしょうか? そのために最初から使用できるクラスが 1 つだけあります。 それは object という名前のクラスです。 これはすべてのクラスの祖先であるため、 親を持たない唯一のクラスです。 そして、 constructprint という 2 つのセレクターを持っています。


6.24.3.4 Creating objects

heap-new ( ... class – object ) を使用するとヒープ上にクラスのオブジェクトを作成して初期化することができ、 dict-new ( ... class – object ) を使用するとディクショナリー内(allot による割り当て)にクラスのオブジェクトを作成して初期化することができます。 どちらのワードも当該クラス classconstruct を呼び出し、 当該クラス classconstruct のスタック効果(上記「...」の部分)で示されたスタック項目を消費します(訳注: 例えば 6.24.3.2 Basic ‘objects.fs’ Usage の例のように、 circleconstruct のスタック効果 n-radius が必要で、 50 circle heap-new constant my-circle としなければならない)。

オブジェクトに自分でメモリーを割り当てたい場合は、 class-inst-size 2@ ( class – align size ) を使用してクラスのアライメント(alignment)とサイズを取得できます。 オブジェクト用のメモリーを確保したら、 init-object ( ... class object – )でオブジェクトを初期化できます(訳注: class のためのデータ構造を object からに構築し、 その後そのオブジェクトに対して construct を実行します。 注意: 当該 classconstruct 用のスタック項目の指定も必要な事に注意)。


6.24.3.5 Object-Oriented Programming Style

このセクションはすべてを網羅したものではありません。

一般に、 同一のセレクターのすべてのメソッドが同一スタック効果を持つようにするのは良いアイデアです。 セレクターを呼び出すとき、 どのメソッドが呼び出されるのかわからないことが多いため、 すべてのメソッドが同じスタック効果を持たない限り、 セレクター呼び出しのスタック効果を知ることはできません。

このルールには例外がひとつあります。 セレクター construct のメソッドです。 同一の場所に構築するクラスを指定しているため、 どのメソッドが呼び出されるのかがわかります。 実際、 著者はユーザーに初期化を指定する便利な方法を提供するためだけに construct をセレクターとして定義しました。 使用方法としては、 セレクター呼び出しとは異なるメカニズムの方が自然です(ただし、 おそらく説明するにはより多くのコードとスペースが必要になります)。


6.24.3.6 Class Binding

通常のセレクター呼び出し(selector invocation)では、 受信オブジェクト(receiving object)のクラスに応じて実行時(run-time)にメソッドが決定されます。 この実行時の選択(selection)は「遅延結び付け」(late binding)と呼ばれます。

場合によっては、 別のメソッドを呼び出すことが望ましい場合があります。 たとえば、 出力オブジェクト(printing objects)では、 受信用クラス(receiver class)の冗長になりがちな print メソッドの代わりに、 単純なメソッドを使用したい事があります。 これを実現するには、 print の呼び出しを以下のように置き換えます(コンパイル時の場合):

[bind] object print

または、 インタープリター時は以下のようにします:

bind object print

あるいは、 メソッドを名前(例: print-object)で定義し、 その名前を使用して呼び出すこともできます。 クラス結び付け(Class binding)は、 同じ効果を達成する(多くの場合、 より便利な)方法にすぎません。 これにより、 名前の乱雑さが回避され、 最初に名前を付けずにメソッドを直接呼び出すことができます。

クラス結び付け(class binding)のよくある使用法は次のとおりです: セレクターのメソッドを定義するとき、 親クラスでセレクターが行っていることと、 それ以上のことをメソッドに実行させたいことがよくあります。 この目的には、 [parent] という特別なワードがあります。 [parent] "selector"[bind] "parent selector" と同等です。 ここで、parent は現在のクラスの親クラスです。 たとえば、メソッド定義は以下のようになります:

:noname
  dup [parent] foo \ 受信オブジェクトに対して親の foo を実行します
  ... \ (親の foo に加えて)更に何かする
; overrides foo

Object-oriented programming in ANS Forth (Forth Dimensions, March 1997) で Andrew McKewan は最適化手法としてクラス結び付け(class binding)を紹介しています。 著者は緊急の場合を除き、 最適化手法の目的で使用しないことをお勧めします。 とにかく、 このモデルでは遅延結び付け(Late binding)が非常に高速であるため、 クラス結び付け(class binding)を使用するメリットは小さいです。 適切でない場合にクラス結び付け(class binding)を使用すると、 保守性が低下します。

プログラミング・スタイルの質問については次のとおりです。 セレクターは受信オブジェクト(receiving object)の祖先クラス(ancestor classes)にのみ結び付け(bind)すべきです。 たとえば、 受信オブジェクトがクラス foo またはその子孫であることがわかっているとします。 その場合は、 foo とその祖先にのみ結び付け(bind)するべきです。


6.24.3.7 Method conveniences

【メソッドをより便利に】通常、 メソッドでは受信オブジェクト(receiving object)に頻繁にアクセスします。 メソッドをプレーンなコロン定義(:noname など)として定義する場合、 多くのスタック体操が必要になる場合があります。 これを回避するには、 m: ... ;m を使用してメソッドを定義します。たとえば、 以下を使用して circledraw するメソッドを定義できます

m: ( x y circle -- )
  ( x y ) this circle-radius @ draw-circle ;m

このメソッドが実行されるときは、 受信オブジェクト(receiver object)(上記例の circle)がスタックから取り除かれます。 その代わりに、 m:;m の間は this を使用して受信オブジェクトにアクセスできます(ええ、 確かに、この例では m: ... ;m を使用する利点ないかもしれませんね)。 注意: しかし、 スタック・コメントとしては m:;m の間のコードだけでなく、 メソッド全体(つまり、 受信オブジェクトを含むメソッド全体)のスタック効果を指定していることに注意してください。 なお、 m:...;m では exit を使用できません。 代わりに、 exitm を使用してください32

あなたは this "field" という形式のシーケンスを頻繁に使用するハメになると思います(上記例では: this circle-radius)。 そこで、 この方法でのみフィールドを使用する場合は、 inst-var を使用してフィールドを定義し、 フィールド名の前の this を削れます。 たとえば、 上記例の circle クラスは以下のように定義することもできます:

graphical class
  cell% inst-var radius

  m: ( x y circle -- )
    radius @ draw-circle ;m
  overrides draw

  m: ( n-radius circle -- )
    radius ! ;m
  overrides construct

end-class circle

radius は、 circle や、 その子孫クラスと、 それらの m:...;m 内でのみ使用できます。

inst-value を使用してフィールドを定義することもできます。 これは、 variable に対して value があるのと同様に、 inst-var に対して inst-value があるのです。 このようなフィールドの値は [to-inst] を使用して変更できます。 たとえば、 クラス circle を以下のように定義することもできます:

graphical class
  inst-value radius

  m: ( x y circle -- )
    radius draw-circle ;m
  overrides draw

  m: ( n-radius circle -- )
    [to-inst] radius ;m
  overrides construct

end-class circle

6.24.3.8 Classes and Scoping

構造体の拡張とは異なり、 継承は頻繁に行われます。 これは、フィールド名命名規則の問題をさらに悪化させます(see Structure Naming Convention): フィールドが最初にどのクラスで定義されたかを常に覚えておく必要があります。 クラス構造の一部を変更すると、 影響を受けないコードの名前も変更する必要があります。

この問題を解決するために、 著者は(著者のオリジナルのモデルにはなかった)スコープ・メカニズムを追加しました。 inst-var (または inst-value)で定義されたフィールドは、 それが定義されているクラスと、 そのクラスの子孫のクラスでのみ可視です。 このようなフィールドの使用は、 いずれにしても、 これらのクラスの m: で定義されたメソッドでのみ意味があります。

このスコープ・メカニズムにより、 名前が無関係なワードと衝突する可能性が大幅に低くなるため、 着飾っていないフィールド名を使用できます。

スコープ・メカニズムがあれば、 他のワードの可視性を制御するために使用することもできます。 protected の後に定義されたすべてのワードは、 現在のクラスとその子孫でのみ表示されます。 public は、 以前に有効だった the current wordlist (新しく定義されたワードを入れるワードリスト)(つまり ユーザー変数 current)を復元します。 public または set-current が介在せずに複数の protected がある場合、 public はこれらの最初の protected より前の有効な the current wordlist を復元します。


6.24.3.9 Dividing classes

メソッドの定義を、 クラスや、そのセレクターや、 フィールドや、 インスタンス変数、 の定義とは別に行う、 つまり、 実装を定義から分離することもできます。 これは以下の方法で行うことができます:

graphical class
  inst-value radius
end-class circle

... \ do some other stuff

circle methods \ now we are ready

  m: ( x y circle -- )
    radius draw-circle ;m
  overrides draw

  m: ( n-radius circle -- )
    [to-inst] radius ;m
  overrides construct

end-methods

複数の methods...end-methods セクションを使用できます。 これらのセクションでクラスに対して実行できることは、 メソッドの定義とクラスのセレクターのオーバーライドのみです。 新しいセレクターやフィールドを定義してはいけません。

注意: 多くの場合、 セレクターを使用する前にオーバーライドする必要があることに注意してください。 特に、 heap-new とその他のメソッドを呼び出す前に、 通常は construct を新しいメソッドでオーバーライドする必要があります。 たとえば、 上記例の overrides construction シーケンスの前に circle を作成してはなりません。


6.24.3.10 Object Interfaces

このモデルでは、 受信オブジェクト(receiving objects)のクラスまたはその祖先の 1 つに定義されたセレクターのみを呼び出すことができます。 これらのクラスのいずれにも属さない受信オブジェクトを使用してセレクターを呼び出した場合、 結果は未定義になります。 あなたの運が良ければ、 プログラムは即座にクラッシュします。

ここで、 2 つのクラスで 1 つまたは複数のセレクターを使用できるようにしたい場合を考えてみましょう。 セレクターを共通の祖先クラスに追加する必要があり、 最悪の場合は object に追加する必要があります。 たとえば、 他の誰かがこの祖先クラスに対して責任を負っているなどの理由で、 共通の祖先クラスへのセレクターの追加を実行したくない場合もあります。

この問題の解決策はインターフェイス(interface)です。 インターフェイスはセレクターのコレクション(collection)です。 クラスがインターフェイスを実装(implement)している場合、 そのインターフェイスのセレクターはそのクラスとその子孫のクラスで使用できるようになります。 クラスは無制限の数のインターフェイスを実装できます。 上で説明した問題については、 セレクター達の為にインターフェイスを定義し、 両方のクラスでそのインターフェイスを実装します。

例として、 オブジェクトをディスクに書き込んで戻すための storage というインターフェイスと、 それを実装するクラス foo について考えてみましょう。 そのコードは以下のようになります:

interface
  selector write ( file object -- )
  selector read1 ( file object -- )
end-interface storage

bar class
  storage implementation

  ... overrides write
  ... overrides read1
  ...

end-class foo

(著者は、 ここから更に read1 を内部的に使用するワード read ( file – object ) を追加するのですが、 それはインターフェイスとは関係ないので、 ここでは説明を割愛します。 )

注意: インターフェイスでは protected を使用できないことに注意してください。 もちろん、 フィールドを定義することもできません。

Neon モデルでは、 すべてのセレクターがすべてのクラスで使用できます。 したがってインターフェイスは必要ありません。 Neon モデルで支払う代償は、 遅延結び付け(late binding)が遅くなるため、 遅延結び付けを回避するために複雑さが増すことです。


6.24.3.11 objects.fs Implementation

オブジェクトは、 struct...end-struct で記述されたデータ構造体の 1 つである、 メモリーの一片です。 これは、 そのオブジェクトのクラスのメソッド・マップ(the method map) を指すフィールド object-map を持っています。

「メソッド・マップ」(method map)33は、 そのオブジェクトのクラスのメソッドの実行トークン(xt)を含む配列です。 各セレクターには、 メソッド・マップへのオフセットが含まれています。

selector は、CREATEDOES> を使用する定義ワードです。 selector の本体の内容はメソッド・マップのオフセット値です。 クラスのセレクターの DOES> アクションは、 基本的には以下のとおりです(訳注: メソッド・マップの実装上のフィールド名は "object-map"):

( object addr ) @ over object-map @ + @ execute

なお、 object-map はオブジェクトの最初のフィールドであるため、 コードは生成されません。 ご覧のとおり、 セレクターの呼び出しには小さいけれども一定のコストがかかります。

クラスは基本的に struct とメソッド・マップを組み合わせたものです。 struct の場合と同様に、 クラス定義中に、 クラスのアライメントとサイズがスタックに渡されるため、 field をクラスのフィールドの定義にも使用できます。 ただし、 スタックにさらに多くの項目を渡すと不便になるため、 class はメモリー内にデータ構造を構築し、 変数 current-interface を通じてアクセスします。 定義が完了すると、 クラスはポインター(たとえば、 子クラス定義のパラメーターとして利用したりする)としてスタックに置かれます。

新しいクラスは、 その親のアライメントとサイズ、 およびその親のメソッド・マップのコピーから始まります。 新しいフィールドを定義すると、 サイズとアライメントが拡張されます。 同様に、 新しいセレクターを定義すると、 メソッド・マップが拡張されます。 overrides は、 セレクターによって指定されたオフセットでメソッド・マップに新しい xt を保存するだけです。

クラス結び付け(class binding)は、 そのクラスのメソッド・マップからセレクターによって指定されたオフセットで xt を取得し、 それをコンパイル(compile,)するだけです([bind] の場合)。

著者は thisvalue として実装しました。 m:...;m メソッドの開始時に、 古い this がリターン・スタックに保存され、 最後に復元されます。 TOS 上のオブジェクトは TO this で保存します。 この手法には欠点が 1 つあります。 ユーザーが ;m 経由ではなく throw または exit 経由でメソッドを終了した場合、 this は復元されません(そして exit がクラッシュする可能性があります)。 著者は throw の問題に対処するために、 this を保存および復元するために catch を再定義しました(訳注: "objects.fs" をインクルードした時に "redefined catch" と警告が出るが、 意図的に再定義してあるので無視してください、 ということ)。 例外をキャッチできるワードについても全く同様に行うべきです。 exit については、 単純に使用を禁止します(代わりに exitm を用意しました)。

inst-varfield とまったく同じですが、 DOES> アクションが異なります:

@ this +

inst-value も同様です。

各クラスは、 inst-varinst-value で定義されたワードや、 それらの protected されたワードを含む、 ワードリストを持っています。 また、 その親へのポインターも持っています。 class は、 クラスとそのすべての祖先のワードリストを検索順序スタック(the search order)にプッシュし、 end-class はそれらをスタックから drop します。

インターフェイスは、 フィールドと親と protected されたワードのないクラスに似ています。 つまり、 メソッド・マップがあるだけです。 クラスがインターフェイスを実装する場合、 そのメソッド・マップにはインターフェイスのメソッド・マップへのポインターが含まれます。 マップ内の正のオフセットはクラス・メソッド用に予約されているため、 インターフェイス・マップ・ポインターは負のオフセットを持ちます。 クラス・セレクターとは異なり、 インターフェイスにはシステム全体で一意のオフセットがあります。 クラス・セレクターのオフセットは、 セレクターが利用可能な(呼び出し可能な)クラスに対してのみ一意です。

この構造は、 インターフェイス・セレクターがメソッドを見つけるために、 クラス・セレクターよりも 1 つ多い間接参照を実行する必要があることを意味します。 その本体には、 クラス・メソッド・マップ内のインターフェイス・マップ・ポインター・オフセットと、 インターフェイス・メソッド・マップ内のメソッド・オフセットが含まれています。 インターフェイス・セレクターの does> アクションは、 基本的には以下のとおりです:

( object selector-body )
2dup selector-interface @ ( object selector-body object interface-offset )
swap object-map @ + @ ( object selector-body map )
swap selector-offset @ + @ execute

ここで、 object-mapselector-offset は最初のフィールドであり、 コードは生成されません。

具体的な例として、 以下のコードについて考えてみましょう:

interface
  selector if1sel1
  selector if1sel2
end-interface if1

object class
  if1 implementation
  selector cl1sel1
  cell% inst-var cl1iv1

  ' m1 overrides construct
  ' m2 overrides if1sel1
  ' m3 overrides if1sel2
  ' m4 overrides cl1sel2
end-class cl1

create obj1 object dict-new drop
create obj2 cl1    dict-new drop

このコードで作成されたデータ構造 (object のデータ構造を含む) は、セル・サイズ 4 を想定して figure に図示されています。


6.24.3.12 objects.fs Glossary

bind ( ... "class" "selector" – ...  ) objects “bind”

指定のクラス class の 指定セレクター selector のメソッドを execute します。

<bind> ( class selector-xt – xt  ) objects “<bind>”

xt は、 指定クラス class のセレクター selector-xt のメソッドです。

bind' ( "class" "selector" – xt  ) objects “bind”’

xt は 指定のクラス class の 指定のセレクター selector のメソッドです。

[bind] ( compile-time: "class" "selector" – ; run-time: ... object – ...  ) objects “[bind]”

指定のクラス class の 指定のセレクター selector のメソッドをコンパイルします。

class ( parent-class – align offset  ) objects “class”

parent-class の子として新しいクラス定義を開始します。 スタックに積まれた align offsetfield などで使用されます。

class->map ( class – map  ) objects “class->map”

指定のクラス class のメソッド・マップへのポインターを map に返します。

class-inst-size ( class – addr  ) objects “class-inst-size”

指定のクラス class のインスタンス(つまり、 オブジェクト)のサイズ仕様を指します。 通常 class-inst-size 2@ ( class -- align size ) として使用されます。

class-override! ( xt sel-xt class-map –  ) objects “class-override!”

指定の xt は、 指定の class-map のセレクター sel-xt の新しいメソッドです。

class-previous ( class –  ) objects “class-previous”

指定の class のワードリストを検索順序スタック(the search order)から drop します。 class のワードリストが実際に検索順序スタックに含まれているかどうかのチェックは行われません。

class>order ( class –  ) objects “class>order”

クラス class のワードリストを検索順序スタック(the search-order)の先頭に追加します。

construct ( ... object –  ) objects “construct”

指定の object のデータ・フィールドを初期化します。 基底クラス objectconstruct メソッドは何も行いません: ( object -- ).

current' ( "selector" – xt  ) objects “current”’

xt は、 現在のクラスの指定の selector のメソッドです。

[current] ( compile-time: "selector" – ; run-time: ... object – ...  ) objects “[current]”

現在のクラスの selector のメソッドをコンパイルします。

current-interface ( – addr  ) objects “current-interface”

変数(Variable): 現在定義中のクラスまたはインターフェイスが含まれます。

dict-new ( ... class – object  ) objects “dict-new”

指定のクラス class オブジェクト用の領域を allot を使用してディクショナリ内に確保し、 初期化(init-object)したオブジェクトへのポインターを object に返します(訳注: object は領域の先頭ではなくてメソッド・マップ(object-map)フィールドを指している事に注意)

end-class ( align offset "name" –  ) objects “end-class”

name という名前を付けてクラス定義を終了します。 name 実行時: -- class

end-class-noname ( align offset – class  ) objects “end-class-noname”

クラス定義を終了します。 結果のクラスは class です。

end-interface ( "name" –  ) objects “end-interface”

name という名前を付けてインターフェイス定義を終了します。 name の実行時: --interface

end-interface-noname ( – interface  ) objects “end-interface-noname”

インターフェイス定義を終了します。 結果のインターフェイスは interface

end-methods ( ) objects “end-methods”

クラスのメソッド定義から通常モードに切り替えます(現在、 これは古い検索順序スタックを復元するだけです)。

exitm ( ) objects “exitm”

メソッドから exit します。 古い this を復元します。

heap-new ( ... class – object  ) objects “heap-new”

allocate し、 クラス class のオブジェクトを初期化します。

implementation ( interface –  ) objects “implementation”

現在のクラスは指定のインターフェイス interface を実装します。 つまり、 あなたは、 現在のクラス内とその子孫のクラス内で、 指定のインターフェイス interface の全てのセレクターを使用できます。

init-object ( ... class object –  ) objects “init-object”

メモリーのチャンク object (アドレス)をクラス class のオブジェクトのために初期化します。 それから construct を実行します(訳注: classconstruct 用のスタック項目も必要な事に注意。 6.24.3.2 Basic ‘objects.fs’ Usage の例 circle で言えば n-radius が必要で(my-circle2 というメモリー領域をクラス・オブジェクトにするとして、) 50 circle my-circle-2 init-object としなければならない)

inst-value ( align1 offset1 "name" – align2 offset2  ) objects “inst-value”

align2 offset2w のサイズだけ足し込んだ値です。 name の実行時: -- w w は、this オブジェクトの name フィールドの値です。

inst-var ( align1 offset1 align size "name" – align2 offset2  ) objects “inst-var”

addr は、this オブジェクトのフィールド name のアドレスです。 name の実行時: -- addr

interface ( ) objects “interface”

インターフェイス定義を開始します。

m: ( – xt colon-sys; run-time: object –  ) objects “m:”

メソッド定義を開始します。 定義中は指定の object が新しい this になります。

:m ( "name" – xt; run-time: object –  ) objects “:m”

名前付きメソッド定義を開始します。 定義中は指定の object が新しい this になります。 ;m で終わらせる必要があります。

;m ( colon-sys –; run-time: –  ) objects “;m”

メソッド定義を終了します。 古い this を復元します。

method ( xt "name" –  ) objects “method”

セレクター name を作成し、xt を現在のクラスのメソッドにします。 name の実行時: ... object -- ...

methods ( class –  ) objects “methods”

class を現在のクラスにします。 これは、 セレクターをオーバーライドするメソッドを定義するために使用することを目的としています。 新しいフィールドやセレクターを定義することはできません。

object ( – class  ) objects “object”

すべてのクラスの祖先。

overrides ( xt "selector" –  ) objects “overrides”

現在のクラスの selector のデフォルト・メソッドを xt に置き換えます。 overrides はインターフェイス定義中に使用してはいけません。

[parent] ( compile-time: "selector" – ; run-time: ... object – ...  ) objects “[parent]”

現在のクラスの親の selector のメソッドをコンパイルします。

print ( object –  ) objects “print”

オブジェクトを出力します。 指定のクラス object のメソッドは、 オブジェクトのアドレスとそのクラスのアドレスを出力します。

protected ( ) objects “protected”

現在のクラスのワードリストをコンパイル・ワードリスト(the compilation wordlist)にセットします

public ( ) objects “public”

実際にコンパイル・ワードリスト(the compilation wordlist)を変更した最後の protected より前に有効だったコンパイル・ワードリストを復元します。

selector ( "name" –  ) objects “selector”

現在のクラスとその子孫のセレクター name を作成します。 overrides を使用して、 現在のクラスのセレクターのメソッドを設定できます。 name の実行時: ... object -- ...

this ( – object  ) objects “this”

現在のメソッドの受信オブジェクト(receiving object)(別名アクティブ・オブジェクト)。

<to-inst> ( w xt –  ) objects “<to-inst>”

実行トークン xt を inst-value で作成したフィールドとみなしてフィールド・オフセットを取得し、 現在のオブジェクト(this)の当該フィールドに指定の値 w を格納します。

[to-inst] ( compile-time: "name" – ; run-time: w –  ) objects “[to-inst]”

wthis オブジェクトの name フィールドに格納します。

to-this ( object –  ) objects “to-this”

this を設定します(内部的に使用されるものですが、 デバッグ時に役立ちます)。

xt-new ( ... class xt – object  ) objects “xt-new”

xt ( align size -- addr ) を使用して何らかの形で領域を確保し、 そこに指定のクラス class の新しいオブジェクトを作成(init-object)します。


6.24.4 The oof.fs model

このセクションでは、 oof.fs パッケージについて説明します。

このセクションで説明するパッケージは、 1991 年以来 bigFORTH で使用されており、 新薬作成に使用されるクロマトグラフィー・システムと、グラフィック・ユーザー・インターフェイス・ライブラリー (MINOS) という 2 つの大きなアプリケーションに使用されています。

oof.fs の説明 (ドイツ語) は、Vierte Dimension 10(2), 1994 に掲載された Bernd Paysan による Object Oriented bigFORTH にあります。


6.24.4.1 Properties of the oof.fs model

  • このモデルは、 オブジェクト指向プログラミングと情報隠蔽を組み合わせたものです。 クラス指向のスコープが提供されるため、 スコープが必要な大規模なアプリケーションを作成するのに役立ちます。
  • 名前付きオブジェクトや、 オブジェクト・ポインターや、 オブジェクト配列を作成できます。 セレクターの呼び出しには「オブジェクト・セレクター」構文が使用されます。 スタック上のオブジェクトやセレクターに対するセレクターの呼び出しはあまり便利ではありませんが、 可能です。
  • セレクターの呼び出しと、 アクティブ・オブジェクトのインスタンス変数の使用は、 どちらもアクティブ・オブジェクトを使用するため、 簡単です。
  • 遅延結び付け(Late binding)は効率的で使いやすいです。
  • ステートスマート・オブジェクトはセレクターをパースします。 ただし、(パース) セレクター postpone とセレクター ' を使用して拡張性が提供されます。
  • gforth 固有ではなく、 標準 Forth 用の実装が利用可能です。

6.24.4.2 Basic oof.fs Usage

このセクションでは、 objects (see Basic objects.fs Usage) と同じ例を使用します。

以下のようにして graphical オブジェクト(図形オブジェクト)のクラスを定義できます:

object class graphical \ "object" is the parent class
  method draw ( x y -- )
class;

このコードは、 draw 操作を持つクラス graphical を定義します。 任意の graphical オブジェクトに対して draw 操作を実行できます。例:

100 100 t-rex draw

ここで、 t-rex (訳注: 恐竜ティラノサウルス) はオブジェクトまたはオブジェクト・ポインターであり、 たとえば次のように作成されます: graphical : t-rex

graphical オブジェクトを作成するにはどうすればよいでしょうか? 現在の定義では、 有用な graphical オブジェクトを作成できません。 クラス graphical は graphical オブジェクト一般を記述しますが、 具体的な graphical オブジェクト・タイプを記述しません(C++ ユーザーはこれを「抽象クラス」(abstract class)と呼びます)。 たとえば、 クラス graphical にはセレクター draw のメソッドがありません。

具体的な graphical オブジェクトのために、 クラス graphical の子クラスを定義します。 例:

graphical class circle \ "graphical" is the parent class
  cell var circle-radius
how:
  : draw ( x y -- )
    circle-radius @ draw-circle ;

  : init ( n-radius -- )
    circle-radius ! ;
class;

ここでは、 クラス circlegraphical の子として、 フィールド circle-radius とともに定義します。 セレクター draw および init の新しいメソッドが定義されています(init は、 graphical の親クラスである object で定義されています)。

いまや、 以下のコマンドを使用してディクショナリー内に circle を作成できます:

50 circle : my-circle

:init を呼び出し、 フィールド circle-radius を 50 で初期化します。 以下のようにして、この新しい circle を (100,100) に描画できます:

100 100 my-circle draw

注意: セレクターを呼び出すことができるのは、 受信オブジェクトが、 セレクターが定義されたクラス、またはその子孫の 1 つに属している場合のみです。 たとえば、 draw は、 graphical またはその子孫(例: circle)に属するオブジェクトに対してのみ呼び出すことができます。 このクラス階層で定義されていないセレクターを呼び出そうとすると、 スコープ・メカニズムがチェックするため、 コンパイル時にエラーが発生します。


6.24.4.3 The oof.fs base class

クラスを定義するときは、 親クラスを指定する必要があります。 では、 クラスの定義はどのように始めればよいのでしょうか? 最初から使用できるクラスは 1 つあります。 それは object です。 これをすべてのクラスの祖先として使用する必要があります。 これは親が存在しない唯一のクラスです。 クラスはインスタンス変数を持たない点を除けばオブジェクトでもあります。 クラスの継承や定義の変更などのクラス操作は、 クラス object のセレクターを通じて処理されます。

object はいくつかのセレクターを提供します:

  • サブクラス化には class を、 後で定義を追加するには settings を、 型情報を取得するには class? (現在のクラスはスタックに渡されたクラスのサブクラスかどうか? を調べる)。
    object-class ( "name" –  ) oof “object-class”
    
    object-definitions ( ) oof “object-definitions”
    
    object-class? ( o – flag  ) oof “class-query”
    
  • initdispose をオブジェクトのコンストラクターとデストラクターとして使用します。 init はオブジェクトのメモリーが割り当て後に呼び出されますが、 dispose は割り当て解除処理も行います。 したがって、 dispose を再定義する場合は、 super destroy を使用して親の destroy も呼び出す必要があります。
    object-init ( ... –  ) oof “object-init”
    
    object-dispose ( ) oof “object-dispose”
    
  • newnew[]:ptrasptr[] を使用して、 名前付きおよび名前なしで、 オブジェクトとオブジェクト配列またはオブジェクト・ポインター を作成します。
    object-new ( – o  ) oof “object-new”
    
    object-new[] ( n – o  ) oof “new-array”
    
    object-: ( "name" –  ) oof “define”
    
    object-ptr ( "name" –  ) oof “object-ptr”
    
    object-asptr ( o "name" –  ) oof “object-asptr”
    
    object-[] ( n "name" –  ) oof “array”
    
  • ::super は明示的なスコープ設定に使用します。 明示的なスコープ指定は、 スーパークラスまたは同じインスタンス変数セットを持つクラスに対してのみ使用する必要があります。 明示的にスコープ指定されたセレクターは早期結び付け(early binding)を使用します。
    object-:: ( "name" –  ) oof “scope”
    
    object-super ( "name" –  ) oof “object-super”
    
  • self でオブジェクト自身のアドレスを取得します
    object-self ( – o  ) oof “object-self”
    
  • bindboundlinkis を使用して、 オブジェクト・ポインターと instance defers を割り当てます。
    object-bind ( o "name" –  ) oof “object-bind”
    
    object-bound ( class addr "name" –  ) oof “object-bound”
    
    object-link ( "name" – class addr  ) oof “object-link”
    
    object-is ( xt "name" –  ) oof “object-is”
    
  • ' はセレクター・トークンを取得し、 send はスタックからセレクターを呼び出し、 postpone はセレクター呼び出しコードを生成します。
    object-' ( "name" – xt  ) oof “tick”
    
    object-postpone ( "name" –  ) oof “object-postpone”
    
  • withendwith は、 スタックから得たオブジェクトをアクティブ・オブジェクトにし、 そのオブジェクトのスコープを有効にします。 withendwith を使用すると、 ステートスマート・オブジェクトにトラップされることなく、 セレクターを postpone してコードを作成することもできます。
    object-with ( o –  ) oof “object-with”
    
    object-endwith ( ) oof “object-endwith”
    

6.24.4.4 Class Declaration

  • インスタンス変数
    var ( size –  ) oof “var”
    

    インスタンス変数を作成する

  • オブジェクト・ポインター
    ptr ( ) oof “ptr”
    

    インスタンス・ポインターを作成します

    asptr ( class –  ) oof “asptr”
    

    インスタンス・ポインターへのエイリアスを作成し、 別のクラスにキャスト(cast)します。

  • Instance defers
    defer ( ) oof “defer”
    

    instance defer を作成します

  • メソッド・セレクター
    early ( ) oof “early”
    

    早期結び付け(early binding)用のメソッド・セレクターを作成します。

    method ( ) oof “method”
    

    メソッド・セレクターを作成します。

  • クラス変数(class-wide variables)
    static ( ) oof “static”
    

    クラス全体のセル・サイズの変数を作成します。

  • 宣言部分を終わらせる
    how: ( ) oof “how-to”
    

    宣言を終わらせ、 実装部分を開始する

    class; ( ) oof “end-class”
    

    クラス宣言または実装の終わり


6.24.4.5 Class Implementation


6.24.5 The mini-oof.fs model

Gforth の 3 番目のオブジェクト指向 Forth パッケージは 12行野郎(12-liner)です。 objects.fs 構文と oof.fs 構文を組み合わせて使用​​し、 機能を最小限に抑えています。 これは、Bernd Paysan の comp.lang.forth への投稿に基づいています。


6.24.5.1 Basic mini-oof.fs Usage

基底クラス(base class)(class、 オブジェクト・ポインターに 1 つのセルを割り当てます) に加えて、 7つのワードがあります。 それは、 メソッドの定義、 変数の定義、 クラスの定義開始、 クラスの定義終了、 結び付けの解決(resolve binding)、 オブジェクトの割り当て、 クラス・メソッドのコンパイル、 です。

object ( – a-addr  ) mini-oof “object”

object はすべてのオブジェクトの基底クラス(base class)です。

method ( m v "name" – m’ v  ) mini-oof “method”

セレクターを定義します。

var ( m v size "name" – m v’  ) mini-oof “var”

size バイトのサイズの変数を定義します。

class ( class – class selectors vars  ) mini-oof “class”

クラスの定義を開始します。

end-class ( class selectors vars "name" –  ) mini-oof “end-class”

クラスの定義を終了します。

defines ( xt class "name" –  ) mini-oof “defines”

xt をクラス class のセレクター name に結び付け(bind)します。

new ( class – o  ) mini-oof “new”

クラス class の新しい具体化(incarnation)を作成します。

:: ( class "name" –  ) mini-oof “colon-colon”

クラス class のセレクター name のメソッドをコンパイルします (注意: 即実行ではありません!)。


6.24.5.2 Mini-OOF Example

短い例で、 このパッケージの使い方を示します。 他の例として button オブジェクト の Mini-OOF バージョンが moof-exm.fs にあります。

object class
  method init
  method draw
end-class graphical

このコードは、 draw 操作を持つクラス graphical を定義します。 任意の graphical オブジェクトに対して draw 操作を実行できます。例:

100 100 t-rex draw

ここで、 t-rex はオブジェクトまたはオブジェクト・ポインターであり、 たとえば次のように作成されます: graphical new Constant t-rex

具体的な graphical オブジェクトのために、 クラス graphical の子クラスを定義します。 例:

graphical class
  cell var circle-radius
end-class circle \ "graphical" is the parent class

:noname ( x y -- )
  circle-radius @ draw-circle ; circle defines draw
:noname ( r -- )
  circle-radius ! ; circle defines init

暗黙的な init メソッドはないため、 明示的に定義する必要があります。 オブジェクトの作成コードは明示的に init を呼び出す必要があります。

circle new Constant my-circle
50 my-circle init

すべてのオブジェクトの同じ場所に init がある場合、 init の自動呼び出しで名前付きオブジェクトを作成する関数を追加することもできます:

: new: ( .. o "name" -- )
    new dup Constant init ;
80 circle new: large-circle

以下のようにして、 この新しい circle を (100,100) に描画できます:

100 100 my-circle draw

6.24.5.3 mini-oof.fs Implementation

遅延結び付け(late binding)を備えたオブジェクト指向システムは通常、 「vtable」アプローチを使用します。 つまり、 各オブジェクトの最初の変数はテーブルへのポインタであり、 テーブルには関数ポインターとしてメソッドが含まれています。 vtable には他の情報も含まれる場合があります。

まず、 セレクターを宣言しましょう:

: method ( m v "name" -- m' v ) Create  over , swap cell+ swap
  DOES> ( ... o -- ... ) @ over @ + @ execute ;

セレクターの宣言中、 セレクターの数とインスタンス変数は(アドレス単位で)スタック上にあります。 method はセレクターを 1 つ作成し、 セレクター番号をインクリメントします。 セレクターを実行するには、 オブジェクトを取得し、 vtable ポインターを取得し、 オフセットを追加して、 そこに保存されているメソッド xt を実行します。 実行時、 各セレクターは、 スタック・パラメーターのTOSとして、 セレクターを呼び出すオブジェクトを受け取り、 パラメーター(オブジェクトを含む)を変更せずに、 適切なメソッドに渡します。

いまや、 インスタンス変数も宣言しなければなりません

: var ( m v size "name" -- m v' ) Create  over , +
  DOES> ( o -- addr ) @ + ;

同様に、 ワードには現在のオフセットを書き込んで作成されます。 インスタンス変数はさまざまなサイズ(セル、 浮動小数点数、 2倍長整数、 char)にすることができるため、 そのサイズを取得してオフセットに追加します。 マシンにアライメント制限がある場合は、 変数の前に適切な aligned または faligned を配置して、 変数のオフセットを調整します。 それが size がスタックのTOSにある理由です。

開始点(基底オブジェクト)と、 いくつかの糖衣構文も必要です:

Create object  1 cells , 2 cells ,
: class ( class -- class selectors vars ) dup 2@ ;

継承の場合、 新しい派生クラスが宣言されるときに、 親オブジェクトの vtable をコピーする必要があります。 これにより、 親クラスのすべてのメソッドが提供されますが、 それらはオーバーライドすることもできます。

: end-class  ( class selectors vars "name" -- )
  Create  here >r , dup , 2 cells ?DO ['] noop , 1 cells +LOOP
  cell+ dup cell+ r> rot @ 2 cells /string move ;

最初の行は、 noop で初期化された vtable を作成します。 2 行目は継承メカニズムで、 親 vtable から xt 達をコピーします。

新しいメソッドを定義する方法がまだありませんね。 ここで定義しましょう:

: defines ( xt class "name" -- ) ' >body @ + ! ;

新しいオブジェクトを割り当てるためのワードも必要です:

: new ( class -- o )  here over @ allot swap over ! ;

派生クラスが親オブジェクトのメソッドにアクセスしたい場合があります。 Mini-OOF でこれを実現するには 2 つの方法があります: 1 つ目は名前付きのワードを使用する方法で、 2 つ目は親オブジェクトの vtable を検索する方法です。

: :: ( class "name" -- ) ' >body @ + @ compile, ;

混乱しないためには良い例に勝るものはないので、 以下に例を示します。 まず、 テキストとその位置を格納するテキスト・オブジェクト(button と呼ばれます)を宣言しましょう:

object class
  cell var text
  cell var len
  cell var x
  cell var y
  method init
  method draw
end-class button

次に、 drawinit の 2 つのメソッドを実装します:

:noname ( o -- )
 >r r@ x @ r@ y @ at-xy  r@ text @ r> len @ type ;
 button defines draw
:noname ( addr u o -- )
 >r 0 r@ x ! 0 r@ y ! r@ len ! r> text ! ;
 button defines init

継承のデモンストレーションとして、 新しいデータや新しいセレクターを持たない、 クラス bold-button を定義します:

button class
end-class bold-button

: bold   27 emit ." [1m" ;
: normal 27 emit ." [0m" ;

クラス bold-button には button とは異なる draw メソッドがありますが、 新しいメソッドは button の draw メソッドに関して定義されています:

:noname bold [ button :: draw ] normal ; bold-button defines draw

最後に、 2 つのオブジェクトを作成し、セレクターを適用します:

button new Constant foo
s" thin foo" foo init
page
foo draw
bold-button new Constant bar
s" fat bar" bar init
1 bar y !
bar draw

6.24.6 Mini-OOF2

Mini-OOF2 は多くの点で Mini-OOF とよく似ていますが、 いくつかの点で大きく異なります。 特に、 Mini-OOF2 には現在のオブジェクト変数(current object variable)を持ち、 プリミティブの >oo> を使用してそのオブジェクト・スタックを操作します。 すべてのメソッド呼び出しとインスタンス変数へのアクセスは、 現在のオブジェクト(current object)を参照します。

>o ( c-addr – r:c-old ) new “to-o”

現在のオブジェクト(current object)を c_addr に設定し、 以前の現在のオブジェクトをリターン・スタックにプッシュします。

o> ( r:c-addr – ) new “o-restore”

リターン・スタックから以前の現在のオブジェクト(current object)を復元します

メソッド呼び出しまたはインスタンス変数アクセスへのオブジェクト・ポインターの受け渡しを容易にするために、 追加の認識器(recognizer) rec-moof2 がアクティブ化されます。

rec-moof2 ( addr u – xt translate-moof2 | notfound  ) mini-oof2 “rec-moof2”

この認識器は非常に単純なドット・パーサ(dot-parser)で、 .SELECTOR.IVAR>o SELECTOR o>>o IVAR o> に変換します。

セレクターにメソッドを割り当てるには、 xt class is selector を使用するため、 defines は必要ありません。 メソッドの早期結び付け(early binding)には、 [ class ] defers selector が使用され、 :: は必要ありません。


6.24.7 Comparison with other object models

【他のオブジェクト・モデルとの比較】オブジェクト指向の Forth 拡張機能が数多く提案されています(A survey of object-oriented Forths (SIGPLAN Notices, April 1996) by Bradford J. Rodriguez and W. F. S. Poehlman lists 17)。 このセクションでは、 先程説明した、 よく知られた 2つのモデルと、 (メソッド・マップの使用に関して)密接に関連した 2 つのモデルとの関係について議論します。 このセクションでは Andras Zsoter が協力してくれました。

現在最も人気のあるモデルは Neon モデル(参照: Object-oriented programming in ANS Forth (Forth Dimensions, March 1997)であるようですですが、 このモデルには多くの制限があります34:

  • selector object 構文を使用しているため、 スタック上でオブジェクトを渡すのが不自然になります。
  • セレクターが入力ストリームを(コンパイル時に)パースする必要があります。 これにより、 拡張性が低下し、 バグが発見されにくくなります。
  • これにより、 すべてのオブジェクトですべてのセレクターを使用できるようになり、 インターフェイスが不要になりますが、 効率的な実装を作成することが難しくなります。

もう 1 つのよく知られた出版物は、 Object-Oriented Forth (Academic Press, London, 1987) by Dick Pountain ですが、 遅延結び付け(late binding)をほとんど扱っていないため、 真のオブジェクト指向プログラミングに関するモノとは言えません。 代わりに、 Ada (83) のようなモジュラー言語の特徴である情報隠蔽やオーバーロードなどの機能に焦点を当てています。

Does late binding have to be slow? (Forth Dimensions 18(1) 1996, pages 31-35)(「遅延結び付けは遅くないといけないのか?」) で、 Andras Zsoter は(objects.fsthis のような)アクティブ・オブジェクトを多用するモデルについて説明しています。 アクティブ・オブジェクトは、 すべてのフィールドにアクセスするために使用されるだけでなく、 すべてのセレクター呼び出しの受信オブジェクトも指定します。 あなたは { ... } を使用してアクティブなオブジェクトを明示的に変更する必要がありますが、 objects.fs では m: ... ;m で多かれ少なかれ暗黙的に変更されます。 Zsoter のモデルでは、 受信オブジェクトがすでにアクティブなオブジェクトであるため、 メソッドのエントリ・ポイントでのこのような変更は不要です。 一方、 Zsoter のモデルでは明示的な変更(機能)が絶対に必要です。 そうしないと、 誰もアクティブ・オブジェクトを変更できなくなるからです。 このモデルの標準 Forth での実装は、 http://www.forth.org/oopf.html から入手できます。

oof.fs モデルは、 (さまざまなワードリストに名前を保持することにより、)情報の隠蔽とオーバーロードの解決(resolution)をオブジェクト指向プログラミングと組み合わせます。 メソッドのエントリ時にアクティブ・オブジェクトを暗黙的に設定しますが、 (>o...o> または with...endwith を使用して、)明示的に変更することもできます。 オーバーロードの解決と早期結び付け(early binding)のために、 パースと、 ステートスマートなオブジェクトとクラスを使用します: オブジェクトまたはクラスはセレクターをパースし、 そこからメソッドを決定します。 セレクターがオブジェクトまたはクラスによってパースされない場合、 Zsoter のモデルのように、 アクティブなオブジェクトのセレクターへの呼び出し(遅延結び付け;late binding)が実行されます。 フィールドには常にアクティブ・オブジェクトを通じてアクセスします。 このモデルの大きな欠点は、 パースとステート・スマートです。 これにより拡張性が低下し、微妙なバグが発生する可能性が高くなります。 基本的に、 オブジェクトやクラスをティック(tick)したり postpone したりしない場合にのみ安全です(Bernd は反対しているが、 著者 (Anton) は納得していない)。

mini-oof.fs モデルは、 objects.fs モデルの非常に簡素化されたバージョンに非常に似ていますが、 構文的には objects.fsoof.fs モデルが混合されたものです。 。


6.25 Regular Expressions

正規表現(regular expression)は、 多くの現代的プログラミング言語で見られる、 文字列のパターン・マッチング・アルゴリズムです。 あなたは require regexp.fs を使用して正規表現機能を Gforth に追加できます。

このパターン・マッチングの古典的な実装はバックトラッキング・アルゴリズムです。 これは、 後方参照などの機能を使用する場合にも必要です。 Gforth は、 パターン・マッチングのためのバックトラッキング・プログラムを定義する言語(language)を提供することで正規表現を実装します。 基本要素は制御構造 FORKJOIN です。 これはワード内での前方呼び出しであるため、 軽量の試行錯誤制御構造(try and fail control structure)をコーディングできます。

FORK ( compilation – orig ; run-time f –  ) gforth-0.7 “FORK”

AHEAD のような制御構造: JOIN の後にコードを呼び出します。

JOIN ( orig –  ) gforth-0.7 “JOIN”

FORK のための、 THEN のような制御構造

あなたは、 チェックが失敗した場合に、 フラグと ?LEAVE を計算することで、 任意の種類のチェックを自分でプログラムできます。 正規表現コードは (()) で囲まれています。

(( ( addr u –  ) regexp-pattern “((”

正規表現ブロックの開始

)) ( – flag  ) regexp-pattern “))”

正規表現ブロックの終了

正規表現のパターン・マッチングには要素として文字セットがあるため、 多くの機能を使用して文字クラス(charclass と呼ばれる)を作成・変更できます。 ここでの文字はすべてバイトであるため、 ユニコード文字用には拡張されません。

charclass ( ) regexp-cg “charclass”

文字クラスを作成します

+char ( char –  ) regexp-cg “+char”

現在の文字クラスに char を追加します

-char ( char –  ) regexp-cg “-char”

現在の文字クラスから char を削除します

..char ( start end –  ) regexp-cg “..char”

char の範囲(startend)を現在の文字クラスに追加します

+chars ( addr u –  ) regexp-cg “+chars”

charの文字列(string)を現在の文字クラスに追加します

+class ( class –  ) regexp-cg “+class”

文字クラス class と現在の文字クラスの結合(union)

-class ( class –  ) regexp-cg “-class”

現在の文字クラスから、 文字クラス class を抜き取ります(subtract)。

事前定義された文字クラス(charclasses)とそれらのテストと、 一般的なチェックがあります。 チェックが失敗した場合は、 次に考えられる正規表現の代替が試行されるか、 または、 ループが終了(leave)します(訳注: 基本的には、 チェックが成功した場合は addr を 1文字(char)分進めた addr’ を返し、 失敗した場合はスタックに何も積まずに leave する)。

c? ( addr class – addr’ ) regexp-pattern “c?”

文字クラス class にマッチ(addr の位置の char が文字クラス class に含まれているなら addr の 1 char 次のアドレスを addr‘ に返し、 含まれていないなら leave する)

-c? ( addr class – addr’ ) regexp-pattern “-c?”

文字クラス class 以外にマッチ(addr の位置の文字が文字クラス class に含まれていなければ addr の1文字後ろのアドレスを addr’ に返し、含まれていたら leave する)

\d ( addr – addr’  ) regexp-pattern “\d”

数字([0-9])にマッチ(指定の addr の char が digit かどうか([0-9])しらべ、 そうなら addr の char 分次のアドレスを addr’ に返す。 そうでなければ leave する)

\s ( addr – addr’  ) regexp-pattern “\s”

非表示文字(\x00〜\x20)にマッチ(指定の addr の char が blanks かどうか([\x00-\x20])しらべ、 そうなら addr の char 分次のアドレスを addr’ に返す。 そうでなければ leave する)

.? ( addr – addr’  ) regexp-pattern “.?”

任意の1文字(char)にマッチ(任意の1文字(char)であれば次の文字のアドレスを addr’ に返します。 そうでなければ leave します)

-\d ( addr – addr’  ) regexp-pattern “-\d”

addr の文字が数字で無ければ([^0-9])次の文字のアドレスを addr’ に返します。 そうでなければ leave します。

-\s ( addr – addr’  ) regexp-pattern “-\s”

指定の addr の char が blanks で無いかどうか([^\x00-\x20])しらべ、 そうなら addr の char 分次のアドレスを addr’ に返す。 そうでなければ leave する

` ( addr "char" – addr’ ) regexp-pattern “‘”

指定の文字をチェックします。 ` c の形で使用し、 addr の文字が 文字 c ならば、 次の文字へのアドレスを addr’ に返し、そうでなければ leave します。

`? ( addr "char" – addr | addr’ ) regexp-pattern “‘?” 
\ 訳注: `? c  addr の文字が c ならば addr をそのまま返す。 c でなければ次の文字へのアドレスを addr’ に返す。
-` ( addr "char" – addr’ ) regexp-pattern “-‘” 
\ 訳注: -` c addr の文字が c でなければ次の文字へのアドレスを addr’ に返す。 そうでなければ leave する。

指定の文字をチェックします。 ` c の形で使用し、 addr の文字が 文字 c ならば、 次の文字へのアドレスを addr’ に返し、そうでなければ leave します。

明示的に文字列の開始と終了、および文字列定数全体をチェックすることもできます。

\^ ( addr – addr  ) regexp-pattern “\^”

文字列の開始にマッチ

\$ ( addr – addr  ) regexp-pattern “\$”

文字列の終了にマッチ

str=? ( addr1 addr u – addr2  ) regexp-pattern “str=?”

指定の文字列 addr u にマッチ(等しい)するかどうかチェックします(マッチすれば文字列の次のアドレスを addr2 に返します。 マッチしない場合は leave します)

doc-=” (原文未記述)

繰り返される文字セットをチェックするループは、 貪欲(greedy)または非貪欲(non-greedy)にすることができます。

{** ( addr – addr addr  ) regexp-pattern “begin-greedy-star”

貪欲な 0 個以上のパターンの開始

**} ( sys –  ) regexp-pattern “end-greedy-star”

貪欲な 0 個以上のパターンの終わり

{++ ( addr – addr addr  ) regexp-pattern “begin-greedy-plus”

貪欲な 1 つ以上のパターンの開始

++} ( sys –  ) regexp-pattern “end-greedy-plus”

貪欲な 1 つ以上のパターンの終わり

{* ( addr – addr addr  ) regexp-pattern “begin-non-greedy-star”

非貪欲な 0 個以上のパターンの開始

*} ( addr addr’ – addr’  ) regexp-pattern “end-non-greedy-star”

非貪欲な 0 個以上のパターンの終わり

{+ ( addr – addr addr  ) regexp-pattern “begin-non-greedy-plus”

非貪欲な 1 つ以上のパターンの開始

+} ( addr addr’ – addr’  ) regexp-pattern “end-non-greedy-plus”

非貪欲な 1 つ以上のパターンの終わり

例: 部分文字列の検索は、 実際にはその前にあるものとの非貪欲な一致です。

// ( ) regexp-pattern “//”

文字列内を検索(後続のパターンを文字列先頭ではなく文字列中でのマッチにする)(訳注:
: ([0-9]) ( addr u – ) (( \( \d \) )) IF ." [" \1 type ." ]" ELSE ." fail" THEN ;
"0123" ([0-9]) [0] ok
"A123" ([0-9]) fail ok
: (//[0-9]) ( addr u – ) (( // \( \d \) )) IF ." [" \1 type ." ]" ELSE ." fail" THEN ;.
"0123" (//[0-9]) [0] ok
"A123" (//[0-9]) [1] ok
"ABC3" (//[0-9]) [3] ok)

選択肢(alternative)は以下のように書きます。

{{ ( addr – addr addr  ) regexp-pattern “begin-alternatives”

選択肢(alternatives)の開始

|| ( addr addr – addr addr  ) regexp-pattern “next-alternative”

選択肢の区切り文字

}} ( addr addr – addr  ) regexp-pattern “end-alternatives”

選択肢の終了

\1\9 という名前の変数を最大 9 つ使用して、 一致した部分文字列を参照できます

\( ( addr – addr  ) regexp-pattern “\(”

マッチング変数の開始;変数は \\1〜9 として参照されます

\) ( addr – addr  ) regexp-pattern “\)”

マッチング変数の終わり

\0 ( – addr u  ) regexp-pattern “\0”

文字列全体

もちろん、 あなたは、 見つけたパターンを置換するコードを作成することもできます。

s>> ( addr – addr  ) regexp-replace “s>>”

検索/置換 の置換パターン領域の開始

>> ( addr – addr  ) regexp-replace “>>”

任意の置換コードを開始し、 コードはスタック上の文字列を評価し、 それを << に渡します。

<< ( run-addr addr u – run-addr  ) regexp-replace “<<”

置換パターン領域の先頭からを、 指定の文字列 addr u に置換します

<<" ( "string<">" –  ) regexp-replace “<<"”

置換パターン領域の先頭から文字列を string に置換します(訳注: 使い方: <<" string )

s// ( addr u – ptr  ) regexp-replace “s//”

検索/置換 ループの開始

//s ( ptr –  ) regexp-replace “//s”

検索の終わり

//o ( ptr addr u – addr’ u’  ) regexp-replace “//o”

検索/置換 の単一ループの終了

//g ( ptr addr u – addr’ u’  ) regexp-replace “//g”

検索/置換 の全てのループの終了

これらの使用例は test/regexp-test.fs にあります。


6.26 Programming Tools


6.26.1 Locating source code definitions

多くのプログラミング・システムは、 エディターがシステムのハブとなり、 プログラムを構築して実行できる統合開発環境(IDE)として編成されています。 それが必要な場合は、Gforth にもそれがあります(see Emacs and Gforth)。

しかしながら、いくつかの Forth システムには異なる種類の IDE を持っています: Forth コマンド・ラインが開発環境のハブです。 そこからさまざまな方法でソースを表示し、 必要に応じてエディターを呼び出すことができます。

gforth もそのような IDE を実装しています。 gforth は SwiftForth の慣習をほぼ踏襲していますが、 それ以上の機能を実装しています。

このアプローチの利点は、 お気に入りのエディターを使用できることです。 環境変数 EDITOR をお気に入りのエディターに設定すると、 編集コマンドがそのエディターを呼び出します。 Gforth はバックグラウンドでいくつかの GUI エディターを呼び出し(そのため、 Forth セッションは続行するので、 編集を終了する必要はありません)、 ターミナル上のエディターはフォアグラウンドで呼び出します(Gforth に認識されないエディターのデフォルトはフォアグラウンドです)。 EDITOR を設定していない場合、 デフォルトのエディターは vi です。

locate ( "name" –  ) gforth-1.0 “locate”

ワード name のソース・コードを表示し、 現在位置(current location)をそこに設定します。

doc-xt-locate(原文 未記述)

「現在位置」(current location)は、 locate に加えて、 他の多くのワードによって設定されます。 また、 ファイルのロード中にエラーが発生した場合、 エラーの場所が現在位置になります。

多くのワードが現在位置(current location)で機能します:

l ( ) gforth-1.0 “l”

現在位置(current location)のソースコードの行達を表示します。

n ( ) gforth-1.0 “n”

現在位置より後ろの行、 または最後の n または b 出力より後ろの行を表示します。

b ( ) gforth-1.0 “b”

現在位置より前の行、 または最後の n または b 出力より前の行を表示します。

g ( ) gforth-0.7 “g”

現在位置、 または最後の n または b 出力の先頭からエディターに入ります。

以下の値を変更することで、 lnb で表示する行数を制御できます:

before-locate ( – u  ) gforth-1.0 “before-locate”

現在位置の前に表示される行数(デフォルトは 3)。

after-locate ( – u  ) gforth-1.0 “after-locate”

現在位置の後に表示される行数 (デフォルトは 12)。

最後に、以下を使用すると、 エディターを開いて当該ワードのソース・コードに直接移動できます。

edit ( "name" –  ) gforth-1.0 “edit”

"name" の場所でエディターを起動します。

以下を使用すると、 似た名前のワードの定義を確認できます

browse ( "subname" –  ) gforth-1.0 “browse”

(mwords のように、) subname を名前の一部に含むワードが定義されているすべての場所を表示します(see Word Lists)。 その後、 ww または nw または bw (see Locating uses of a word) を使用して、 特定の場所をより詳細に調査できます(訳注: 表示結果各行末尾の数(インデックス)を指定して、 ww で当該の場所の内容を閲覧する、 等)


6.26.2 Locating uses of a word

where ( "name" –  ) gforth-1.0 “where”

name が「使用されている」すべての場所を表示します(テキスト・インタープリター時)。 その後、 ww または nw または bw を使用して、 特定の場所をより詳細に調べられます。 gforth の where では name の定義は表示されません。 そのためには locate を使用してください。

ww ( u –  ) gforth-1.0 “ww”

その後の lg で閲覧する場所をインデックス u で指定します。 ここで、 インデックスは browsewhere で表示した際の行末尾の数字です。

nw ( ) gforth-1.0 “nw”

次の l または g では、 browse または where の次の行の場所を表示します。 現在位置(current location)が browsewhere の表示の最後のモノである場合、 nw の後には何も無いです。 現在位置(current location)がまだ設定されていない時は、 nw の後は、 browsewhere の表示の最初のものが現在位置(current location)になります。

bw ( ) gforth-1.0 “bw”

次の l または g では、 browse または where の前の行の場所を表示します。 現在位置(current location)が browsewhere の表示の最初のモノである場合、 bw の後には何も無いです。 現在位置(current location)がまだ設定されていない時は、 nw の後は、 browsewhere の表示の最後のものが現在位置(current location)になります。

gg ( ) gforth-1.0 “gg”

このワードの次の wwnwbwbbnblb (ただし、 locateeditlg は除く)は、 その結果を(g のように)エディターに流し込みます。 これをこの場限り(one-shot)では無くく永続的にするには、 gg gg を使用します。

ll ( ) gforth-1.0 “ll”

このワードの次の wwnwbwbbnblb (ただし、 locateeditlg は除く)は(l のように) Forth システムに表示されます。 これを1回限りではなく永続的にするには、 ll ll を使用します。

whereg ( "name" –  ) gforth-1.0 “whereg”

where と似ていますが、 出力をエディターに流し込みます。 Emacs では、コンパイル・モード・コマンド (see Compilation Mode in GNU Emacs Manual) を使用して、 特定の箇所をより詳細に調査できます。

short-where ( ) gforth-1.0 “short-where”

短いファイル形式を使用するように where を設定します(デフォルト)。

expand-where ( ) gforth-1.0 “expand-where”

完全に拡張されたファイル形式を使用するように where を設定します(例えば、 エディターなどに渡すためです)。

prepend-where ( ) gforth-1.0 “prepend-where”

where を設定してファイルを別の行に表示し、 その後にファイル名のない where 行が続きます(SwiftForth 風)。

ワードの使用状況に関するデータを利用すると、 どのワードが使用されていないのかを表示することもできます:

unused-words ( ) gforth-1.0 “unused-words”

使用されていないすべてのワードをリストします


6.26.3 Locating exception source

tt ( u –  ) gforth-1.0 “tt”
\ 訳注: エラー表示時の Backtrace: で表示される番号を指定して、その場所を表示します。
nt ( ) gforth-1.0 “nt”
\ 訳注: tt の次の backtrace 行の場所を表示します。
bt ( ) gforth-1.0 “bt”
\ 訳注: tt で表示した1行前の backtrace 行の場所を表示します。

6.26.4 Examining compiled code

そして最後に、 see とそのファミリーが、 コンパイル済のコードの表示を行います。 ソース・コード内の一部の内容は、 コンパイルされたコードには存在しません(書式設定やコメント等)。 しかし、 これは、 マクロや Gforth の最適化機能によってどのようなスレッド化コード(threaded code)やネイティブ・コード(native code)が生成されるかを確認するのに役立ちます。

see ( "<spaces>name" –  ) tools “see”

現在の検索順序(search order)を使用して namelocate します。 locate できたら、 その定義を表示します。 これは定義を逆コンパイルすることで実現されるため、 書式は機械化され、 一部のソース情報(コメントや定義内のインタプリトされたシーケンスなど)は失われます。

xt-see ( xt –  ) gforth-0.2 “xt-see”

xt で表される定義を逆コンパイルします。

simple-see ( "name" –  ) gforth-0.6 “simple-see”

コロン定義 name を逆コンパイルして各セルごとに行表示し、 セルの意味を推測してそれを表示します。

xt-simple-see ( xt –  ) gforth-1.0 “xt-simple-see”

simple-see のようにコロン定義 xt を逆コンパイルします。

simple-see-range ( addr1 addr2 –  ) gforth-0.6 “simple-see-range”

[addr1,addr2) ( addr1 <= and < addr2 )の範囲のコードを simple-see のように逆コンパイルします

see-code ( "name" –  ) gforth-0.7 “see-code”

simple-see と似ていますが、 インライン化されたプリミティブの動的ネイティブ・コード(dynamic native code)も表示されます。 静的命令融合(superinstructions)の場合、 最初のプリミティブではなくプリミティブ・シーケンスが表示されます(命令融合(superinstructions)の他のプリミティブも表示されます)。 ネイティブ・コードが生成されるプリミティブの場合、 最初と最後のレジスタ内のスタック項目の数が表示されます(たとえば、 1->1 は、 最初と最後のレジスターに 1 つのスタック項目があることを意味します)。 ネイティブ・コードを使用した各プリミティブまたは命令融合(superinstructions)については、 インライン引数とコンポーネント・プリミティブが最初に表示され、 次にネイティブ・コードが表示されます。

xt-see-code ( xt –  ) gforth-1.0 “xt-see-code”

コロン定義 xtsee-code のように逆コンパイルします。

see-code-range ( addr1 addr2 –  ) gforth-0.7 “see-code-range”

[addr1,addr2) ( addr1 <= and < addr2 )の範囲のコードを see-code のように逆コンパイルします。

例として、 以下について考えてみましょう:

: foo x f@ fsin drop over ;

この例は特に役に立つわけではありませんが、 さまざまなコード生成の違いを示しています。 これを AMD64 の gforth-fast でコンパイルし、 see-code foo を使用すると以下の出力が得られます(訳注: OSのコマンドラインから、 gforth ではなくて gforth-fast で起動する):

$7FD0CEE8C510 lit f@     1->1 
$7FD0CEE8C518 x
$7FD0CEE8C520 f@
7FD0CEB51697:   movsd   [r12],xmm15
7FD0CEB5169D:   mov     rax,$00[r13]
7FD0CEB516A1:   sub     r12,$08
7FD0CEB516A5:   add     r13,$18
7FD0CEB516A9:   movsd   xmm15,[rax]
7FD0CEB516AE:   mov     rcx,-$08[r13]
7FD0CEB516B2:   jmp     ecx
$7FD0CEE8C528 fsin
$7FD0CEE8C530 drop    1->0 
7FD0CEB516B4:   add     r13,$08
$7FD0CEE8C538 over    0->1 
7FD0CEB516B8:   mov     r8,$10[r15]
7FD0CEB516BC:   add     r13,$08
$7FD0CEE8C540 ;s    1->1 
7FD0CEB516C0:   mov     r10,[rbx]
7FD0CEB516C3:   add     rbx,$08
7FD0CEB516C7:   lea     r13,$08[r10]
7FD0CEB516CB:   mov     rcx,-$08[r13]
7FD0CEB516CF:   jmp     ecx

まず、 コンポーネント litf@ を含む静的命令融合(superinstruction)のスレッド化コード・セルが表示されます。 それは、 レジスター内の 1 つのデータ・スタック項目 (1->1) で始まり・終わります。 )。 これに、lit の引数 x のセルと、 命令融合の f@ コンポーネントのセルが続きます。 後者のセルは使用されませんが、 Gforth 内部の理由により存在します。

その次に、 命令融合 lit f@ に対して動的に生成されたネイティブ・コードを表示します。 注意: アドレスを比較するとわかるように、 このネイティブ・コードはメモリー内のスレッド化コードと混合されていない(not mixed)ことに注意してください。

ここに表示されているネイティブ・コードを理解するには、以下のレジスターについて知っておくべきです:
スレッド化コードの命令ポインターは r13
データ・スタック・ポインターは r15
最初(TOS)のデータ・スタック・レジスターは r8 (データ・スタックのTOS、 つまり、 レジスターにデータ・スタック項目が 1 つある場合、 スタックのTOSがそこに存在します)
リターン・スタック ポインターは rbx
FP スタック・ポインターは r12
FP スタックの先頭(TOS)は xmm15
注意: レジスターの割り当てはエンジンによって異なるため、 コード表示においては異なるレジスターの割り当てが表示される場合があることに注意してください。

lit f@ の動的ネイティブ・コードは、 ディスパッチ・ジャンプ(dispatch jump;別名 NEXT)(訳注: 古典的には内部インタープリターへ制御を戻すためのジャンプ)で終了します。 これは、 定義内の次のワード fsin のネイティブ・コードは動的に生成しないためです。

次に、 fsin のスレッド化コード・セルが表示されます。 このワードには動的に生成されるネイティブ・コードはなく、 see-code では静的ネイティブ・コードは表示されません(fsin の静的ネイティブ・コードを確認したければ see fsin)。 gforth-fast 内の静的ネイティブ・コードを含むすべてのワードと同様、 データ・スタック表現への影響は 1->1 (gforth エンジンでは 0->0) です。 しかし、 これは表示されません。

次に、 drop のスレッド化コード・セルが表示されます。 ここで使用されるネイティブ・コード版のバリエーションは、 レジスター内の 1 つのデータ・スタック項目で始まり、 レジスター内の 0 個のデータ スタック項目で終わります(1->0)。 この後に drop のこのバリエーションのネイティブ・コードが続きます。 ここのネイティブ・コードは次のワードのコードに続くため、 ここには NEXT はありません。

次に、 over のスレッド化コード・セルと、 その後に 0->1 バリエーションで動的に生成されたネイティブ・コードが表示されます。

最後に、 ;s のスレッド化されたネイティブ・コード(foo 内の ; 用にコンパイルされたプリミティブ)が表示されます。 ;s は制御フローを実行する(制御フローからリターンする)ため、 NEXT で終了する必要があります。


6.26.5 Examining data and code

以下のワード群は、 スタックを非破壊的に調査します:

... ( x1 .. xn – x1 .. xn  ) gforth-1.0 “...”

.s のスマート・バージョン

.s ( ) tools “dot-s”

データ・スタック上の項目の数を表示し、 その後に項目のリストを表示します(ただし、 表示項目数は maxdepth-.s で指定された値を超えないようにします)。 右端が TOS になります。

f.s ( ) gforth-0.2 “f-dot-s”

浮動小数点スタック上の項目の数を表示し、 その後に項目のリストを表示します(ただし、 表示項目数は maxdepth-.s で指定された値を超えないようにします)。 右端が TOS になります。

f.s-precision ( – u  ) gforth-1.0 “f.s-precision”

value 型変数です。 u は f.s の出力のフィールド幅です。 他の精度の詳細はこの値から導出されます。

maxdepth-.s ( – addr  ) gforth-0.2 “maxdepth-dot-s”

変数(variable)。 デフォルトは 9 です。 .sf.s は最大でその数のスタック項目を表示します。

.r というワードがありますが、 リターン・戻りスタックは「表示されません」。 これは、 フォーマットされた数値出力に使用されるワードです(see Simple numeric output)。

以下のワード群は、 スタックの深さを決定したり、スタックをクリアしたりして、 スタック全体に作用します:

depth ( – +n  ) core “depth”

+n は、 +n 自体がスタックに置かれる前にデータ・スタックに置かれていた項目の数です。

fdepth ( – +n  ) floating “f-depth”

データ・スタックに積まれた +n は、 浮動小数点スタック上にある(浮動小数点)値の現在の数です。

clearstack ( ... –  ) gforth-0.2 “clear-stack”

データ・スタックから全ての項目を削除して破棄します(訳注: データ・スタックを空にします)。

fclearstack ( r0 .. rn –  ) gforth-1.0 “f-clearstack”

浮動小数点スタックをクリアします

clearstacks ( ... –  ) gforth-0.7 “clear-stacks”

データ・スタックと FP スタックを空にします

以下のワード群はメモリーを調べます。

? ( a-addr –  ) tools “question”

アドレス a-addr の内容を現在の基数(base)で表示します。

dump ( addr u –  ) tools “dump”

メモリーのアドレス addr から始まる u バイトを表示します。 各行には 16 バイト分の内容が表示されます。 Gforth がオペレーティング・システム上で実行されている場合、 任意の場所にアクセスしようとすると、 Invalid memory address エラーが発生する可能性があります。


6.26.6 Forgetting words

forth は、 ワード達 (および、 その後ろでディクショナリー割り当てられたすべての内容) を LIFO 方式で忘れさすことができます。

marker ( "<spaces> name" –  ) core-ext “marker”

name という定義を作成します(マーク(mark)と呼ばれる)。 その実行機能(execution semantics)は、 それ自体とその後に定義されたすべてのものを削除します(訳注: 注意: 古典的な forth の forget name と異なり、 任意のワード以降を「忘れる」コマンドでは無い事に注意。 あらかじめ marker でマークした位置でしか「忘れる」事はできない)。

この機能の最も一般的な使用法は、 プログラム開発時です。 ソース・ファイルを変更するときは、 定義されているワードをすべて忘れて、 再度ロードします(ソース・ファイルのロード後に定義されたワードもすべて忘れるので、 それも再ロードする必要があります)。 変数への保存やシステム・ワードの破壊などの効果は、 ワードを忘れても元に戻されないことに注意してください。 著者は、 gforth のようなシステムでは、 起動とコンパイルが十分に速いので、 gforth を終了して再起動する方が、 白紙の状態(clean state)で使えるので便利だと思います。

デバッグしているソース・ファイルの先頭で marker を使用する例を以下に示します。 これにより、 そのファイルの定義は二重定義されないようになります。

[IFDEF] my-code
    my-code
[ENDIF]

marker my-code
init-included-files

\ .. definitions start here
\ .
\ .
\ end

6.26.7 Debugging

編集/コンパイル/リンク/テスト の開発ループが遅い言語では、 デバッグを容易にするために高度な トレース/ステッピング デバッガが必要になる傾向があります。

高速コンパイル言語でのより良い(より速い)方法は、 適切に選択した場所に表示コード(printing code)を追加し、 プログラムを実行してその出力を見て、 問題が発生した箇所を確認し、 バグが見つかるまでさらに表示コード等を追加することです(訳注: C言語で言うところの printf デバッグすると言っている)。

debugs.fs で提供される単純なデバッグ支援機能は、 このスタイルのデバッグをサポートすることを目的としています。

~~ というワードは、 デバッグ情報(デフォルトではソースの場所とスタックの内容)を出力します。 これは簡単にソース・コードに挿入できます。 Emacs を使用している場合は、 削除も簡単です(Emacs Forth モードで C-x ~ を実行すると、 ~~ を ""(nothing) に query-replace します)。 defer ワード printdebugdata.debugline は、 ~~ の出力を制御します。 デフォルトのソース位置出力形式は Emacs のコンパイル・モードで適切に動作するため、 C-x ` を使用してソース・レベルでプログラムをステップ実行できます(ステップ実行デバッガーと比較した利点は、 任意の方向にステップ実行でき、 クラッシュが発生した場所や奇妙なデータが発生した箇所を知る事ができるということです)。

~~ ( ) gforth-0.2 “tilde-tilde”

~~ を記述したソース・コードの場所とスタックの内容を .debugline を使って出力します。

printdebugdata ( ) gforth-0.2 “print-debug-data”
.debugline ( nfile nline –  ) gforth-0.6 “print-debug-line”

nfile nline で示されるソース・コードの場所と追加のデバッグ情報を出力します。 デフォルトの .debugline は、 printdebugdata を使用して追加情報を出力します。

debug-fid ( – file-id  ) gforth-1.0 “File-id

デバッグ用の出力先。 デフォルトでは、 当該プロセスの stderr です。

~~ (およびアサート(assertions))は、 その出現後に同一ファイル内で marker が実行されると、 通常、 間違ったファイル名を出力します。 ~~ (およびアサート(assertions))は、 その出現前に同じファイル内で marker が実行されると、 ファイル名として「どこか」(somewhere)を出力します。

once ( ) gforth-1.0 “once”

once から THEN までの操作を 1 回だけ だけ実行します(訳注: 1度実行されるとその旨を body に書き込むので、 再度実行するには once を含む定義を「忘れ」(forget)て再度定義(読み込み)する必要がある)

~~bt ( ) gforth-1.0 “~~bt”

スタック・ダンプとバックトレースを出力

~~1bt ( ) gforth-1.0 “~~1bt”

スタックダンプとバックトレースを(onceと同様) 1 回だけ実行します

??? ( ) gforth-0.2 “???”

デバッグ・シェルを実行します

WTF?? ( ) gforth-1.0 “WTF??”

バックトレースとスタック・ダンプを使用してデバッグ・シェルを実行します

!!FIXME!! ( ) gforth-1.0 “!!FIXME!!”

決してこの場所に到達してはいけないワード(訳注: -2605 を throw する)

replace-word ( xt1 xt2 –  ) gforth-1.0 “replace-word”

xt2 が xt1 を実行するようにします。 両方ともコロン定義である必要があります。(訳注:
: foo ." foo" ;
: bar ." bar" ;
foo foo ok
bar bar ok
’ foo ’ bar replace-word ok
foo foo ok
bar foo ok

~~Variable ( "name" –  ) gforth-1.0 “~~Variable”

アクセスごとにワッチされる(~~ する)変数(variable を ~~variable に一時的にリネームする)

~~Value ( n "name" –  ) gforth-1.0 “~~Value”

アクセスごとにワッチされる(~~ する) value ( value を 一時的に ~~value にリネームする)

+ltrace ( ) gforth-1.0 “+ltrace”

行単位のトレースをONにする。

-ltrace ( ) gforth-1.0 “-ltrace”

行単位のトレースをOFFにする

#loc ( nline nchar "file" –  ) gforth-1.0 “#loc”

次のワードの場所を "file"nline nchar と設定します


6.26.8 Assertions

特にメンテナンス中に無効になる可能性があると仮定する場合(たとえば、 データ構造の特定のフィールドは決してゼロにならない、 等)、 プログラムを自己チェックするようにすることをお勧めします。 Gforth は、 こ​​の目的のために「アサート」(assertions)をサポートしています。 それらは以下のように使用します:

assert( flag )

assert() の間のコードは flag を計算する必要があります。 すべてが正常な場合は true 、それ以外の場合は false になります。 スタック上の他のものは何も変更しないでください。 アサートの全体的なスタック効果は ( -- ) です。 例:

assert( 1 1 + 2 = ) \ what we learn in school
assert( dup 0<> ) \ assert that the top of stack is not zero
assert( false ) \ このコードには決して到達してはならない

アサートの必要性は、 その時々によって異なります。 デバッグ中はより多くのチェックが必要ですが、 運用環境では速度をより重視することがあります。 したがって、アサートをオフにすることができます。 その場合、 つまり、 アサートはコメントになります。 アサートの重要性とそのチェックにかかる時間に応じて、 一部のアサートをオフにし、 他のアサートをオンにしたほうがよい場合があります。 Gforth は、こ​​の目的のためにいくつかのレベルのアサートを提供します:

assert0( ( ) gforth-0.2 “assert-zero”

常にオンにする必要がある重要なアサート。

assert1( ( ) gforth-0.2 “assert-one”

通常のアサート: デフォルトではオンになっています。

assert2( ( ) gforth-0.2 “assert-two”

デバッグ用のアサート。

assert3( ( ) gforth-0.2 “assert-three”

通常のデバッグでは有効にしたくないであろう遅いアサート。 主に徹底的なチェックを行うためにオンにします。

assert( ( ) gforth-0.2 “assert(”

assert1( と同等です

) ( ) gforth-0.2 “close-paren”

アサートを終了します。 汎用の終端なので、 他の同様の目的でも使用されます

変数 assert-level は、オンにする最高のアサートを指定します。 つまり、デフォルトの assert-level の 1 では、 assert0( および assert1( はチェックを実行しますが、 assert2( および assert3() はコメントとして扱われます。

assert-level の値は、 実行時ではなくコンパイル時に評価されます。 したがって、 実行時にアサートをオンまたはオフにすることはできません。 あなたはコードをコンパイルする前に assert-level を適切に設定する必要があります。 異なるコード部分を異なる assert-level でコンパイルできます(例: 信頼に足るライブラリーはレベル 1 で、 新しく作成されたコードはレベル 3 など)。

assert-level ( – a-addr  ) gforth-0.2 “assert-level”

変数(variable)。 指定のレベルを超えるすべてのアサートはオフになります。

アサートが失敗すると、 Emacs のコンパイル・モードと互換性のあるメッセージが生成され、 実行が中止(abort)されます(現在は ABORT" を使用します。 あなたの興味があれば、 特別な throw コードを導入します。 ただし、 特定の条件をキャッチ(catch)したい場合は、 おそらくアサートよりも throw を使用する方が適切です)。

アサート (および ~~) の出現後に同じファイル内で marker が実行されると、 通常は間違ったファイル名が出力されます。 アサート (および ~~) の出現前に同じファイル内で marker が実行された場合、 ファイル名として「どれか」(somewhere)が出力されます。

これらのアサート・ワードの標準 Forth での定義は、 compat/assert.fs で提供されます。


6.26.9 Singlestep Debugger

シングルステップ・デバッガーは、 gforth-itc エンジンでのみ動作します(訳注: OSコマンドラインから gforth ではなくて gforth-itc で起動する)。

新しいワードを作成するとき、 それが正しく動作するかどうかを確認する必要があることがよくあります。 これを行うには、dbg badword と入力します。 デバッグ・セッションは以下のようになります:

: badword 0 DO i . LOOP ;  ok
2 dbg badword 
: badword  
Scanning code...

Nesting debugger ready!

400D4738  8049BC4 0              -> [ 2 ] 00002 00000 
400D4740  8049F68 DO             -> [ 0 ] 
400D4744  804A0C8 i              -> [ 1 ] 00000 
400D4748 400C5E60 .              -> 0 [ 0 ] 
400D474C  8049D0C LOOP           -> [ 0 ] 
400D4744  804A0C8 i              -> [ 1 ] 00001 
400D4748 400C5E60 .              -> 1 [ 0 ] 
400D474C  8049D0C LOOP           -> [ 0 ] 
400D4758  804B384 ;              ->  ok

各行が 1 ステップです。 次のワードを実行して表示するには、 常に Return キーを押す必要があります。 次のワード全体を実行したくない場合は、 nest に対して n と入力する必要があります。 利用可能なキーの概要は以下のとおりです:

RET

Next; 次のワードを実行する(execute)。

n

Nest; 次のワードまで 1 ステップずつ辿る。

u

Unnest; デバッグを停止し(stop)、残りのワードを実行します(execute)。 ネスト(nest)機能でこのワードに到達した場合、 それを呼び出したワードでデバッグを続行します。

d

Done; デバッグを停止し(stop)、 残りを実行します(execute)。

s

Stop; 直ちに終了(abort)します。

このメカニズムを使用して大規模なアプリケーションをデバッグすることは、 あなたが関心のある部分が始まる前にプログラム内に非常に深くネストする必要があるため、 非常に困難です。 これにはとても時間がかかります。

これをより直接的に行うには、 BREAK: コマンドをあなたのソース・コードに追加します。 プログラムの実行が BREAK: に達すると、 シングル・ステップ・デバッガーが呼び出され、 上記すべての機能が利用できるようになります。

デバッグする部分が複数ある場合、 プログラムが現在どこで停止しているかを把握できると便利です。 これを行うには、 BREAK" string" コマンドを使用します。 これは BREAK: と同様に動作しますが、 「ブレークポイント」に到達したときに文字列が出力される点が異なります。

dbg ( "name" –  ) gforth-0.2 “dbg”
break: ( ) gforth-0.4 “break:”
break" ( ’ccc"’ –  ) gforth-0.4 “break"”

6.26.10 Code Coverage and Execution Frequency

あなたのコードに対して広範なテスト(extensive tests)を実行する場合、 そのテストがコードのすべての部分を実行するかどうかを確認する必要が生じることがよくあります。 これを(テスト)カバレッジと呼びます。 ファイル coverage.fs には、 カバレッジと実行頻度を測定するためのツールが含まれています(訳注: gforth に組み込まれて無いので使う時は coverage.fs をロードしてください)。

コード・カバレッジは、 coverage.fs の後にロードされるすべての基本ブロック(逐次実行のコード・シーケンス)にカウント(計数)コードを挿入します。 コードが実行されるたびに、その基本ブロックのカウンター(計数器)がインクリメントされます。 後で、これらの基本ブロックに挿入されたカウント(計数)を含むソース・ファイルを表示できます。

nocov[ ( ) gforth-1.0 “nocov-bracket”

(即実行ワード) 一時的にカバレッジをオフにすることを開始。

]nocov ( ) gforth-1.0 “bracket-nocov”

(即実行ワード) 一時的にカバレッジをオフにすることを終了。

coverage? ( – f  ) gforth-internal “coverage?”

Value: カバレッジ・チェックの オン/オフ

cov+ ( ) gforth-experimental “cov+”

(即実行ワード) ここ(here)にカバレッジ・カウンターを配置します。

?cov+ ( flag – flag  ) gforth-experimental “?cov+”

(即実行ワード) フラグ(flag)のカバレッジ・カウンター。 カバレッジの出力では、 ?cov の後ろに 3 つの数字が表示されます。 1 つ目はスタックの最上位が 0 以外だった実行の数です。 2 番目は、 ゼロだった場合の実行数です。 3 番目は実行の合計数です。

.coverage ( ) gforth-experimental “.coverage”

コードを実行頻度(execution frequencies)とともに表示します。

annotate-cov ( ) gforth-experimental “annotate-cov”

カバレッジ情報を含むファイルごとに、 実行頻度(execution frequencies)が挿入された .cov ファイルを作成します。 最初に bw-cover を使用することをお勧めします(デフォルトの color-cover を使用すると、 ファイル内にエスケープ シーケンスが含まれます)。

cov% ( ) gforth-experimental “cov-percent”

coverage.fs の後でロードされ、 少なくとも 1 回実行された基本ブロックの割合を出力します。

.cover-raw ( ) gforth-experimental “.cover-raw”

生の実行カウントを出力します。

デフォルトでは、 カウント(計数)は(ANSI エスケープ・シーケンスを使用して、)カラーで表示されますが、 bw-cover を使用すると、 エスケープ・シーケンスを使用せずに括弧で囲まれた形式でカウントを表示できます。

bw-cover ( ) gforth-1.0 “bw-cover”

実行カウントを(カラー表示ではなく)括弧内に出力します(ソース・コード互換)(訳注: つまり forth のコメントとして出力する。出力をそのまま forth のソース・コードとして利用可能)。

color-cover ( ) gforth-1.0 “color-cover”

実行カウントをカラーで表示します(デフォルト)。

カバレッジ・カウンター(計数器)をバイナリ形式で保存および再ロードして、 複数のテスト実行にわたるカバレッジ・カウンターを集計できます。

save-cov ( ) gforth-experimental “save-cov”

カバレッジ・カウンターを保存します。

load-cov ( ) gforth-experimental “load-cov”

カバレッジ・カウンターをロードします。


6.27 Multitasker

Gforth は 2 つのマルチタスカーを提供します。 1 つは従来の協調的なラウンドロビン・マルチタスカー(cooperative round-robin multitasker)で、 もう 1 つはマルチコア・マシン上で複数のスレッドを同時に実行できる pthread ベースのマルチタスカーです。 将来、 Forth マルチタスカーの標準化により、 その意味は変更せずにワードの名前が変更される可能性が高いため、 pthread ベースのマルチタスカーは現在、 実験的な機能としてマークされています。


6.27.1 Pthreads

pthred 、 つまり posix スレッドは、 複数のコア上で並行実行することも、 1 つのコア上でプリエンプティブ・マルチタスクで実行することもできます。 ただし、 以下のワード群の多くは、 従来の協調マルチタスク(traditional cooperative multi-tasker)の場合と同一です。

さらに、 一度に 1 つのタスクのみが何かを変更することを確認したり、 タスク間のコミュニケーションに使用したりするためのワード群もあります。 pause の呼び出し間でトランザクション(取引)を実行する協調マルチタスク(cooperative-multitasking)はこの環境では機能しないため、 これらのワード群はプリエンプティブ・マルチタスクおよびマルチコア・マルチタスクで必要なものです。


6.27.1.1 Basic multi-tasking

タスクは newtask または newtask4 を使用して、 指定された量のスタック領域(すべて同じ、 または、 データ・スタックとリターン・スタックと FP スタックとローカル変数スタックそれぞれのサイズを指定)で作成できます。

newtask ( stacksize – task  ) gforth-experimental “newtask”

タスク(taskを作成します。 データ・スタックとリターン・スタックと FP スタックとローカル変数スタックのサイズは全て同じにします(stacksize)(訳注: スタックごとに個別サイズ指定したい時は newtask4 を使用してください)。

task ( ustacksize "name" –  ) gforth-experimental “task”

タスク name を作成します。 各スタック(データ・スタック、 リターン・スタック、 FP スタック、 ローカル変数スタック) サイズは ustacksize です。
実行時: ( – task )

newtask4 ( u-data u-return u-fp u-locals – task  ) gforth-experimental “newtask4”

データ・スタック・サイズ u-data、 リターン・スタック・サイズ u-return、 FP スタック・サイズ u-fp、 ローカル変数スタック・サイズ u-localstask を作成します。

あなたが、 タスクにどのスタック・サイズを使用すればよいかわからない場合は、 メイン・タスクのサイズを流用できます:

stacksize ( – u  ) gforth-experimental “stacksize”

u はメイン・タスクのデータ・スタックサイズです。

stacksize4 ( – u-data u-return u-fp u-locals  ) gforth-experimental “stacksize4”

メイン・タスクのデータ・スタック・サイズと、 リターン・スタック・サイズと、 FP スタック・サイズと、 ローカル変数スタックサイズを、 プッシュします。

タスクは非アクティブ状態(inactive state)で作成されます。 実行するには、 以下のいずれかのワードを使用してアクティブ化(activate)する必要があります:

initiate ( xt task –  ) gforth-experimental “initiate”

taskxtexecute させます。 xt から戻ると、 タスクは自動的に終了します(VFX 互換)。 ワンタイム実行可能クロージャ(one-time executable closures)を使用して、 任意のパラメーターをタスクに渡せます。

以下の古い(legacy)ワード、 activatepass は、 initiate と同じ機能を提供しますが、 インターフェイスが異なります: does> と同様に、 含まれるコロン定義が 2 つの部分に分割されます: activate/pass より前の部分はタスクをアクティブ化する部分で実行され、 タスクをアクティブ化した後に呼び出し元に戻ります。 activate/pass より後の部分は、 アクティブ化されたターゲット・タスク内で実行されます。

activate ( run-time nest-sys1 task –  ) gforth-experimental “activate”

指定のタスク(task)に activate のより後ろにあるコードを実行させ、 activate を含むワードの呼び出し元に戻ります。 タスクが activate の後ろにあるコードから戻ると、 タスク自体が終了します。

pass ( x1 .. xn n task –  ) gforth-experimental “pass”

現在のタスクのデータ・スタックから x1 .. xn n を取り出し、 x1 .. xntask のデータ・スタックにプッシュします。 taskpass の後ろのコードを実行させ、 pass を含むワードの呼び出し元に戻ります。 タスクが pass の後ろにあるコードから戻ると、 タスク自体が終了します。

作成とアクティブ化(activation)をワン・ステップで行うこともできます:

execute-task ( xt – task  ) gforth-experimental “execute-task”

メイン・タスクと同じスタック・サイズで新しいタスク task を作成します。 taskxtexecute させます。 xt から戻ると、 タスクは自動的に終了します。

タスクは最後まで実行して終了する以外に、 kill-task を使用してタスク自身を終了することもできます。 他のタスクは kill で終了できます。

kill-task ( ) gforth-experimental “kill-task”

現在のタスクを終了(terminate)します。

kill ( task –  ) gforth-experimental “kill”

指定のタスク(task)を終了(terminate)します。

タスク自体を一時的に停止(temporarily stop)したり、 タスクを停止(be stopped)したりすることもできます:

halt ( task –  ) gforth-experimental “halt”

タスクを停止(stop)する

stop ( ) gforth-experimental “stop”

現在のタスクを停止(stop)し、 イベントを待ちます(再起動(restart)される可能性があります)

stop-ns ( timeout –  ) gforth-experimental “stop-ns”

タイムアウト(ナノ秒単位)まで停止(stop)します。 ms のより良い代替品です

タスクは、 タイムアウトが経過(timeout is over)するか、 別のタスクが以下のワードでウェイクアップ(wake)すると再起動(restart)します:

restart ( task –  ) gforth-experimental “restart”

タスクを起動(wake)する

こちらもどうぞ:

pause ( ) gforth-experimental “pause”

次の待機中のタスクに自発的に切り替えます(pause は従来の協調(cooperative)タスク・スイッチャー(切替器)です。 pthread マルチタスクでは、 協調するのに pause は必要ありませんが、 それでも使用できます。 例えば、 何らかの理由でポーリングに頼らなければならないときです)。 これにより、 キュー内のイベントもチェックされます。


6.27.1.2 Task-local data

Forth では、 すべてのタスクには、 「ユーザー領域」(user area)と呼ばれる、 基本的に同じタスク・ローカル・データがあります(初期の Forth システムはマルチ・ユーザー・システムであり、 タスクごとに 1 人のユーザーが存在することがよくありました)。 task の結果、 たとえばnewtask は、そのユーザー領域の開始アドレスです。 各タスクは、 システムによって定義されたユーザー・データ(例: base)を取得します。 あなたは以下を使用して追加のユーザー・データを定義できます:

User ( "name" –  ) gforth-0.2 “User”

name という名前のユーザー変数(1 セル)を作成します。
name 実行時: ( – addr )
addr は現在のタスクのユーザー変数のアドレスです。

AUser ( "name" –  ) gforth-0.2 “AUser”

name というユーザー変数を作成します( User とはクロス・コンパイラーでのみ違いが生じます)。

uallot ( n1 – n2  ) gforth-0.3 “uallot”

n1 バイトのユーザー・データを確保します。 確保した領域の先頭をユーザー領域内のオフセット値(n2)として返します。

UValue ( "name" –  ) gforth-1.0 “UValue”

name はユーザー value です。
name 実行時: ( – x )

UDefer ( "name" –  ) gforth-1.0 “UDefer”

name はタスク・ローカルの deferされたワードです。
Name 実行時: ( ... – ... )

ユーザー・データを扱う場合には、 以下のようなワード群もあります。

up@ ( – addr ) new “up-fetch”

addr は現在のタスクのユーザー領域の先頭です(addr は現在のタスクの task 識別子としても機能します)。

user' ( "name" – u  ) gforth-experimental “user”’

各タスクのユーザー領域のユーザー変数 name の、 ユーザー領域先頭からのオフセット値を U に返します。

's ( addr1 task – addr2  ) gforth-experimental “’s”

addr1 が現在のタスクのユーザー・データ内のアドレスである場合、 addr2 は 指定のタスク(task)のユーザー・データ内の対応するアドレスです。


6.27.1.3 Semaphores

協調的(cooperative)マルチタスカーは、 pause の 2 つの呼び出しの間に相互作用する他のタスクがないことを保証できます。 しかしながら、 pthread は実際には(少なくともマルチコア CPU 上では)並列(concurrent)タスクであるため、 同じリソースにアクセスする際の競合を回避するためのいくつかのテクニックが必要です。

セマフォ(semaphore)を取得できるのは 1 つのスレッドのみであり、 他のすべてのスレッドはセマフォが解放されるまで待機する必要があります。

semaphore ( "name" –  ) gforth-experimental “semaphore”

名前付きセマフォ name を作成します
name 実行時: ( – semaphore )

lock ( semaphore –  ) gforth-experimental “lock”

セマフォをロック(lock)

unlock ( semaphore –  ) gforth-experimental “unlock”

セマフォのロックを解除します(unlock)

同時アクセスを防ぐもう 1 つのアプローチが、 クリティカル・セクションです。 ここではクリティカル・セクションをセマフォで実装しているため、 クリティカル・セクションに使用するセマフォを指定する必要があります。 同一のセマフォを使用するクリティカル・セクション達のみが相互に排他的です。

critical-section ( xt semaphore –  ) gforth-experimental “critical-section”

semaphoreをロックした状態で xtexecute します。 xt を離れた後は、 それが例外の throw によるものであっても semaphore のロックが解除されます。


6.27.1.4 Hardware operations for multi-tasking

アトミックなハードウェア操作は、 他のタスクが中間状態を参照することなく、 操作全体を実行します。 これらの操作は、 遅い OS プリミティブを使用せずにタスクを同期するために使用できますが、 非アトミックな操作シーケンスと比較すると遅くなる傾向があります。 アトミックな操作は、 アライメントを必要としないハードウェアであっても、 アライメントされたアドレスでのみ正しく機能します。

!@ ( u1 a-addr – u2 ) gforth-experimental “store-fetch”

a-addr から 値を u2 に取得し、 そして、 a-addr に 値 u1 を格納する操作を、 アトミック操作として行います。

+!@ ( u1 a-addr – u2 ) gforth-experimental “add-store-fetch”

a-addr から 値 u2 を取得し、 a-addr の値に u1 を足し込む操作を、 アトミック操作として行います。

?!@ ( unew uold a-addr – uprev ) gforth-experimental “question-store-fetch”

a-addr から取得した値と uold を比較し、 等しい場合はunewa-addr に格納して uprev には uold をセットします。 等しくない場合は a-addr への格納は行わずに a-addr から取得した値を uprev にセットします。 これらの操作をアトミック操作として行います。

もう 1 つのハードウェア操作はメモリー・バリアーです(memory barrier)。 残念ながら、 最新のハードウェアでは、 メモリー操作を他のメモリー操作と比較して(別のコアから見た場合に)並べ替えることができることが多く、 メモリー・バリアーにより、 タスク実行のある時点でこの並べ替えを抑制します。

barrier ( ) gforth-experimental “barrier”

バリアー前のすべてのメモリー操作は、 バリアー後のメモリー操作より前に実行されます。


6.27.1.5 Message queues

Gforth のメッセージ・キューはアクター・モデルのバリエーションです。

イベントは xt です。 送信タスクは受信タスクにイベントを実行するようにお願いし、 受信タスクは準備ができたらそのメッセージ・キューからイベントを 1 つ実行します。 単回実行クロージャ(Execute-once closures)は、 パラメーターを渡すイベントに使用できます。

send-event ( xt task –  ) gforth-experimental “send-event”

タスク IPC: xttask に送信します。 taskxtexecute されます。 使い捨てクロージャ(one-shot closure)を使用して、 xt でパラメーターを渡します。

複数のタスクが 1 つのタスクにイベントを送信する場合、 イベントは任意の順序で到着する可能性があります。 イベントを受信するには、 受信タスクに以下のいずれかのワードを使います:

?events ( ) gforth-experimental “question-events”

現在のタスクのメッセージ・キュー内のすべてのイベント・シーケンスを一度に 1 つずつ実行します。

event-loop ( ) gforth-experimental “event-loop”

イベント・シーケンスを待ち、 イベント・シーケンスが到着したら実行します。 キューにイベント・シーケンスがない場合は待機に戻ります。 このワードは二度と制御を戻しません(This word never returns.)。

event-loop の代わりに、 タスクが stop 状態になると、 イベントを受信する準備も整い、 そして、 イベントを受信するとタスクが起動(wake)されます。


6.27.2 Cilk

gforth の Cilk は、 同じ名前のプログラミング言語からインスピレーションを得た、 複数のコアで実行される複数のタスク間で作業を分割するためのフレームワークです。 Cilk を使用する場合は、 require cilk.fs としてください。

考え方としては、 並行して解決できる部分問題(subproblems)を特定し、 フレームワークがワーカー・タスクをこれらの部分問題に割り当てるというものです。 特に、 各サブ・タスクに対して spawn ワードの 1 つを使用します。 最終的には、 部分問題が解決されるまで cilk-sync で待つ必要があります。

現在、 すべての生成は 1 つのタスクから発生する必要があり、 cilk-sync はすべての部分問題(subproblems)が完了するまで待機するため、 再帰アルゴリズムに現在の gforth Cilk を使用するのは簡単ではありません。

オーバーヘッドを避けるために、 部分問題を細かく分割しすぎないでください。 どのくらい細かいかは、 部分問題の実行時間がどの程度均一であるかによって決まりますが、 かなりの実行時間の問題の場合は、 5*cores 個の部分問題(subproblems)にするのが、 おそらく良い出発点となります。

cores ( – u  ) cilk “cores”

使用するワーカー・タスクの数を含む値。 デフォルトでは、 これはハードウェア・スレッドの数(SMT/HT の場合)です。 (決定できれば、)それ以外の場合は 1 になります。 別の数を使用したい場合は、 cilk-init を呼び出す前に cores を変更します。

cilk-init ( ) cilk “cilk-init”

ワーカー・タスクがまだ実行されていない場合は開始します。

spawn ( xt –  ) cilk “spawn”

ワーカー・タスクで xt ( – ) を execute します。 使い捨て実行可能クロージャ(one-time executable closures)を使用して、 ヒープに割り当てられたクロージャを渡し、 spawn 呼び出し側から、 ワーカーで実行されているコードに任意のデータを渡すことができます。
例: ( n r ) [{: n f: r :}h1 code ;] spawn

spawn1 ( x xt –  ) cilk “spawn1”

Execute xt ( x – ) in a worker task.

spawn2 ( x1 x2 xt –  ) cilk “spawn2”

Execute xt ( x1 x2 – ) in a worker task.

cilk-sync ( ) cilk “cilk-sync”

すべての部分問題が完了(complete)するまで待ちます(wait)。

cilk-bye ( ) cilk “cilk-bye”

すべてのワーカーを終了(terminate)します。


6.28 C Interface

Gforth の C言語インターフェイスは、 Forth スタックからパラメーターを取得してC言語の関数を呼び出す、 C言語の関数を含むラッパー・ライブラリをコンパイルすることによって機能します。 このラッパー・ライブラリーはC言語のコンパイラーによってコンパイルされます。 コンパイル結果はキャッシュされるため、 ラッパー・ライブラリーを変更する必要がある場合、 Gforth は C言語のコンパイルを再実行するだけで済みます。 このビルド処理は自動(automatic)で、 インターフェイス宣言の最後に行われます。 Gforth は、 この処理に libtool と GCC を使用します。

C言語のインターフェイスはほぼ完成した状態で、 コールバックも既に追加されていますが、 構造体については、 独立したスコープを持たない Forth2012 構造体を使用します。 これらの構造体のオフセットは、 SWIG プラグインを使用してヘッダー・ファイルから抽出されます。


6.28.1 Calling C functions

C言語の関数関数が宣言されたら(see Declaring C Functions 参照)、 次のように呼び出すことができます: あなたは引数達をスタックにプッシュしてから、 C言語関数のワードを呼び出します。 引数は、 C言語のドキュメントに記載されているのと同じ順序でプッシュする必要があります(つまり、 最初の引数がスタックの最も深いところにあります)。 整数およびポインター引数はデータ・スタックにプッシュする必要があり、 浮動小数点引数は FP スタックにプッシュする必要があります。 これらの引数は、 呼び出されたC言語の関数によって消費されます。

C言語の関数から戻るとき、 戻り値がある場合、 その戻り値は適切なスタックに置きます。 整数の戻り値はデータ・スタックにプッシュされ、 FP の戻り値は FP スタックにプッシュされ、 void 戻り値の場合は何もプッシュされません。 C言語ではあまり使用されない場合でも、 ほとんどのC言語の関数には戻り値があることに注意してください。 Forth では、 この戻り値を使用しない場合は、 明示的に捨てる(drop)必要があります。

C言語インターフェイスは、 必要に応じて、 ベストエフォートで C言語の型と Forth の型の間で自動的に変換します(場合によっては、 ある程度の損失が発生する可能性があります)。

例として、 POSIX 関数 lseek() について考察してみましょう:

off_t lseek(int fd, off_t offset, int whence);

この関数は 3 つの整数引数を受け取り、 整数引数 1 つ返すため、 現在のファイルのオフセットをファイルの先頭に設定するための Forth 呼び出しは以下のようになります:

fd @ 0 SEEK_SET lseek -1 = if
  ... \ error handling
then

あなたは、 off_t がセルに収まらないため、 lseek に大きなオフセットを渡すことができず、 戻り値の一部しか取得できないのではないかと心配するかもしれません。 その場合、 関数の宣言(see Declaring C Functions)で、off_t 引数と戻り値に 2倍長セルを使用するように宣言し、 結果の Forth ワードに dlseek などの別の名前を付ける必要があります。 その結果は以下のように呼び出すことができます:

fd @ #0. SEEK_SET dlseek #-1. d= if
  ... \ error handling
then

構造体または共用体の受け渡しは現在、 このインターフェイスではサポートされていません35

可変個の引数を持つ関数の呼び出し(「variadic関数」例: printf())は、 引数パターンごとに 1 つの関数呼び出しワードを宣言し、 目的のパターンに適切なワードを呼び出すことによってのみサポートされます。


6.28.2 Declaring C Functions

あなたは、 先の例の lseekdlseek を呼び出す前に、 宣言する必要があります。 宣言は以下の 2 つの部分で構成されます:

C言語部分

関数の C 言語宣言です。 より一般的かつ移植性の高いものとしては、 C 言語の関数の宣言を含むファイルの C 言語スタイルの #include です。

Forth部分

パラメーターの Forth 型と、 C言語の関数に対応する Forth ワード名を宣言します。

前述の lseekdlseek の宣言は以下のとおりです:

\c #define _FILE_OFFSET_BITS 64
\c #include <sys/types.h>
\c #include <unistd.h>
c-function lseek lseek n n n -- n
c-function dlseek lseek n d n -- d

宣言の C言語部分は \c という接頭辞が付けられ、 \c の行の残りの部分は通常の C 言語のコードです。 C 言語の宣言の行は好きなだけ使用でき、 それ以降のすべての関数宣言で参照できます。

Forth 部分は、 c-function で各インターフェイス・ワードを宣言し、 その後にワードの Forth 名と、 呼び出される C 言語の関数名と、 ワードのスタック効果が続きます。 スタック効果には、 任意の数のパラメーターのタイプ、 --、 そして戻り値の「 1 つ」のタイプが含まれます。 可能なタイプは以下のとおりです:

n

単一セル整数

a

アドレス(単一セル)

d

2倍長セル整数

r

浮動小数点数値

func

C言語の関数へのポインター

void

値なし(void 関数の戻り値の型として使用)

可変個の引数(variadic)の C 言語の関数を扱うには、 あなたが使用したいパターンごとに 1 つの Forth ワードを宣言します。 例:

\c #include <stdio.h>
c-function printf-nr printf a n r -- n
c-function printf-rn printf a r n -- n

注意: C 言語の関数が可変個(variadic)の引数として宣言されている場合(または関数プロトタイプを提供していない場合)、 C 言語インターフェイスには変換先の C 言語の型がないため、 自動変換は行われず、 場合によっては移植性の問題が発生する可能性があることに注意してください。 C 言語の型キャストを波括弧({})で囲んで Forth 型の後に追加できます。 これにより、 例えば、 Forth ではスタック上に存在できない構造体を C 言語の関数へ渡す事も可能になります。

c-function printfll printf a n{(long long)} -- n
c-function pass-struct pass_struct a{*(struct foo *)} -- n

C言語では左辺値(lvalues)の型キャスト(typecast)が許可されていないため、 この型キャストは戻り値には使用できません。

\c ( "rest-of-line" –  ) gforth-0.7 “backslash-c”

C言語インターフェイスのC言語宣言の1行

c-function ( "forth-name" "c-name" "{type}" "–" "type" –  ) gforth-0.7 “c-function”

Forth ワード forth-name を定義します。 Forth-name には指定されたスタック効果({type}type)があり、 C言語関数 c-name を呼び出します。

c-value ( "forth-name" "c-name" "–" "type" –  ) gforth-1.0 “c-value”

Forth ワード forth-name を定義します。 Forth-name には指定されたスタック効果(type)があり、 c-name の C言語からの値を与えます(訳注: スタック効果の通り、 読み取りのみ)。

c-variable ( "forth-name" "c-name" –  ) gforth-1.0 “c-variable”

Forth ワード forth-name を定義します。 Forth-namec-name のアドレスを返します。

この C言語インターフェイスが機能するために、 実行時に GCC を呼び出し、 動的リンク(dynamic linking)を使用します。 これらの機能が利用できない場合は、 lib.fsoldlib.fs に、利便性と移植性に劣る他の C 言語インターフェイスが用意されています。 これらのインターフェイスはほとんど文書化されておらず、 また文書化された C言語インターフェイスとほとんど互換性がありません。 あなたが例を見たければ、 lib.fs インターフェイスの例が lib.fs にあります。


6.28.3 Calling C function pointers from Forth

C言語の関数へのポインター(たとえば、 C言語で構築された構造体など)を見つけて、 それを Forth プログラムから呼び出したい場合は、 マクロを定義することで上記の構造体を使用できます。 または、 c-funptr を使用します。

c-funptr ( "forth-name" <{>"c-typecast"<}> "{type}" "–" "type" –  ) gforth-1.0 “c-funptr”

Forth ワード Forth-name を定義します。 Forth-name には、 その後続に記述されたスタック効果と、 スタックのTOSに置かれた呼び出されたポインター(the called pointer)(つまり {type} ptr – type )があり、 型キャスト(typecast)または構造体アクセス(c-typecast)を使用して C 言語の関数へのポインター ptr を呼び出します。

ヘッダー・ファイル func1.h に C言語の関数ポインター型 func1 が定義されており、 これらの関数は 1 つの整数引数を受け取り、 整数の結果を返すことがわかっているとします。 そして、 そのようなポインターを介して関数を呼び出したいとします。 あなたは以下のように定義するだけです

\c #include <func1.h>
c-funptr call-func1 {((func1)ptr)} n -- n

そして、 例えば、 以下のようにして func1-addr が指す関数を呼び出すことができます:

-5 func1-addr call-func1 .

Forth ワード call-func1execute に似ていますが、Forth 実行トークンの代わりに C 言語の func1 ポインターを使用し、 func1 ポインタ固有である点が異なります。 Forth から呼び出したい C 言語の関数ポインターのタイプごとに、 個別の呼び出しワードを定義する必要があります。


6.28.4 Defining library interfaces

以下のように、 一連の C 言語関数宣言(ライブラリー・インターフェイス)に名前を付けることができます:

c-library lseek-lib
\c #define _FILE_OFFSET_BITS 64
...
end-c-library

インターフェイスにこのような名前を付けると、 生成されるファイルの名前にその名前が含まれるようになり、 インターフェイスを 2 回目に使用するときに、 再度生成してコンパイルする代わりに既存のファイルが使用されるため、時間を節約できます。 生成されたファイルにはソース・コードの 128 ビット・ハッシュ(暗号的に安全ではありませんが、 この目的には十分である)が含まれているため、 宣言を変更すると新しいコンパイルが発生します。 通常、 これらのファイルは $HOME/.gforth/architecture/libcc-named にキャッシュされるため、 問題が発生した場合、 または再コンパイルの強制が必要なその他の理由がある場合は、そこにあるファイルを削除してください。

注意: c-library を使用すると、 一部の設定がリセットされるため、 そのライブラリに関連する他のすべての処理の前に c-library を使用する必要があることに注意してください。 c-library の考え方は、 一般的な使用法としては、 各 c-library...end-c-library ユニットを独自のファイルに配置し、 これらのファイルを任意の順序でインクルードできるようにすることです。 C 言語インターフェースを扱う他のすべてのワードは、 ボキャブラリー c-lib に隠されており、 c-library によって検索順序スタックの最上位に置かれ、 end-c-library によって検索順序スタックの最上位から削除されます。

注意: ライブラリー名はディクショナリーに割り当てられないため、 ディクショナリー内の名前に影響を与えないことに注意してください。 ファイル・システムで使用されるため、 ファイル・システムに適した命名規則を使用する必要があります。 この名前はC言語のシンボルの一部としても使用されますが、 正当なC言語のシンボル名以外の文字はアンダースコアに置き換えられます。 また、 c-library の後で宣言した関数を end-c-library を実行する前に呼び出してはなりません。

これらの名前付きライブラリー・インターフェイスの主な利点は、 それらが生成されると、 その生成に使用したツール(特に C 言語のコンパイラーと libtool)が不要になるため、 ツールがインストールされていないマシンでもインターフェイスを使用できることです。 Gforth のビルド・システムはこれらのライブラリーをクロス・コンパイルすることもできるため、 ビルド・ツールがインストールされていないプラットフォームでもライブラリーを利用できます。

c-library-name ( c-addr u –  ) gforth-0.7 “c-library-name”

c-addr u という名前で C 言語ライブラリー・インターフェイスを開始。

c-library ( "name" –  ) gforth-0.7 “c-library”

c-library-name のパース対応(parsing)バージョンです

end-c-library ( ) gforth-0.7 “end-c-library”

直近の C 言語ライブラリー・インターフェイスを終了し、 (必要に応じて)ビルドします。


6.28.5 Declaring OS-level libraries

一部の C 言語の関数を呼び出すには、 その関数を含む特定の OS レベルのライブラリーとリンクする必要があります。 たとえば、sin 関数では、 コマンドライン・スイッチ -lm を使用して特別なライブラリーをリンクする必要があります。 C 言語インターフェイスでは、 以下のように add-lib を呼び出して同等のことを行います:

clear-libs
s" m" add-lib
\c #include <math.h>
c-function sin sin r -- r

まず、 (あなたが sin に必要としない、)以前に宣言された可能性のあるライブラリーをすべてクリアします。 次に m ライブラリー(実際には libm.so など) を現在宣言されているライブラリーに追加します。 これは必要なだけ追加できます。 最後に、 上に示したように関数を宣言します。 通常は、 多くの関数宣言に同じライブラリー宣言のセットを使用します。 あなたは、 最初に、 この組を、 ただ 1 つだけ作成する必要があります。

注意: c-library...end-c-library 内で clear-libs を呼び出してはいけないことに注意してください。 しかしながら、 c-libraryclear-libs の機能も実行するため、 clear-libs は必要なく、 そして、 あなたは、 通常は add-lib 呼び出しを c-library...end-c-library 内に置きたいと思うでしょう。

clear-libs ( ) gforth-0.7 “clear-libs”

libs のリストをクリア

add-lib ( c-addr u –  ) gforth-0.7 “add-lib”

ライブラリー libstring をライブラリーのリストに追加します。ここで、 stringc-addr u で表されます。

add-libpath ( c-addr u –  ) gforth-0.7 “add-libpath”

パス string をライブラリー検索パスのリストに追加します。 ここで、stringc-addr u で表されます。

add-framework ( c-addr u –  ) gforth-1.0 “add-framework”

フレームワーク libstring をフレームワークのリストに追加します。 ここで、stringc-addr u で表されます。

add-incdir ( c-addr u –  ) gforth-1.0 “add-incdir”

パス c-addr u をインクルード検索パスのリストに追加します

add-cflags ( c-addr u –  ) gforth-1.0 “add-cflags”

あらゆる種類の cflags をC言語コンパイラーに追加します

add-ldflags ( c-addr u –  ) gforth-1.0 “add-ldflags”

リンカー(linker)にフラグを追加


6.28.6 Callbacks

場合によっては、 C言語の関数へ関数のポインターを渡す必要があります。 つまり、 ライブラリーがアプリケーションにコールバックしたい場合です(ポイント先の関数はコールバック関数と呼ばれます)。 既存のC言語の関数のアドレス(lib-sym で取得。 see Low-Level C Interface Words)を渡すことができますが、 適切な C言語の関数がない場合、 あなたは Forth ワードをC言語の関数として定義したい筈です。 その際は、 下記にて説明する、 コールバックを生成する必要があります:

c-callback を使用して Forth コードからC言語のコールバックを生成できます。

c-callback ( "forth-name" "{type}" "–" "type" –  ) gforth-1.0 “c-callback”

指定のシグネチャ forth-name を使用してコールバック・インスタンス化器(instantiator;インスタンシエーター)を定義します。 コールバック・インスタンス化器 forth-name ( xt -- addr ) は、 xt を受け取り、 そのコールバックを処理する C言語関数の addr を返します。

c-callback-thread ( "forth-name" "{type}" "–" "type" –  ) gforth-1.0 “c-callback-thread”

指定のシグネチャ forth-name を使用してコールバック・インスタンス化器を定義します。 コールバック・インスタンス化器 forth-name ( xt -- addr ) は、 xt を受け取り、 そのコールバックを処理する C言語の関数の addr を返します。 このコールバックは、 別のスレッドから呼び出された場合に安全です

これにより、 (value callback# までの)多数のコールバック関数がプリコンパイルされます。 C言語の関数プロトタイプは、 Forth シグネチャから推定されます。 これでは不十分な場合は、 Forth 型の後に波括弧({})で囲んで型を追加できます。

c-callback vector4double: f f f f -- void
c-callback vector4single: f{float} f{float} f{float} f{float} -- void

6.28.7 How the C interface works

文書化されたC言語インターフェイスは、 宣言より C 言語のコードを生成することによって機能します。

特に、 c-function で宣言されたすべての Forth ワードに対して、 Forth スタックから Forth データを取得するラッパー関数をC言語で生成し、 これらのデータを引数としてターゲットのC言語の関数を呼び出します。 次に、 C言語のコンパイラーは、 スタックからの Forth 型と、 C言語の関数プロトタイプによって指定されるパラメーターのC言語の型、 の間で暗黙的な変換を実行します。 C言語の関数から戻った後、 戻り値も同様に暗黙的に Forth 型に変換され、 スタックに書き戻されます。

\c 行はそのままC言語のコードに含まれており(ただし、\c は含まれません)、 C言語のコンパイラーがC言語の型を認識し、 そして、 変換を実行するのに十分な情報を得るために必要な宣言を提供します。

これらのラッパー関数は最終的にコンパイルされ、そして、 Gforth に動的にリンクされ、 そして、 呼び出すことができるようになります。

add-lib で追加されたライブラリーは、 コンパイラーのコマンド・ラインで -llib で依存ライブラリーを指定するために使用され、 ラッパー関数がリンクされるときにこれらのライブラリーが動的にリンクされます。


6.28.8 Low-Level C Interface Words

open-lib ( c-addr1 u1 – u2 ) gforth-0.4 “open-lib”
\ 訳注: 指定の名前のライブラリーを開く。 u2 にハンドルを返す。
エラーの場合は u2 に 0 を返し、 lib-error で参照できるエラーメッセージをセットする。
lib-sym ( c-addr1 u1 u2 – u3 ) gforth-0.4 “lib-sym”
\ 訳注: open-lib で取得したハンドル u2 を使って
 シンボル文字列 c-addr1 u1 のシンボル・テーブルのアドレスを u3 に返す。
エラーの場合は u3 に 0 を返し、 lib-error で参照できるエラーメッセージをセットする。
lib-error ( – c-addr u ) gforth-0.7 “lib-error”

最後に失敗した open-lib または lib-sym のエラー・メッセージ。

call-c ( ... w – ... ) gforth-0.2 “call-c”

w が指す C言語の関数を呼び出します。 C言語関数はそれ自身でスタックにアクセスする必要があります。 スタック・ポインターはC言語の関数に渡される ptrpair 構造体にエクスポートされ、 その形式でC言語関数から返されます。


6.28.9 Automated interface generation using SWIG

SWIG (Simple Wrapper Interface Generator)は、 多くのプログラミング言語でC言語インターフェイスを作成するために使用されています。 Forth モジュールで拡張された SWIG バージョンは、 github にあります。

6.28.9.1 Basic operation

C言語のヘッダーはパースされ、 前述のC言語インターフェイス関数を使用する Forth のソース・コードに変換されます。

6.28.9.2 Detailed operation:

  1. ターゲットを選択します。 この例では example.h を使用しています。
  2. ヘッダー用のインターフェイス・ファイルを作成します。 これは、 オプションと、スイッチと、変数の定義を、 渡すために使用できます。 最も単純なケースでは、example.h のすべてを変換(translate)するよう指示するだけです:
    %module example
    %insert("include")
    {
        #include "example.h"
    }
    %include "example.h"
    
  3. SWIG を使用して .fsi-c ファイルを作成します:
    swig -forth -stackcomments -use-structs -enumcomments -o example-fsi.c example.i
    とします。FSI は「Forth Source Independent」の略で、 C言語コンパイラーを備えた任意のホストに転送できることを意味します。 この時点以降、 SWIG は必要ありません。
  4. ターゲット・マシンで .fsi-c ファイルを .fsx にコンパイルします(x は executable(実行可能)の意味です):
    gcc -o example.fsx example-fsi.c
    こうすると、 すべての定数(constant)がターゲット上の値(value)に解決されます。
  5. 最後のステップは、 実行可能ファイルを実行し、 その出力を Forth のソース・ファイルである .fs ファイルに取り込むことです:
    ./example.fsx -gforth > example.fs
    これで、 このコードはターゲット・プラットフォームで使用できるようになりました。

6.28.9.3 Examples

いくつかの例: SWIG’s Forth Example section

たくさんのインターフェイス・ファイルがこちらにあります: Forth Posix C-InterfaceForth C-Interface Modules

Forth C-Interface Module repository への貢献はいつでも歓迎です!


6.28.10 Migrating from Gforth 0.7

このバージョンでは、 \cc-functionadd-libc-library...end-c-library 内でのみ使用できます。 add-lib は常に c-library 内の白紙の状態から開始されるため、 ほとんどの場合 clear-libs を使用する必要はありません。

c-library...end-c-library の外側でこれらのワードを使用するプログラムがあった場合は、 あなたは、 それらを c-library...end-c-library で囲むだけです。 ただし、 add-lib のインスタンスをいくつか追加する必要がある場合があります。


6.29 Assembler and Code Words


6.29.1 Definitions in assembly language

Gforth は、 アセンブリ言語でワードを実装する方法(abi-codeend-code を使用)と、 任意の実行時の振る舞いを持つ定義ワードを定義する方法(does> のようなの)を提供し、 ここで、 この実行時の振る舞いを(does> とは異なり、) Forth ではなくアセンブリ言語で定義します。

ただし、 Gforth のマシン非依存の性質により、 いくつかの問題が生じます。 まず、 Gforth は複数のアーキテクチャー上で実行されるため、 標準のアセンブラーを提供できません。 ただし、 実行されるいくつかのアーキテクチャー用のアセンブラーは提供されています。 さらに言えば、 Gforth ではシステムに依存しないアセンブラーを使用したり、 ,c, を使用してマシン・コードを直接コンパイルしたりできます。

もう 1 つの問題は、Gforth の仮想マシンのレジスター(スタック・ポインターと仮想マシン命令ポインター)がインストールとエンジンに依存することです。 また、 どのレジスタを自由に使用できるかは、 インストールとエンジンによって異なります。 したがって、 Gforth 仮想マシンのコンテキストで実行するように記述されたコードは、 基本的に、 そのコードが開発されたインストールとエンジンに限定されます(たまたま、 他の場所でも動く可能性はありますが、 それに頼ることはできません)。

幸いなことに、 同じ呼び出し規約(ABI)を持つプラットフォーム上で実行されている Gforth に移植可能(portable)な abi-code ワードを Gforth で定義できます。 通常、 これは同じアーキテクチャーと OS の組み合わせへの移植性を意味し、 しばしば OS の境を越えることができます。

assembler ( ) tools-ext “assembler”

ボキャブラリー: 検索順序スタック(the search order)のTOSのワードリストを assembler ワードリストに置き換えます。

init-asm ( ) gforth-0.2 “init-asm”

assembler ワードリストを検索順序スタック(the search order)にプッシュします(訳注:つまり assembler ワードリストが検索順序スタックのTOSになる)。

abi-code ( "name" – colon-sys  ) gforth-1.0 “abi-code”

C言語プロトタイプ(C-prototype)に対応するプラットフォームの ABI 規則を使用して呼び出されるネイティブ・コード定義を開始します:

Cell *function(Cell *sp, Float **fpp);

ここで、 FP スタック・ポインターは、 FP スタック・ポインターを含むメモリー位置への参照を提供することによって渡され、 (必要な場合)変更された FP スタック・ポインターをそこに格納することによって渡されます。

;abi-code ( ) gforth-1.0 “semicolon-abi-code”

コロン定義を終了しますが、 実行時に最後に定義されたワード X (create で作られたワードである必要があります)の変更もして、 C言語プロトタイプに対応するプラットフォームの ABI 規約を使用してネイティブ・コードを呼び出します:

Cell *function(Cell *sp, Float **fpp, Address body);

FP スタック・ポインターは、 FP スタック・ポインターを含むメモリー位置への参照を提供することによって渡され、 変更された FP スタック・ポインターをそこに格納することによって渡されます(必要な場合)。 パラメーター bodyX の本体です。

end-code ( colon-sys –  ) gforth-0.2 “end-code”

コード定義を終了します。 ABI 呼び出しからの戻り(abi-code の場合)、 または次の VM 命令へのディスパッチ(code および ;code の場合)を自分でアセンブルする必要があることに注意してください。

code ( "name" – colon-sys  ) tools-ext “code”

Gforth 仮想マシン(エンジン)のコンテキストで実行されるネイティブ・コード定義を開始します。 このような定義は Gforth インストール間で移植できないため、 code の代わりに abi-code を使用することをお勧めします。 code 定義は、 次の仮想マシン命令へのディスパッチで終了する必要があります。

;code ( compilation. colon-sys1 – colon-sys2  ) tools-ext “semicolon-code”

;code の後のコードが、 最後に定義されたワード(create されたワードである必要があります) の振る舞いになります。 code の場合と同じ注意事項が適用されるため、 代わりに ;abi-code を使用することをお勧めします。

flush-icache ( c-addr u – ) gforth-0.2 “flush-icache”

プロセッサの命令キャッシュ(存在する場合)の c-addru バイト以降に古いデータが含まれていないことを確認してください。 END-CODEflush-icache を自動的に実行します。 注意(Caveat): flush-icache はあなたのインストール環境では機能しない可能性があります。 これは通常、 マシンでダイレクト・スレッドがサポートされておらず(machine.h を確認してください)、 マシンに別個の命令キャッシュがある場合に当てはまります。 このような場合、 flush-icache は命令キャッシュをフラッシュする代わりに何も行いません。

flush-icache が正しく動作しない場合、 abi-code ワードなども(まず確実に)動作しません。

これらのワードの一般的な使用法は、 同等の高レベルの定義ワードから類推することで最も簡単に示すことができます:

: foo                              abi-code foo
   <high-level Forth words>              <assembler>
;                                  end-code
                                
: bar                              : bar
   <high-level Forth words>           <high-level Forth words>
   CREATE                             CREATE
      <high-level Forth words>           <high-level Forth words>
   DOES>                              ;code
      <high-level Forth words>           <assembler>
;                                  end-code

abi-code を使用する場合は、 あなたのプラットフォームの ABI ドキュメントを参照して、 パラメーターがどのように渡されるか(スタック・ポインターをどこで取得するかがわかります)、 戻り値がどのように渡されるか(データ・スタック・ポインターがどこに返されるかがわかります)を確認してください。 ABI のドキュメントでは、 どのレジスターが呼び出し元によって保存されるか(caller-saved)や、 コード内で自由に破棄できるかや、 どのレジスターが呼び出されたワードによって保存される必要があるか(callee-saved)についても説明されています。 あなたはそれらを使用する前に保存し、 後で復元します。 一部のアーキテクチャーと OS については、適切なセクションで呼び出し規約の各部分の短い概要を示します。 リバース・エンジニアリング志向の人々は、 see abi-call を通じてスタック・ポインターの受け渡しと戻りについて知ることもできます。

ほとんどの ABI はレジスターを介してパラメーターを渡しますが、 一部の ABI(特に最も一般的な 386 (別名 IA-32) 呼び出し規約) はアーキテクチャー・スタック上でパラメータを渡します。 共通の ABI はすべてレジスターで戻り値を渡します。

abi-code を使用する際に知っておく必要があるその他のことは、 Gforth ではデータと FP スタックの両方が下方向(下位アドレスに向かって)に成長し、 セルあたりのサイズは 1 cells になり、 FP 値ごとのサイズは 1 floats になるということです。

386 アーキテクチャーで abi-code を使用する例を以下に示します:

abi-code my+ ( n1 n2 -- n )
4 sp d) ax mov \ sp into return reg
ax )    cx mov \ tos
4 #     ax add \ update sp (pop)
cx    ax ) add \ sec = sec+tos
ret            \ return from my+
end-code

この例の AMD64 バリエーションは AMD64 (x86_64) Assembler にあります。

386 で FP 値を扱う例を以下に示します:

abi-code my-f+ ( r1 r2 -- r )
8 sp d) cx mov  \ load address of fp
cx )    dx mov  \ load fp
.fl dx )   fld  \ r2
8 #     dx add  \ update fp
.fl dx )   fadd \ r1+r2
.fl dx )   fstp \ store r
dx    cx ) mov  \ store new fp
4 sp d) ax mov  \ sp into return reg
ret             \ return from my-f+
end-code

6.29.2 Common Assembler

Gforth のアセンブラーは通常、 後置構文(postfix syntax)を使用します。 つまり、命令名がオペランドの後に続きます。

オペランドは通常の順序(アーキテクチャーのマニュアルで使用されている順序と同じ)で渡されます。 これらはすべて Forth ワードであるため、 スペースで区切る必要があります。 Forth ワードを使用してオペランドを計算することもできます。

通常、 命令名は , で終わります。 これにより、 複数の命令を 1 行に配置した場合に、 命令を視覚的に分離しやすくなります。 また、 他の Forth ワード (例: and) が隠されてしまう(shadowing)のも回避します。

レジスターは通常、 番号で指定します。 たとえば、 (10 進数の) 11 は、 Alpha アーキテクチャー上のレジスター R11 および F11 を指定します(どちらかは命令によって異なります)。 通常の名前も使用できます(例: Alpha の R11 の s2)。

制御フローは、 通常の Forth コード(see Arbitrary control structures)と同様に、 if,, ahead,, then,, begin,, until,, again,, cs-roll, cs-pick, else,, while,, repeat, で指定します。 条件は各アセンブラーに固有の方法で指定されます。

このセクションの残りの部分は、 (移植性の高い abi-code ワードではなく、)主に code ワードを定義したい人にとって興味深いものです。

注意: Gforth エンジンのレジスター割り当ては、 Gforth バージョン間、 または同じ Gforth バージョンの異なるコンパイル間でも変更される可能性があることに注意してください(たとえば、 異なる GCC バージョンを使用する場合)。 ABI-CODE の代わりに CODE を使用していて、 Gforth のレジスター (スタック・ポインターや TOS など) を参照したい場合は、 これらのレジスターを参照するための独自のワードを定義して使用することをお勧めします。 そうすれば、 変更されたレジスター割り当てに適応できます。

これらのレジスターの最も一般的な使用法は、 code 定義を次のワードへのディスパッチ(next ルーチン)で終了することです。 これを行う移植可能な方法は、' noop >code-address にジャンプすることです(もちろん、 これは next コードを統合して適切にスケジュールするほど効率的ではありません)。 ABI-CODE を使用する場合は、 通常のサブルーチンの戻り値をアセンブルするだけです(ただし、 必ずデータ・スタック・ポインターを返すようにしてください)。

Gforth バージョン間のもう 1 つの違いは、 ほとんどのプラットフォームではスタックの最上位が gforth のメモリーに保持され、 gforth-fast ではレジスタに保持されることです。 ABI-CODE 定義の場合、 スタック・キャッシュ・レジスターは必ずスタックにフラッシュされるため、 メモリー内のスタックの先頭に確実にアクセスできます。


6.29.3 Common Disassembler

code ワードは see で逆アセンブルできます(see Debugging)。 以下を使用してメモリーのセクションを逆アセンブルできます

discode ( addr u –  ) gforth-0.2 “discode”

逆アセンブラーのフック: addr から u バイトのコードを逆アセンブルします。

Gforth には 2 種類の逆アセンブラーがあります。 Forth 逆アセンブラー(一部の CPU で利用可能) と gdb 逆アセンブラー(gdb および mktemp を備えたプラットフォームで利用可能)です。 両方が使用可能な場合は、 Forth 逆アセンブラがデフォルトで使用されます。 gdb 逆アセンブラーを使いたい場合は、 以下のようにします

' disasm-gdb is discode

どちらも使用できない場合は、discodedump を実行します。

Forth 逆アセンブラーは通常、 アセンブラーに入力できる出力(つまり、同じ構文など)を生成します。 コメントには追加情報も含まれます。 特に、 命令のアドレスは命令の前のコメントで示されます。

gdb 逆アセンブラーは、 デフォルトの形式(flavour)(386 および AMD64 アーキテクチャの AT&T 構文)で、 gdb disassemble コマンドと同じ形式で出力を生成します(see Source and machine code in Debugging with GDB)。

see は、 コードの終わりの認識が信頼できないため、 ワードの実際のコードより多めに表示したり少なめに表示したりする場合があります。 十分に表示されない場合は、 discode を使用できます。 コード・ワードの直後に名前付きワードが続かない場合は、 さらに表示される可能性があります。 他に何かがある場合は、 ワードの後に align latest , を付けると、 末尾が認識されるようになります。


6.29.4 386 Assembler

Gforth に含まれる 386 アセンブラーは Bernd Paysan によって書かれ、 GPL の下で入手可能で、 元々は bigFORTH の一部でした。

Gforth に含まれる 386 逆アセンブラーは Andrew McKewan によって作成され、 パブリック・ドメインです。

逆アセンブラーは、 Intel のようなプレフィックス構文(Intel-like prefix syntax)でコードを表示します。

アセンブラーは、 AT&T スタイルのパラメーター順序(つまり、宛先が最後;destination last)で後置構文(postfix syntax)を使用します。

アセンブラーには、 Athlon のすべての命令、 つまり 486 コア命令や、Pentium および PPro 拡張機能や、浮動小数点、MMX、3Dnow! が含まれていますが、 ISSE は含まれていません。 これは、16 ビットおよび 32 ビットの統合アセンブラーです。デフォルトは 32 ビットですが、 .86 で 16 ビットに切り替え、.386 で 32 ビットに戻すことができます。

異なる操作サイズを切り替えるためのプレフィックスがいくつかあります。 バイト・アクセスの場合は .b 、 ワード・アクセスの場合は .w 、 ダブルワード・アクセスの場合は .d です。 アドレッシング・モードは、 16 ビットアドレスの場合は .wa 、32 ビットアドレスの場合は .da で切り替えることができます。 (AL など、 )バイト・レジスター名のプレフィックスは必要ありません 。

浮動小数点演算の場合、 プレフィックスは .fs (IEEE single)、.fl (IEEE double)、.fx (extended)、.fw (word)、 .fd (double-word)、 .fq (quad-word)。 デフォルトは .fx であるため、 Gforth FP 値を扱うときは .fl を明示的に指定する必要があります。

MMX オペコードにはサイズのプレフィックスはなく、 Intel アセンブラーと同じように記述されます。 メモリー間での移動の代わりに、 PLDQ/PLDD と PSTQ/PSTD があります。

レジスターには「e」接頭辞がありません。 32 ビット モードでも、 eax は ax と呼ばれます。 即値は、# を接尾辞として付けることによって示されます(例: 3 #)。 以下に、 さまざまな構文でのアドレス指定モードの例をいくつか示します:

Gforth          Intel (NASM)   AT&T (gas)      Name
.w ax           ax             %ax             register (16 bit)
ax              eax            %eax            register (32 bit)
3 #             offset 3       $3              immediate
1000 #)         byte ptr 1000  1000            displacement
bx )            [ebx]          (%ebx)          base
100 di d)       100[edi]       100(%edi)       base+displacement
20 ax *4 i#)    20[eax*4]      20(,%eax,4)     (index*scale)+displacement
di ax *4 i)     [edi][eax*4]   (%edi,%eax,4)   base+(index*scale)
4 bx cx di)     4[ebx][ecx]    4(%ebx,%ecx)    base+index+displacement
12 sp ax *2 di) 12[esp][eax*2] 12(%esp,%eax,2) base+(index*scale)+displacement

D)DI) の代わりに L)LI) を使用して、32 ビット displacement フィールドを強制できます(後のパッチ適用に役立ちます)。

いくつかの命令の例は以下のとおりです:

ax bx mov             \ move ebx,eax
3 # ax mov            \ mov eax,3
100 di d) ax mov      \ mov eax,100[edi]
4 bx cx di) ax mov    \ mov eax,4[ebx][ecx]
.w ax bx mov          \ mov bx,ax

バイナリ命令では以下の形式がサポートされています:

<reg> <reg> <inst>
<n> # <reg> <inst>
<mem> <reg> <inst>
<reg> <mem> <inst>
<n> # <mem> <inst>

シフト/ローテート の構文は以下のとおりです:

<reg/mem> 1 # shl \ shortens to shift without immediate
<reg/mem> 4 # shl
<reg/mem> cl shl

バイト・バージョンを取得するには、 (movs などの)文字列命令(string instructions)の前に .b を付けます。

制御構造ワードの IFUNTIL などの前に、 次の条件のいずれかを指定する必要があります: vs vc u< u>= 0= 0<> u<= u> 0< 0>= ps pc < >= <= > (注意: code ワードなど、 検索パス内で assemblerforth の前にある場合、 これらのワードのほとんどは Forth ワードの一部を隠してしまう事に注意してください)。 現在、 制御構造ワードは 1 つのスタック項目を使用しているため、 それらをいじるには cs-roll の代わりに roll を使用する必要があります(swap などを使用することもできます)。

Intel ABI (Linux で使用)に基づいて、abi-code ワードは 4 sp d) でデータ・スタック・ポインターを見つけ、 8 sp d) で FP スタック・ポインターのアドレスを見つけることができます; データ・スタック・ポインターは ax で返されます。 Axcxdx は呼び出し元で保存されるため、 ワード内に値を保存する必要はありません。 ret を使用してワードから戻ることができます。 パラメーターは呼び出し元によってクリーンアップされます。

386 の abi-code ワードの例については、 Definitions in assembly language


6.29.5 AMD64 (x86_64) Assembler

AMD64 アセンブラーは、 386 アセンブラーをわずかに変更したバージョンであり、 構文の大部分を共有しています。 2 つの新しい接頭辞 .q.qa が、 それぞれ 64 ビット・サイズのオペランドやアドレスを選択するために提供されています。 64 ビット・サイズがデフォルトであるため、 通常は他のプレフィックスを使用するだけで済みます。 また、 追加のレジスター・オペランド R8R15 もあります。

レジスターには「e」または「r」プレフィックスがありません。 64 ビット・モードでも、 raxax と呼ばれます。 すべてのレジスターで最下位バイトを参照するために追加のレジスター・オペランドを使用できます: R8L 〜 -R15L, SPL, BPL, SIL, DIL

Linux-AMD64 の呼び出し規則では、 最初の 6 つの整数パラメーターを rdi, rsi, rdx, rcx, r8, r9 で渡し、 結果を rax , rdx で返します。 最初の 8 つの FP パラメータを xmm0 ~ xmm7 に渡し、 FP 結果を xmm0 ~ xmm1 に返します。 したがって、abi-code ワードは、 di でデータ・スタック・ポインターを取得し、 si で FP スタック・ポインターのアドレスを取得し、 リターン時は ax にデータ・スタック ポインターをセットします。 呼び出し元が保存する他のレジスターは、 r10, r11 xmm8 ~ xmm15 です。 この呼び出し規約は、 Microsoft 以外の他の OS でも使用されていると報告されています。

Windows x64 は、 最初の 4 つの整数パラメーターを rcx, rdx, r8, r9 に渡し、 整数の結果を rax に返します。 他の、呼び出し元保存レジスターは r10 と r11 です。

https://uclibc.org/docs/psABI-x86_64.pdf の 21ページによると、 Linux プラットフォームでは、 レジスター AX CX DX SI DI R8 R9 R10 R11 が自由(scratch)に使えます。

AMD64 のアドレッシング・モードは以下のとおりです:

\ ご注意: ワード A を実行すると、レジスターが初期化されていないため、 メモリー・エラーが発生します ;-)
ABI-CODE A  ( -- )
    500        #               AX  MOV     \ immediate
        DX              AX  MOV     \ register
        200             AX  MOV     \ direct addressing
        DX  )           AX  MOV     \ indirect addressing
    40  DX  D)          AX  MOV     \ base with displacement
        DX  CX      I)  AX  MOV     \ scaled index
        DX  CX  *4  I)  AX  MOV     \ scaled index
    40  DX  CX  *4  DI) AX  MOV     \ scaled index with displacement

        DI              AX  MOV     \ SP Out := SP in
                            RET
END-CODE

AMD64 abi-code ワードの例をいくつか示します:

abi-code my+  ( n1 n2 -- n3 )
\ SP passed in di, returned in ax,  address of FP passed in si
8 di d) ax lea        \ compute new sp in result reg ( 結果として di+8 → ax つまり drop と同じ)
di )    dx mov        \ get old tos ( [di] つまり n2   → dx )
dx    ax ) add        \ add to new tos ( dx + [ax] → [ax]
ret
end-code
\ Do nothing
ABI-CODE aNOP  ( -- )
       DI  )       AX      LEA          \ SP out := SP in  
                           RET
END-CODE
\ Drop TOS
ABI-CODE aDROP  ( n -- )
   8   DI  D)      AX      LEA          \ SPout := SPin - 1
                           RET
END-CODE
\ Push 5 on the data stack
ABI-CODE aFIVE   ( -- 5 )
   -8  DI  D)      AX      LEA          \ SPout := SPin + 1
   5   #           AX  )   MOV          \ TOS := 5
                           RET
END-CODE
\ Push 10 and 20 into data stack
ABI-CODE aTOS2  ( -- n n )
   -16 DI  D)      AX      LEA          \ SPout := SPin + 2
   10  #       8   AX  D)  MOV          \ TOS - 1 := 10
   20  #           AX  )   MOV          \ TOS := 20
                           RET
END-CODE
\ Get Time Stamp Counter as two 32 bit integers
\ The TSC is incremented every CPU clock pulse
ABI-CODE aRDTSC   ( -- TSCl TSCh )
                           RDTSC        \ DX:AX := TSC
   $FFFFFFFF #     AX      AND          \ Clear upper 32 bit AX
  0xFFFFFFFF #     DX      AND          \ Clear upper 32 bit DX
       AX          R8      MOV          \ Tempory save AX
   -16 DI  D)      AX      LEA          \ SPout := SPin + 2
       R8      8   AX  D)  MOV          \ TOS-1 := saved AX = TSC low
       DX          AX  )   MOV          \ TOS := Dx = TSC high
                           RET
END-CODE
\ Get Time Stamp Counter as 64 bit integer
ABI-CODE RDTSC   ( -- TSC )
                           RDTSC        \ DX:AX := TSC
   $FFFFFFFF #     AX      AND          \ Clear upper 32 bit AX
   32  #           DX      SHL          \ Move lower 32 bit DX to upper 32 bit
       AX          DX      OR           \ Combine AX wit DX in DX
   -8  DI  D)      AX      LEA          \ SPout := SPin + 1
       DX          AX  )   MOV          \ TOS := DX
                           RET
END-CODE
VARIABLE V

\ Assign 4 to variable V
ABI-CODE V=4 ( -- )
       BX                  PUSH         \ Save BX, used by gforth
   V   #           BX      MOV          \ BX := address of V
   4   #           BX )    MOV          \ Write 4 to V
       BX                  POP          \ Restore BX
       DI  )       AX      LEA          \ SPout := SPin
                           RET
END-CODE
VARIABLE V

\ Assign 5 to variable V
ABI-CODE V=5 ( -- )
   V   #           CX      MOV          \ CX := address of V
   5   #           CX )    MOV          \ Write 5 to V
   DI )            AX      LEA          \ SPout := SPin
                           RET
END-CODE
ABI-CODE TEST2  ( -- n n )
   -16 DI  D)  AX          LEA          \ SPout := SPin + 2
   5   #       CX          MOV          \ CX := 5
   5   #       CX          CMP
   0= IF
       1   #   8   AX  D)      MOV      \ If CX = 5 then TOS - 1 := 1  <--
   ELSE
       2   #   8   AX  D)      MOV      \ else TOS - 1 := 2
   THEN
   6   #       CX          CMP
   0= IF
       3   #       AX  )       MOV      \ If CX = 6 then TOS := 3
   ELSE
       4   #       AX  )       MOV      \ else TOS := 4  <--
   THEN
                           RET
END-CODE
\ Do four loops. Expect : ( 4 3 2 1 -- )
ABI-CODE LOOP4  ( -- n n n n )
       DI          AX      MOV          \ SPout := SPin
   4   #           DX      MOV          \ DX := 4  loop counter
   BEGIN
       8   #           AX      SUB      \ SP := SP + 1
           DX          AX  )   MOV      \ TOS := DX
       1   #           DX      SUB      \ DX := DX - 1
   0= UNTIL
                           RET
END-CODE

以下は、FP 値を扱う AMD64 用の例です:

abi-code my-f+  ( r1 r2 -- r )
\ SP passed in di, returned in ax,  address of FP passed in si
si )       dx mov         \ load fp
8 dx d)  xmm0 movsd       \ r2
dx )     xmm0 addsd       \ r1+r2
xmm0  8 dx d) movsd       \ store r
8 #      si ) add         \ update fp
di         ax mov         \ sp into return reg
ret
end-code

6.29.6 Alpha Assembler

Alpha アセンブラーと逆アセンブラーは、もともとは Bernd Thallner によって書かれました。

レジスター名 a0a5 は、 16 進数を隠してしまう(shadowing)のを避けるために使用できません。

算術命令の即時形式は、 , の直前の # によって区別されます(例: and#,)(注: lda, は算術命令としてカウントされません)。

他のアセンブラーがオプションとみなすオペランドも含めて、 命令にすべてのオペランドを指定する必要があります。 たとえば、br, の宛先レジスター、 または jmp, の宛先レジスターとヒントです。

if, の条件を指定するには、 対応する名前のブランチから最初の b と末尾の , を削除します。 たとえば、以下のようにします:

11 fgt if, \ if F11>0e
  ...
endif,

fbgt,fgt になります。


6.29.7 MIPS assembler

MIPS アセンブラーは、もともとは Christian Pirker によって書かれました。

現在、 アセンブラーと逆アセンブラーは MIPS32 アーキテクチャーの大部分をカバーしていますが、 FP 命令はサポートしていません。

レジスター名 $a0$a3 は、 16 進数を隠してしまう(shadowing)のを避けるために使用できません。 代わりにレジスター番号 $4$7 を使用してください。

レジスターと即値を区別するものはありません。 即値引数を持つ命令には、 i 接尾辞を付けた明示的なオペコード名を使用します。 例えば。 addu, の代わりに addiu, です。

アーキテクチャー・マニュアルで命令の形式が複数指定されている場合(例: jalr, の場合)、より多くの引数を持つ形式(つまり、jalr, の場合は 2 つ)を使用してください。 疑問がある場合は、 正しい使用例について arch/mips/testasm.fs を参照してください。

MIPS アーキテクチャーの分岐とジャンプには遅延スロット(delay slot)があります。 手動で入力する必要があります(最も簡単な方法は nop, を使用することです)。 アセンブラーは(as とは異なり)自動的に入力しません。 if,, ahead,, until,, again,, while,, else,, repeat, でも遅延スロットが必要です。 begin,then, は分岐ターゲットを指定するだけなので影響を受けません。 ブランチの場合、 ターゲットを指定する引数は相対アドレスです。 遅延スロットのアドレスを追加して絶対アドレスを取得します。

注意: 遅延スロットに分岐やジャンプ(または制御フロー命令)を入れてはいけないことに注意してください。 また、li, などの疑似演算を遅延スロットに入れることは、 複数の命令に拡張される可能性があるため、お勧めできません。 MIPS I アーキテクチャーにもロード遅延スロットがあり、 新しい MIPS では依然として mfhi, および mflo, の使用に制限があります。 これらの制限を満たすように注意してください。 アセンブラーが自動的に制限を満たしてくれるわけではありません。

いくつかの命令の例は以下のとおりです:

$ra  12 $sp  sw,         \ sw    ra,12(sp)
$4    8 $s0  lw,         \ lw    a0,8(s0)
$v0  $0  lui,            \ lui   v0,0x0
$s0  $s4  $12  addiu,    \ addiu s0,s4,0x12
$s0  $s4  $4  addu,      \ addu  s0,s4,$a0
$ra  $t9  jalr,          \ jalr  t9

if, などの条件を指定するには、 条件分岐を実行し、 先頭の b と末尾の , を省略します。 例えば以下のようにします:

4 5 eq if,
  ... \ do something if $4 equals $5
then,

32 ビット MIPS マシンの呼び出し規則では、 最初の 4 つの引数をレジスター $4$7 に渡し、 戻り値に $v0$v1 を使用します。 これらのレジスターに加えて、 レジスター $t0$t8 は保存・復元せずに上書きしても問題ありません。

jalr, を使用してダイナミック・ライブラリー・ルーチンを呼び出す場合は、 最初に呼び出される関数のアドレスを $t9 にロードする必要があります。 これは、 相対メモリー・アクセスを行うために位置間接コード(position-indirect code)によって使用されます。

MIPS32 abi-code ワードの例を以下に示します:

abi-code my+  ( n1 n2 -- n3 )
  \ SP passed in $4, returned in $v0
  $t0  4 $4  lw,         \ load n1, n2 from stack
  $t1  0 $4  lw,    
  $t0  $t0  $t1  addu,   \ add n1+n2, result in $t0
  $t0  4 $4  sw,         \ store result (overwriting n1)
  $ra  jr,               \ return to caller
  $v0  $4  4  addiu,     \ (delay slot) return uptated SP in $v0
end-code

6.29.8 PowerPC assembler

PowerPC アセンブラーと逆アセンブラーは、 Michal Revucky の貢献に依ります。

このアセンブラーは、 ニーモニック名を「,」で終了する規則に従っていないため、 一部のニーモニック名は通常の Forth ワード (特に: and or xor fabs) を隠し(shadow)ます。 そのため、 Forth ワードを使用したい場合は、 最初に、 たとえば also forward を使用して、 Forth ワードを表示できるようにする必要があります。

レジスターは番号によって参照されます。 たとえば、9 は、 (命令に応じて、)整数レジスター 9 または FP レジスター 9 を意味します。

レジスターと即値を区別する方法がないため、 add, だけでなく add, などの即値用の命令を明示的に使用する必要があります。

アセンブラーと逆アセンブラーは通常、 最も一般的な形式の命令をサポートしますが、 通常は短い形式(特に分岐)はサポートしません。


6.29.9 ARM Assembler

ARM アセンブラーには、 ARM アーキテクチャー・バージョン 4 のすべての命令と、 アーキテクチャー・バージョン 5 の BLX 命令が含まれています。 Thumb 命令は (まだ) サポートされていません。 また、 コ・プロセッサーもサポートされていません。

アセンブラーは、 「ARM Architecture Reference Manual」で使用されているのと同じオペランド順序を持つ後置構文(postfix syntax)を使用します。 ニーモニックにはカンマが付けられます。

レジスターは r0r15 までの名前で指定され、別名は pc, lr, sp, ip, fp で、これらは利便性のために提供されています。 ip は「プロシージャ内呼び出しスクラッチ・レジスター」(intra procedure call scratch register)(注意: r12) を指し、 命令ポインターを指すわけではないことに注意してください。 sp は、 Forth のスタック・ポインターではなく、 ARM ABI スタック・ポインター (r13) を指します。

条件コードは命令内のどこにでも指定できますが、 ニーモニックの直前に指定すると最も読みやすくなります。 「S」フラグは別個のワードではなく、 命令ニーモニックにエンコードされます。 ステータス・レジスターを更新したい場合は、 add, の代わりに adds, を使用してください。

以下の表に、 一般的な命令のオペランドの構文を示します:

Gforth          normal assembler      description
123 #           #123                  immediate
r12             r12                   register
r12 4 #LSL      r12, LSL #4           shift left by immediate
r12 r1 LSL      r12, LSL r1           shift left by register
r12 4 #LSR      r12, LSR #4           shift right by immediate
r12 r1 LSR      r12, LSR r1           shift right by register
r12 4 #ASR      r12, ASR #4           arithmetic shift right
r12 r1 ASR      r12, ASR r1           ... by register
r12 4 #ROR      r12, ROR #4           rotate right by immediate
r12 r1 ROR      r12, ROR r1           ... by register
r12 RRX         r12, RRX              rotate right with extend by 1

メモリー・オペランドの構文を以下の表に示します:

Gforth            normal assembler      description
r4 ]              [r4]                  register
r4 4 #]           [r4, #+4]             register with immediate offset
r4 -4 #]          [r4, #-4]             with negative offset
r4 r1 +]          [r4, +r1]             register with register offset
r4 r1 -]          [r4, -r1]             with negated register offset
r4 r1 2 #LSL -]   [r4, -r1, LSL #2]     with negated and shifted offset
r4 4 #]!          [r4, #+4]!            immediate preincrement
r4 r1 +]!         [r4, +r1]!            register preincrement
r4 r1 -]!         [r4, +r1]!            register predecrement
r4 r1 2 #LSL +]!  [r4, +r1, LSL #2]!    shifted preincrement
r4 -4 ]#          [r4], #-4             immediate postdecrement
r4 r1 ]+          [r4], r1              register postincrement
r4 r1 ]-          [r4], -r1             register postdecrement
r4 r1 2 #LSL ]-   [r4], -r1, LSL #2     shifted postdecrement
' xyz >body [#]   xyz                   PC-relative addressing

複数のロード/ストア命令のレジスター・リストは、 それぞれ { および } というワードを使用して開始および終了します。 {} の間に、 レジスター名を 1 つずつリストすることも、 後置演算子 r-r を使用してレジスター範囲を形成することもできます。 ^ フラグはレジスター・リスト・オペランドではエンコードされませんが、 命令ニーモニックに直接エンコードされます。 つまり、 ^ldm,^stm, を使用します。

複数のロード/ストアのアドレッシング・モードは命令接尾辞としてエンコードされず、 代わりにアドレッシング・モードのように指定されます。 DA, IA, DB, IB, DA!, IA!, DB!, IB! のいずれかを使います。

以下の表にいくつかの例を示します:

Gforth                           normal assembler
r4 ia  { r0 r7 r8 }  stm,        stmia    r4, {r0,r7,r8}
r4 db!  { r0 r7 r8 }  ldm,       ldmdb    r4!, {r0,r7,r8}
sp ia!  { r0 r15 r-r }  ^ldm,    ldmfd    sp!, {r0-r15}^

Forth アセンブラーに典型的な制御構造ワードが利用可能です: if,, ahead,, then,, else,, begin,, until,, again,, while,, repeat,, repeat-until, 。 条件は以下のワードの前に指定します:

r1 r2 cmp,    \ compare r1 and r2
eq if,        \ equal?
   ...          \ code executed if r1 == r2
then,

ARM アセンブラーを使用した定義の例:

abi-code my+ ( n1 n2 --  n3 )
   \ arm abi: r0=SP, r1=&FP, r2,r3,r12 saved by caller
   r0 IA!  { r2 r3 }  ldm,     \ pop r2 = n2, r3 = n1
   r3  r2  r3         add,     \ r3 = n1+n1
   r3  r0 -4 #]!      str,     \ push r3
   pc  lr             mov,     \ return to caller, new SP in r0
end-code

6.29.10 Other assemblers

別のアセンブラー/逆アセンブラーを提供したい場合は、 そのようなアセンブラーがすでに存在するかどうかを確認するために著者達()までご連絡ください。 これらを最初から作成する場合は、 著者たちが使用しているものと同様の構文スタイルを使用してください(つまり、 後置や命令名の末尾のカンマ see Common Assembler)。 逆アセンブラーの出力をアセンブラーの有効な入力にし、使用したスタイルと同様のスタイルを維持します。

実装に関するヒント: 最も重要なのは、 すべての手順を含む優れたテスト・スイートを用意することです。 それができたら、 あとは簡単です。 実際のコーディングについては、 arch/mips/disasm.fs を参照して、 アセンブラーと逆アセンブラーの両方でデータを使用し、 冗長性や潜在的なバグを回避する方法についてアイデアを得ることができます。 また、そのファイル (および see Advanced does> usage example) を見て、 逆アセンブラーをファクタリングする方法のアイデアを得ることができます。

逆アセンブラーから始めます。 逆アセンブラーからのデータをアセンブラーで再利用する方が、 その逆よりも簡単だからです。

アセンブラーについては、 arch/alpha/asm.fs を見てください。 これがいかに簡単であるかを示しています。


6.30 Carnal words

これらのワードは Gforth の仕組み(Forth サークルでは Forth システムの「肉体的知識」(carnal knowledge)と呼ばれる)を扱っていますが、 著者等は文書化するに当たって、 これらが十分安定(stable)していると考えています。


6.30.1 Header fields

Gforth 1.0 では、 新しいワード・ヘッダー・レイアウトに切り替えられました。 詳細な説明については、 Bernd Paysan and M. Anton Ertl. The new Gforth header を参照してください。 この論文の公開以後に、xt と nt は本体(body)のようにパラメーター・フィールドを指すように変更されましたが、 それ以外は依然としてこの文献が最新です。

このセクションでは、 データ構造とそれにアクセスするために使用されるワードについてのみ説明します。 ヘッダーには以下のフィールドがあります:

name
>f+c
>link
>cfa
>namehm
>body

現在、 Gforth には xt/nt/body から各フィールドに到達するために上に示した名前がありますが、 標準の >body を除けば、 これらは定着した Gforth ワードではありません。 これらの代わりアクセス用ワードを提供しています。 注意:文書化されたアクセス・ワードはヘッダー・レイアウトの再編成後も生き残ることに注意してください。

ワードの中には nt を期待するものもあれば、 xt を期待するものもあります。 nt と xt が両方ともワードの本体(body)を指しているとすると、 その違いは何でしょうか? ほとんどのワードでは、xt と nt は同一ヘッダーを使用するので、 nt=xt となり、 同じ場所を指します。 ただし、 同義語(synonym)(see Aliases)では違いがあります。 以下の例で考えてみましょう

create x
synonym y x
synonym z y

この場合、z の nt は z の本体(body)を指し、 z の xt は x の本体(body)を指します。 alias または forward (see Forward) で定義されたワードも、 nt と異なる xt を持ちます。

名前フィールドは可変長で、 name>string (see Name token) でアクセスします。

>f+c フィールドには、フラグと名前の長さ(カウント)が含まれます。 name>string でカウントを読み取り、 以下でフラグを読み取ります。

compile-only? ( nt – flag  ) gforth-1.0 “compile-only?”

nt がコンパイル専用(compile-only)としてマークされている場合は true 。

>link フィールドには、 同じワードリスト内の前のワードへのリンクが含まれます。 name>link (see Name token) で読み取ることができます。

name と >f+c>link フィールドは noname ワードには存在しませんが、 それでも name>stringname>link は機能し、 name>string は 0 0 を返し、 name>link は 0 を返します。

>cfa フィールド(別名 コード・フィールド) には、 ワードを execute するために使用されるコード・アドレスが含まれます。 >code-address で読み取り、 code-address! (see Threading Words) で書き込むことができます。

>namehm フィールドには、 後述するするヘッダー・メソッド・テーブルのアドレスが含まれます。 あなたがこれにアクセスするには、 ヘッダー・メソッド (see Header methods) の実行によるか、 または、 ヘッダー・メソッドへのアクセスによります。

>body (別名 パラメーター・フィールド)には、 ワードの種類に固有のデータまたはスレッド化コードが含まれます。 その長さはワードの種類によって異なります。 たとえば、 constant の場合、 定数の値を含むセルが含まれます。 >body (see The gory details of CREATE..DOES>) を通じてアクセスできますが、 これは標準の create で定義したワードのみです。


6.30.2 Header methods

新しい Gforth ワード・ヘッダーはオブジェクト指向であり、 以下のメソッド(メソッド・セレクター)をサポートします:

.hm label method          overrider        field
          execute         set-execute      >cfa
opt:      opt-compile,    set-optimizer    >hmcompile,
to:       (to)            set-to           >hmto
extra:                                     >hmextra
>int:     name>interpret  set->int         >hm>int
>comp:    name>compile    set->comp        >hm>comp
>string:  name>string     set-name>string  >hm>string
>link:    name>link       set-name>link    >hm>link

これらのワードの多くは定着(stable)した Gforth ワードではありませんが、 Gforth には後述する定着した高レベルのワードがあります。

以下を使用すると、 ワードのヘッダー・メソッドを確認できます

.hm ( nt –  ) gforth-1.0 “dot-h-m”

nt のヘッダー・メソッドを出力します

オーバーライダー(overrider)(セッター)ワードは、 最新の定義のメソッド実装を変更します。 クォーテーションまたはクロージャーは、 完了時に以前の最新の定義を復元するため、 最新のものとはみなされず、 以下のようなことができます:

: my2dup over over ;
[: drop ]] over over [[ ;] set-optimizer

execute メソッドは、 パフォーマンス上の理由から、 実際にはヘッダー・メソッド・テーブルではなくヘッダーの >cfa フィールドに格納されます。 また、 他のメソッドは xt を呼び出すことによって実装されますが、 ネイティブ・コード・アドレスを通じて実装されます。 このメソッドを設定する大まかな方法​​は以下のとおりです

set-execute ( ca –  ) gforth-1.0 “set-execute”

ca のネイティブ・コードにジャンプするように現在のワードを変更します。 また、 compile, 実装を最も一般的な(そして最も遅い)実装に変更します。 より効率的な compile, 実装が必要な場合は、 後で set-optimizer を呼び出します。

set-execute で使用するコード・アドレスを取得するには、 docol: または >code-address などのワードを使用できます。 See Threading Words

set-execute の代わりに、 xt を受け取る set-does> (see User-defined Defining Words)もあります。

さらに、 低レベルの code-address!definer! があります。 (see Threading Words)

opt-compile, メソッドは、 ほとんどの Gforth エンジンで動く compile, です(gforth-itc は代わりに , を使用します)。 set-optimizer を使用して、 現在のワードに対する compile, のより効率的な実装を定義できます((see User-defined compile,))。 注意: 最終結果は postpone literal postpone execute と同等でなければならないことに注意してください。

set-optimizer の使用例として、 以下の constant の定義を考えてみましょう:

: constant ( n "name" -- ; name: -- n )
  create ,
  ['] @ set-does>
;

5 constant five
: foo five ; see foo

Forth システムは、 定数の値を変更してはならないことを認識せず、 単に create されたワード (>body で変更可能)として見て、 foo は最初に five のボティのアドレスをスタックにプッシュし、 その次にそこから値を取得します。 set-optimizer を使用すると、 constant の定義を以下のように最適化できます:

: constant ( n "name" -- ; name: -- n )
  create ,
  ['] @ set-does>
  [: >body @ postpone literal ;] set-optimizer
;

いまや、 foo には、 five の呼び出しではなく、 即値(literal)の 5 が含まれるようになりました。

注意: set-executeset-does> は、executecompile, が一致していることを確認するために、 set-optimizer 自体を実行することに注意してください。 あなた独自のオプティマイザーを追加するには、 後で追加する必要があります。

defer! (別名 (to) メソッド(see User-defined to and defer@)は、 defer で定義されたワードおよび類似のワードに対して defer! を実装します。 ですが、 これは to の核心(core)でもあります。 defer!/(to) メソッドの一般的なスタック効果は ( val xt -- ) です。 ここで xt は格納されているワードを示し、 val はそこに格納されている(適切な型の)値です。

たとえば、 以下のように fvalue を実装できます:

: fvalue-to ( r xt -- ) >body f! ;

: fvalue ( r -- )
  create f,
  ['] f@ set-does>
  ['] fvalue-to set-to ;

5e fvalue foo
: bar foo 1e f+ to foo ;
see bar

set-optimizer を使用して、 生成されたコードを改善できます:

: compile-fvalue-to ( xt-value-to -- )
  drop ]] >body f! [[ ;
  
: fvalue-to ( r xt -- ) >body f! ;
' compile-fvalue-to set-optimizer

: fvalue ( r -- )
  create f,
  ['] f@ set-does>
  [: >body ]] literal f@ [[ ;] set-optimizer
  ['] fvalue-to set-to ;

5e fvalue foo
: bar foo 1e f+ to foo ;
see bar

実際には、 Gforth には、 +TO など、 実装するための追加の工夫がいくつかあります。

Set-defer@ (see User-defined to and defer@) を使用すると、 defer のようなワードに対して defer@ (see Deferred Words) メソッドのバリエーションを実装できます。

>hmextra フィールドは、 追加のデータをヘッダー・メソッド・テーブルに保存する必要がある場合に使用されます。 特に、 それは set-does> に渡す xt を保存し(そして does>set-does> を呼び出し)、 そして、 ;abi-code の後のコードのアドレスを保存します。

これらのメソッドはすべて、 nt ではなく xt を使用しますが、 オーバーライド・ワード(override words)は最新の定義で機能します。 これは、 たとえば、 同義語(synonym)に対して set-optimizer を使用した場合、 その効果はおそらくあなたが意図したものとは異なることを意味します。 ワードの xt を compile, する場合、 新たに設定された同義語(synonym)ではなく、 元のワードの opt-compile, 実装が使用されます。

以下のメソッド達は nt を消費します。

name>interpret メソッドは、 同義語(synonym)や類似のワードを除くほとんどのワードに対して noop として実装されます。

set->int ( xt –  ) gforth-1.0 “set-to-int”

現在のワードの name>interpret (nt -- xt2 ) メソッドの実装を xt に設定します。

name>compile メソッドは、 nt のコンパイル機能(compilation semantics)を生成します。 set->comp で変更することでコンパイル機能を変更できますが、 name>compile のスタック効果のため、 目的のコンパイル機能の xt をプッシュするだけというほど単純ではありません。 一般に、 コンパイル機能の変更は避ける必要があり、 変更する場合は、 immediate または interpret/compile: などの高レベル・ワードを使用してください(See Combined Words)。

set->comp ( xt –  ) gforth-1.0 “set-to-comp”

現在のワードの name>compile ( nt -- w xt2 ) メソッドの実装を xt に設定します。

immediate? ( nt – flag  ) gforth-1.0 “immediate?”

ワード nt がデフォルト以外のコンパイル機能(compilation semantics)を持っている場合は true (これは即時性(immediacy)の定義と完全には一致しませんが、 多くの人はワード語を「即時」(immediate)と呼ぶときはこれを意味しています)。

Name>string および Name>link は、 noname ヘッダーから name と >f+clink フィールドを削除できるようにするためのメソッドです。 これらのワードを使用すると意味のある結果が得られます。 通常、 noname を使用する場合を除き、 これらのメソッドの実装を変更することはありませんが、 あなたがそれでも必要とするなら以下をご覧ください

set-name>string ( xt –  ) gforth-1.0 “set-name-to-string”

現在のワードの name>string ( nt -- addr u ) メソッドの実装を xt に設定します。

set-name>link ( xt –  ) gforth-1.0 “set-name-to-link”

現在のワードの name>link (nt1 -- nt2|0 ) メソッドの実装を xt に設定します。


6.30.3 Threading Words

ここで使用される用語は、 間接スレッド Forth システム(indirect threaded Forth systems)に由来しています。 間接スレッド Forth システムでは、 ワードの XT はワードの CFA (コード・フィールド・アドレス) によって表されます。 CFA は、 コード・アドレスを含むセルを指します。 コード・アドレスとは、 ワードを呼び出す実行時のアクションを実行するマシン・コードのアドレスです(たとえば、 dovar: ルーチンは、 ワード(変数)の本体(body)のアドレスをスタックにプッシュします)。

以下のワード群は、 Gforth のコード・フィールドやコード・アドレスやその他のスレッド機能へのアクセスを提供します。 これにより、直接スレッドと間接スレッドの違いは、 多かれ少なかれ抽象化されます。

Gforth 0.7 までは、 ワードの種類を知るのに、 コード・アドレス(さらに、 does> で定義されたワードの場合は、 >does-code によって返されるアドレス)で十分でした。 ただし、 Gforth-1.0 以降、 少なくとも、 compile,name>compile のようなワードの振る舞いや実装は、 Header methods で説明されているように独立して決定できます。

以下のワード達は、 コード・フィールドを作成し、 そして、 同時にヘッダー・メソッドを初期化します:

hmcopy, ( xt –  ) gforth-experimental “hmcopy-comma”

ヘッダーの構築中に、 コード・フィールドを割り当て、 コード・フィールドとヘッダー・メソッドを設定するためのプロトタイプとして xt を使用します。

docol, ( ) gforth-1.0 “docol,”

コロン定義のコード・アドレスを書き込む。

docon, ( ) gforth-1.0 “docon,”

CONSTANT のコード・アドレスを書き込む。

dovar, ( ) gforth-1.0 “dovar,”

CREATE されたワードのコード・アドレスを書き込む。

douser, ( ) gforth-1.0 “douser,”

USER 変数のコード・アドレスを書き込む。

dodefer, ( ) gforth-1.0 “dodefer,”

defer されたワードのコード・アドレスを書き込む。

dofield, ( ) gforth-1.0 “dofield,”

field のコード・アドレスを書き込む。

dovalue, ( ) gforth-1.0 “dovalue,”

CONSTANT のコード・アドレスを書き込む。

doabicode, ( ) gforth-1.0 “doabicode,”

ABI-CODE 定義のコード・アドレスを書き込む。

does> で定義されたワードの場合、 hmcopy, を使用してください。

または、 create-from のような高レベルのワードを使用します(see Creating from a prototype)。

以下のワード群はヘッダー・メソッドが導入される前に設計されたものであるため、 Gforth でさまざまなワード・タイプを処理する最良の(推奨される)方法ではありません。

間接スレッド Forth では、 ' name @ を使用して name のコード・アドレスを取得できます。 Gforth では、 スレッド化方式とは関係なく、 ' name >code-address で取得できます。

threading-method ( – n ) gforth-0.2 “threading-method”

エンジンが直接スレッドの場合は 0。 注意: これはイメージの存続期間中(lifetime)に変更される可能性があることに注意してください。

>code-address ( xt – c_addr  ) gforth-0.2 “>code-address”

c-addr は、 ワード xt のコード・アドレスです。

code-address! ( c_addr xt –  ) gforth-obsolete “code-address!”

コード・アドレス c-addr のコード・フィールドを xt に変更します。

さまざまな定義ワードによって生成されるコード・アドレスは、 以下のワード群によって生成されます:

docol: ( – addr  ) gforth-0.2 “docol:”

コロン定義のコード・アドレスを書き込む。

docon: ( – addr  ) gforth-0.2 “docon:”

CONSTANT のコード・アドレスを書き込む。

dovar: ( – addr  ) gforth-0.2 “dovar:”

CREATE されたワードのコード・アドレスを書き込む。

douser: ( – addr  ) gforth-0.2 “douser:”

USER 変数のコード・アドレスを書き込む。

dodefer: ( – addr  ) gforth-0.2 “dodefer:”

defer されたワードのコード・アドレスを書き込む。

dofield: ( – addr  ) gforth-0.2 “dofield:”

field のコード・アドレスを書き込む。

dovalue: ( – addr  ) gforth-0.7 “dovalue:”

CONSTANT のコード・アドレスを書き込む。

dodoes: ( – addr  ) gforth-0.6 “dodoes:”

DOES> で定義されたワードのコード・アドレス。

doabicode: ( – addr  ) gforth-1.0 “doabicode:”

ABI-CODE 定義のコード・アドレスを書き込む。

set-does> で定義されたワード X の場合、 コード・アドレスは dodoes: を指し、 そして、 ヘッダー・メソッドの >hmextra フィールドには、 X の本体(body)アドレスをプッシュした後に呼び出されるワードの xt が含まれます。

ワードが DOES> で定義されたワードであるかどうか、 そしてそのワードが execute する Forth コードを知りたい場合は、>does-code で以下のことがわかります:

>does-code ( xt1 – xt2  ) gforth-0.2 “>does-code”

xt1set-does> で定義されたワードの子の実行トークンである場合、 xt2set-does> に渡される xt 、 つまり xt1 実行時に実行されるワードの xt です(ただし、 最初に xt1 の本体(body)アドレスがプッシュされます)。 xt1set-does> で定義されたワードに属していない場合、 xt2 は 0 です。

結果の xt2set-does> (推奨) とともに使用して、 もっとも最新のワードを変更するか以下を使用して変更できます

does-code! ( xt2 xt1 –  ) gforth-0.2 “does-code!”

xt1xt2 set-does> で定義されたワードに変更します。

任意のワードを変更するためには…

以下の 2 つのワードは、 >code-address>does-codecode-address!does-code! を一般化します:

>definer ( xt – definer  ) gforth-0.2 “>definer”

definer (definer;定義者)は、 xt の定義方法を示す一意の識別子です。 異なる does> コードで定義されたワードには、 異なる definer が存在します。 definer は比較や definer! で使用できます。

definer! ( definer xt –  ) gforth-obsolete “definer!”

xt で表されるワードは、 その振る舞いを definer に関連付けられた振る舞いに変更します。

Code-address!does-code!definer! は、 opt-compile, メソッドをそのワード型用のやや汎用的なコンパイラーに更新します(特に、 プリミティブに対しては、 プリミティブ固有の peephole-compile, (peephole-compile;のぞき穴最適化コンパイル)ではなく、 遅い general-compile, (一般コンパイル)メソッドが使用されます)。


6.31 Passing Commands to the Operating System

Gforth を使用すると、 (そのようなものが存在する場合、)ホスト・オペレーティング・システムのシェルで実行するための任意の文字列をシェルに渡すことができます。

sh ( "..." –  ) gforth-0.2 “sh”

行(コマンド・ライン)の残りの部分をシェルコマンドとして実行します。 その後、 ワード $? によってコマンドの終了ステータスを取得します。

system ( c-addr u –  ) gforth-0.2 “system”

c-addr u で指定された文字列をホスト・オペレーティング・システムに渡し、 サブ・シェルで実行します。 その後、 ワード $? によってコマンドの終了ステータスが生成されます。 環境変数 GFORTHSYSTEMPREFIX の値 (またはそのデフォルト値) が文字列の先頭に付加されます(主に、 Cygwin がデフォルトで使用するシェルではなく、 Windows のシェルとして command.com の使用をサポートするためです。)。 see Environment variables

sh-get ( c-addr u – c-addr2 u2  ) gforth-1.0 “sh-get”

シェル・コマンド addr u を実行します。 c-addr2 u2 はコマンドの出力です。 終了コードは $? にあります。 コマンドの出力は sh$ 2@ にもあります。

$? ( – n  ) gforth-0.2 “dollar-question”

Value – 最後に実行された system コマンドによって返された終了ステータス。

getenv ( c-addr1 u1 – c-addr2 u2 ) gforth-0.2 “getenv”

文字列 c-addr1 u1 は環境変数名を指定します。 文字列 c-addr2 u2 は、 ホスト・オペレーティング・システムによる、 その環境変数の展開結果です。 環境変数が存在しない場合、 c-addr2 u2 は長さ 0 文字の文字列を返します(訳注: 存在しない:c-addr2,u2 = 0, 0 存在するけど中身が空: 【有効なアドレス】, 0)


6.32 Keeping track of Time

ms ( n –  ) facility-ext “ms”
\ 訳注: 指定のミリ秒ウエイトします(エポックでカウントします)。
ns ( d –  ) gforth-1.0 “ns”
\ 訳注: 指定のナノ秒ウエイトします(エポックでカウントします)
time&date ( – nsec nmin nhour nday nmonth nyear  ) facility-ext “time-and-date”

現在の時刻を報告します。 nsec 、 nmin、 nhour は 0 から数えます。 nmonth は 1 から数えます。

>time&date&tz ( udtime – nsec nmin nhour nday nmonth nyear fdst ndstoff c-addrtz utz ) gforth-1.0 “to-time-and-date”

1970 年 1 月 1 日 0:00Z からの時間を秒単位で現在の時刻に変換します。 nsec、 nmin、 nhourは 0 から数えます。 nmonth は 1 から数えます(訳注: 使い方: 秒単位の値を与える必要があるので、 例えば utime #1000000 ud/mod rot drop >time&date&tz とする。 fdst: 夏時間を 採用しているときに負数 、採用していないときに 0、この情報が得られないときに正数。 ndstoff : GMTからのオフセット(秒単位)、 c-addrtz utz : タイムゾーン文字列(例: JST))

utime ( – dtime ) gforth-0.5 “utime”

エポック(some epoch)以降の現在時刻をマイクロ秒単位で報告します。 #1000000 um/mod nip を使用して秒に変換します

ntime ( – dtime ) gforth-1.0 “ntime”

エポック(some epoch)以降の現在時刻をナノ秒単位で報告します。

cputime ( – duser dsystem ) gforth-0.5 “cputime”

duser と dsystem は、 Forth システムの開始以降に使用されたユーザー・レベルとシステム・レベルの CPU 時間(子プロセスを除く)をマイクロ秒単位で表したものです(ただし、 粒度はさらに粗くなる可能性があります)。 getrusage コールのないプラットフォームでは、 duser については経過時間(エポック)が報告され、 dsystem については 0 が報告されます。


6.33 Miscellaneous Words

このセクションでは、 このマニュアルの他の場所で説明されていない標準 Forth のワードをリストします。 いつかは、 これらのワードはそれぞれ適切なセクションに配置されることになるでしょう。

quit ( ?? – ??  ) core “quit”

リターン・スタックを空にし、 ユーザー入力デバイスを入力ソースにして、 インタプリタ状態にして、 テキスト・ インタープリターを開始します。

以下の標準 Forth のワードは現在、 Gforth ではサポートされていません(see Standard conformance):

EDITOR EMIT? FORGET (訳注: forth で書かれたラインエディタである EDITOR ボキャブラリーはありません。 FORGET はありません。任意の箇所で「忘れる」ことはできません。代わりに marker name で忘れるポイントを「マーク」しておきます。 name を実行するとそのポイントまで「忘れ」ます。)


7 Error messages

Gforth の典型的なエラー・メッセージは以下のようになります:

in file included from \evaluated string/:-1
in file included from ./yyy.fs:1
./xxx.fs:4: Invalid memory address
>>>bar<<<
Backtrace:
$400E664C @
$400E6664 foo

エラーを特定するメッセージは Invalid memory address です。 そのエラーはファイル ./xxx.fs の 4 行目をテキスト解釈(text-interpreting)しているときに発生しました。 その行のエラーが発生したワードを(>>><<< で囲んで)指摘します。

エラーを含むファイルは ./yyy.fs の 1 行目でインクルード(included)されており、 yyy.fs はファイル以外からインクルードされています(この場合、 yyy.fs を Gforth へのコマンドライン パラメーターとして指定することにより)。

エラー・メッセージの最後には、 バックトレースとして解釈できるリターン・スタック・ダンプが表示されます(空の可能性があります)。 その一番上の行には throw が発生したときのリターン・スタックのTOSが表示され、 その一番下の行には最上位のテキスト・インタープリターのリターン・スタックのすぐ上にあるリターン・スタック・エントリが表示されます。

ほとんどのリターン・スタック・エントリの右側には、 そのリターン・スタック・エントリをリターン・アドレスとしてプッシュしたワードを推測して表示します。 これによりバックトレースが得られます。 この例では、 barfoo を呼び出し、 foo@ を呼び出していることがわかります(そして、 この @ には 「Invalid Memory address」例外がありました)。

注意: バックトレースは完璧では無いことに注意してください。 どのリターン・スタック・エントリがリターン・アドレスであるかは知りません(そのため、 誤検知が発生する可能性があります)。 また、 (abort" など、)場合によっては、 リターン・アドレスからはリターン・ アドレスをプッシュしたワードを特定できないため、 リターン・アドレスによってはリターン・スタック・ダンプに名前が表示されません。

リターン・スタック・ダンプは、 特定の throw が execute されたときのリターン・スタックを表します。 catch を使用するプログラムでは、 リターン・スタック・ダンプにどの throw を使用する必要があるかが必ずしも明確ではありません(たとえば、 エラーを示すある throw がキャッチされ、 そのリカバリ中に別のエラーが発生したとすると、 スタック・ダンプにはどの throw のを使用するべきでしょうか?)。 Gforth は、 最後に execute された(そして、 その execute からまだ返ってきていない状態で、 ) catch または nothrow の後の最初の throw のリターン・スタック・ダンプを表示します: 通常、 これはうまく機能します。 正しいバック・トレースを取得するためには、 通常、 エラーが再 throw されない場合は catch の後に nothrow または ['] false catch 2drop を挿入します。

gforth エンジン(訳注: OSコマンドラインから gforth で起動)は、 プリミティブが生成した throw のリターン・スタック・ダンプを行えます(例: invalid memory address(不正なメモリーアクセス), stack empty(スタックが空) 等)。 gforth-fast エンジン(訳注: OSコマンドラインから gforth-fast で起動)は、 直接呼び出された throw (abort などを含む) からのリターン・スタック・ダンプのみを行うことができます。 gforth-fast エンジンのプリミティブによって例外が発生した場合、 通常はリターン・スタック・ダンプは全く見れません。 しかしながら、 catch によって例外がキャッチされ(たとえば、 何らかの状態を復元するため)、 その後再び throw が返される場合、 最初の throw に対するリターン・スタック・ダンプは見れます。

また、 gforth-fast は、 ゼロ除算と除算オーバーフローを区別しようとしません。 これは、 どの除算も時間が掛かる処理だからです。


8 Tools

こちらもご覧ください Emacs and Gforth


8.1 ans-report.fs: Report the words used, sorted by wordset

Forth プログラムを標準プログラムとしてラベル付けしたい場合は、 プログラムがどのワードセットを使用するかを文書化する必要があります。

ans-report.fs ツールを使用すると、アプリケーションでどのワードセットのどのワードが使用されているか、 どの非標準ワードが使用されているかを簡単に判断できます。 チェックしたいプログラムをロードする前に ans-report.fs をインクルードするだけです。 プログラムをロードした後、 print-ans-report を使用してレポートを取得できます。 一般的な使用法は、 以下のようにこれをバッチ・ジョブとして実行することです:

gforth ans-report.fs myprog.fs -e "print-ans-report bye"

出力は以下のようになります (compat/control.fs の場合):

The program uses the following words
from CORE :
: POSTPONE THEN ; immediate ?dup IF 0= 
from BLOCK-EXT :
\ 
from FILE :
( 

ans-report.fs は、 Forth-94 と Forth-2012 の両方のワードセットをレポートします。 両方の標準に含まれるワードについては、 接尾辞なしでワードセットが報告されます(例: CORE-EXT)。 Forth-2012 専用のワードの場合、-2012 接尾辞が付いたワードセットが報告されます(例: CORE-EXT-2012)。 Forth-94 のみのワード(つまり、Forth-2012 で削除されたワード)についても同様です。

8.1.1 Caveats

注意: ans-report.fs は、 どのワードが使用されているかをチェックするだけで、 標準に準拠した方法で使用されているかどうかをチェックするわけではないことに注意してください。

一部のワードは、 標準で複数のワードセットで定義されています。 ans-report.fs は、 それらのワードセットのうちの 1 つについてのみレポートし、 それは必ずしもあなたが期待したワードセットとは限りません。 どのワードセットを指定するのが適切かは、 用途によって異なります。 たとえば、S" のコンパイル機能(compilation semantics)のみを使用する場合、それは CORE ワードです。 インタプリタ機能(interpretation semantics)も使用する場合、 それは FILE ワードです。


8.2 Stack depth changes during interpretation

ファイルをロードした後、 スタックに項目が残っていることに気づくことがあります。 Depth-changes.fs ツールを使用すると、 これらのスタック項目がファイル内のどこから来たのかをすばやく見つけることができます。

Depth-changes.fs を使用する最も簡単な方法は、 チェックするファイルの前にこれをインクルードすることです。 例:

gforth depth-changes.fs my-file.fs

これにより、 すべての空行(インタープリター状態)でのデータ・スタックの深さと FP スタックの深さが、 最後の空行(インタープリター状態)での深さと比較されます。 深さが等しくない場合は、 ファイル内の位置とスタックの内容が ~~ (see Debugging) で出力されます。 これは、 指摘行より前の空ではない行内の段落(paragraph)でスタックの深さの変更が発生したことを示します。 ファイルの最後に空行を残しておいて、 最後の段落もチェックされるようにすることをお勧めします。

通常、 空行のみをチェックするのはうまく機能しますが、 空行ではない大きなブロックが存在する場合(大きなテーブルを構築する場合など)、 このブロックのどこでスタックの深さが変更されたのかを知りたい場合があります。 以下を使用すると、 解釈(interpret)されたすべての行をチェックできます

gforth depth-changes.fs -e "' all-lines is depth-changes-filter" my-file.fs

これにより、 各行末ごとにスタックの深さがチェックされます。 したがって、 深さの変更は ~~ によって報告された行で発生しています(それより前の行ではありません)。

注意: これにより、 スタックの深さが変更される場所を示す精度が向上しますが、 多くの、 意図した、 スタックの深さの変更が報告されることが多いことに注意してください(たとえば、 解釈(interpret)された計算が複数の行にまたがる場合)。 一部の行のチェックを抑制するには、 これらの行の末尾にバックスラッシュを置き(その後に空白は続きません)、 以下を使用します

gforth depth-changes.fs -e "' most-lines is depth-changes-filter" my-file.fs

9 Standard conformance

(標準適合度)私達の知る限り、 Gforth は…

ANS Forth システムと Forth-2012 システムに関しては

Gforth には以下の環境的制限(environmental restrictions)があります:

さらに、 標準 Forth システムでは、 特定の実装での選択を文書化する必要があります。 この章では、 Forth-94 標準のこれらの要件を満たすことを試みます。 Forth-2012 標準については、 需要がある場合にのみ追加のドキュメントを作成することにしました。 したがって、 このドキュメントで本当に足りないという場合は、 著者達までご連絡ください。

多くの場合、 以下のドキュメント群においては、 特に、 情報がプロセッサに依存する場合、 オペレーティング・システムまたは選択したインストール・オプション、 または、 Gforth のメンテナンス中に変更される可能性があるかどうについては、 情報を直接提供する代わりに、 システムに情報を要求する方法が説明されています。


9.1 The Core Words


9.1.1 実装毎オプション(Implementation Defined Options;idef)

セル単位に整列されたアドレス:

プロセッサ依存です。 Gforth の整列(alignment)ワードは自然な整列(natural alignment)を実行します(たとえば、 サイズ 8 のデータに対して整列されたアドレスは 8 で割り切れます)。 通常、 非整列アクセス(unaligned accesses)では -23 THROW が発生します。

EMIT と非表示文字:

文字(character)は C 言語のライブラリー関数(実際にはマクロ) putc を使用して出力されます。

ACCEPTEXPECT の文字編集:

これは、 Emacs のようなキー・バインディングを備えた GNU readline ライブラリー(see Command Line Editing in The GNU Readline Library)をモデルにしています。 Tab を入力するたびに (すべての補完に共通のプレフィックスを生成するのではなく)完全なワード語補完を生成するという点で少し異なります。 See Command-line editing

文字セット:

あなたのコンピュータと表示デバイスの文字セット。 Gforth は 8 ビットクリーンです(ただし、 システム内の他のコンポーネントが問題を引き起こす可能性があります)。

文字単位に整列されたアドレスの要件:

インストールに依存します。 現在、 文字は C言語の unsigned char で表されます。 (リクエストへのコメントですけども、 )将来的には wchar_t に切り替える可能性があります。

文字セット拡張と名前の照合:

ASCII NUL 文字を除く任意の文字を名前に使用できます。 照合では大文字と小文字が区別されません(TABLE を除く)。 照合は C 言語ライブラリー関数 strncasecmp を使用して実行されますが、 その関数はおそらくロケールの影響を受けます。 たとえば、C ロケールではアクセントとウムラウトが認識されないため、 そのロケールでは大文字と小文字が区別されて照合されます。 移植性の理由から、 C ロケールで動作するようにプログラムを作成することが最善です。 そうすれば、 ポーランド人のプログラマ(ISO Latin-2 でエンコードされた文字を含むワードを使用する可能性がある)と、 フランスのプログラマ(ISO Latin-1)が作成したライブラリーを同じプログラム内で使用できます(もちろん、 WORDS は一部のワードに対して愉快な結果を生成します(どのワードでそうなるかは、 使用しているフォントによって異なります))。 また、 あなたのご希望のロケールが他のオペレーティング・システムでは利用できない場合もあります。 Unicode がいつかこれらの問題を解決してくれることを願っています。

制御文字がスペース区切り文字と一致する条件:

word がスペース文字を区切り文字として使用して呼び出された場合、 すべての空白文字(C 言語マクロ isspace() で識別される)が区切り文字になります。 一方、 parse はスペースを他の区切り文字と同様に扱います。 parse-name はデフォルトで外部インタープリター (別名テキスト・インタープリター) によって使用され、 すべての空白文字を区切り文字として扱います。

制御フロースタックの形式:

データ・スタックは制御フロー・スタックとして使用されます。 制御フロー・スタック項目のセル単位でのサイズは、 constant cs-item-size によって与えられます。 この記事の執筆時点では、 制御フロー・スタック項目はローカル変数リスト(3番目)、 コード内のアドレス(2 番目)、 アイテムを識別するためのタグ(TOS) で構成されています。 タグとして次のタグが使用されています: defstart, live-orig, dead-orig, dest, do-dest, scopestart

35を超える「数字」の変換

文字 [\]^_' は、 10 進数値で 36〜41 の「数字」として解釈されます(訳注: 基数による)。 それより大きな「数字」の多くは(直接)入力する方法がありません。

ACCEPT および EXPECT で入力が終了した後の表示:

入力した文字列の末尾にカーソルが移動します。 Return キーを使用して入力を終了する場合は、 スペースが入力されます。

ABORT" の 例外中止(exception abort)シーケンス:

エラー文字列は変数 "error に保存され、 -2 throw が実行されます。

入力行終端文字:

対話入力の場合は、 C-m (CR) および C-j (LF) で行を終了します。 通常、 これらの文字は、 Enter キーまたは Return キーを入力したときに生成されます。

カウンタ付き文字列の最大サイズ:

s" /counted-string" environment? drop . で得られます。 現在、 すべてのプラットフォームで 255 文字ですが、 これは変更される可能性があります。

パースされる字列の最大サイズ:

constant /line によって与えられます。 現在 255 文字です。

定義名の最大サイズ(文字単位):

MAX-U / 8 (s" max-u" environment? drop 8 u/ u.)

ENVIRONMENT? の最大文字列長(文字単位):

MAX-U / 8 (s" max-u" environment? drop 8 u/ u.)

ユーザー入力デバイスを選択する方法:

ユーザー入力デバイスは標準入力です。 現時点では、 Gforth 内から変更する方法はありません。 ただし、 入力は通常、Gforth を起動するOSコマンド・ラインでリダイレクトできます。

ユーザー出力デバイスを選択する方法:

EMIT および TYPE は、値 outfile-id (デフォルトでは stdout) に格納されているファイル ID に出力します。 Gforth は、ユーザー出力デバイスが端末の場合はバッファなしの出力を使用します。 それ以外の場合、 出力はバッファリングされます。

ディクショナリーのコンパイル方法:

わざわざここで文書化する必要ある?(What are we expected to document here?)

アドレス単位 1 つ分のビット数:

s" address-units-bits" environment? drop . で得られます。 現在はすべてのプラットフォームで 8

数値の表現と算術演算:

プロセッサに依存します。 現在のすべてのプラットフォームでは 2 進数は 2 の補数表現です。

整数型の範囲:

インストールに依存します。 MAX-NMAX-UMAX-DMAX-UD の環境クエリ(environmental queries)を作成します。 符号なし(および正)型の下限は 0 です。 2 の補数マシンおよび 1 の補数マシンの符号付き型の下限は、 その上限に 1 を加算することで計算できます。

読み取り専用データ空間領域:

Forth データ空間全体が書き込み可能です。

WORD のバッファのサイズ:

PAD HERE - . で得られます。 32 ビット マシンでは 104 文字。 バッファは、 数値表示出力文字列(pictured numeric output string)と共有されます。 PAD の上書きが許容される場合、 そのサイズは残りの辞書スペースと同じになりますが、 実用になるのはカウンタ付き文字列に収まる範囲程度です。

アドレス単位での 1 セルのサイズ:

1 cells . で得られます。

アドレス単位での 1 文字のサイズ:

1 chars . で得られます。 現在のすべてのプラットフォームで 1

キーボード・ターミナル・バッファのサイズ:

さまざまです。 lp@ tib - . を使用して、 特定の時点でのサイズを確認できます。 これは、 現在のファイルを含むファイルのローカル変数スタックおよび TIB と共有されます。 コマンド・ライン・オプション -l を使用して、 Gforth 起動時に TIB とローカル変数スタックのスペースの量を変更できます。

表示数値出力(pictured numeric output)バッファのサイズ:

PAD HERE - . で得られます。 32 ビット マシンでは 104 文字。 バッファは WORD と共有されます。

PAD によって返されるスクラッチ領域のサイズ:

ディクショナリー・スペースの残りまるごと。 unused pad here - - . で得られます。

システムが英大文字・小文字を区別するか(case-sensitivity characteristics):

ディクショナリーの検索では大文字と小文字が区別されません(TABLE を除く)。 ただし、 上記 character-set extensions で説明したように、 非 ASCII 文字のマッチングは使用しているロケールによって決まります。 デフォルトの C ロケールでは、 すべての非 ASCII 文字は大文字と小文字を区別して照合されます。

システムのプロンプト:

インタープリター状態では ok で、 コンパイル状態では compiled です。

除算の四捨五入(division rounding):

通常の除算ワード / mod /mod */ */mod は、フロア除算(floored division)を実行します (Gforth のデフォルトのインストールを使用の場合)。 s" floored" environment? drop . でこれを確認できます。 特定の除算の四捨五入が必要なプログラムを作成する場合は、 移植性を高めるために fm/mod または sm/rem を使用するのが最適です。

true の場合の STATE の値:

-1.

算術オーバーフロー後に返される値:

2 の補数マシンでは、 1倍長の場合は 2**bits-per-cell 、 2倍長の場合はセルあたり 4**bits-per-cell を法として演算(modulo)が実行されます(符号付き型では適切なマッピングを使用)。 ゼロによる除算は通常、 -55 throw (Floating-point unidentified fault) または -10 throw (divide by zero) を引き起こします。 整数除算のオーバーフローにより、 -55 throw-10 throw 、 または -11 throw が発生する可能性があります。 gforth-fast エンジン(OSコマンドラインから gforth-fast で起動)の除算オーバーフローやゼロ除算では、 例外が生成されずに偽の結果が返される可能性があります。

現在の定義(current definition)が DOES> の後ろで見つかる(可視)かどうか:

いいえ(No)。


9.1.2 Ambiguous conditions

ワードでも数字でもない名前:

-13 throw (Undefined word)

定義名が許可される最大長を超えています:

-19 throw (Word name too long)

forth システムのさまざまなデータ空間内で無い領域をアドレス指定しようとした:

スタックやコード・スペースやヘッダー・スペースはアクセス可能です。 マシン・コード空間は通常、読み取り可能です。 他のアドレスにアクセスすると、 オペレーティング・システムに応じた結果が得られます。 まともなシステムの場合: -9 throw (Invalid memory address)

引数の型がパラメーターと互換性がありません:

これは通常はキャッチされません。 一部のワード(制御フロー・ワードなど)はチェックを実行し、 ABORT" または -12 THROW (Argument type mismatch) となります。

実行機能(execution semantics)が未定義であるワードの実行トークンを取得しようとしています:

実行トークンは、 ワードのインタープリター機能(interpretation semantics)を表します。 Gforth はすべてのワードのインタープリター機能を定義します。 標準でインタプリタ機能が定義されていないが、 実行機能(execution semantics)が定義されているワード(LEAVE を除く)については、 インタープリター機能が実行機能を実行します。 標準でインタープリター機能が定義されていないが、 コンパイル機能(compilation semantics)(および LEAVE)が定義されているワードの場合、 インタプリタ機能はコンパイル機能を実行します。 一部の単語はコンパイル専用(compile-only)としてマークされており、 ' はこれらのワードに対して警告を出します。

ゼロ除算:

一部のプラットフォームでは、 これにより -10 throw (Division by zero) が生成されます。 他のシステムでは、 通常、 これにより -55 throw (Floating-point unidentified fault) が生成されます。

データ・スタックのスペースまたはリターン・スタックのスペースが不十分:

オペレーティング・システムやインストール時の設定や Gforth の起動時の設定に応じて、 これはメモリー管理ハードウェアによって、 チェックされたり、チェックされなかったり。 これがチェックされている場合、 通常はオーバーフローが発生するするやいなや、 (プラットフォームとオーバーフローを達成した方法によって異なりますが、) -3 throw (Stack overflow) または -5 throw (Return stack overflow) または -9 throw (Invalid memory address) を受け取ります。 これがチェックされていない場合、 オーバーフローは通常、 原因不明の不正なメモリー・アクセスを引き起こし、 -9 throw (Invalid memory address) または -23 throw (Address alignment exception) を生成します。 また、ALLOCATE とそのファミリーの内部データ構造も破壊し、 これらのワードにさまざまなエラーが発生する可能性があります。

ループ制御パラメーター用のスペース不足:

他のリターン・スタック・オーバーフローと同様。

ディクショナリーのスペースが不足:

ディクショナリーで利用可能なメモリーを超えるメモリーを(allot で直接、 または ,create などで間接的に)割り当てようとすると、 -8 throw (Dictionary overflow) が発生します。 ディクショナリーの末尾を超えてメモリにアクセスしようとすると、 結果はスタック・オーバーフローと同様になります。

未定義のインタープリター機能(interpretation semantics)でワードを解釈(interpret):

Gforth は全てのワードのインタープリター機能(interpretation semantics)を定義します。 標準で実行機能(execution semantics)が定義されているワード(LEAVE を除く)については、 インタープリター機能が実行機能を実行します。 標準ではインタプリタ機能が定義されていないが、 コンパイル機能(compilation semantics)(および LEAVE)が定義されているワードの場合、 インタープリター機能はコンパイル機能。 一部のワードはコンパイル専用(compile-only)としてマークされており、 テキスト解釈(text-interpreting)すると警告が表示されます。

入力バッファまたは文字列リテラルの内容を変更:

これらは書き込み可能なメモリーに配置されており、 変更することができます。

表示数値出力文字列(pictured numeric output string)のオーバーフロー:

-17 throw (Pictured numeric ouput string overflow)

パースする文字列のオーバーフロー:

PARSE はオーバーフロー不可能です。 WORD はオーバーフローをチェックしません。

範囲外の結果の生成:

2 の補数マシンでは、 1倍長演算の場合は 2**bits-per-cell 、2倍長演算の場合は 4**bits-per-cell を法(modulo)として演算が実行されます(符号付き型は適切なマッピングを使用)。 ゼロ除算は通常、-10 throw (divide by zero) または -55 throw (floating point unidentified fault) を引き起こします。 除算のオーバーフローにより、 -10 throw (divide by zero) または -55 throw (floating point unidentified fault) が発生したり、 -11 throw (result out of range) が発生したりする可能性があります。 Gforth-fast エンジン(訳注: OSコマンドラインから Gforth-fast で起動)では、 除算のオーバーフローまたはゼロによる除算の際に、 暗黙的に偽の結果を生成する可能性があります。 Convert>number は現在、 何も警告を出さずにオーバーフローします。

空のデータ・スタックまたは空のリターン・スタックからの読み取り:

データ・スタックは、 各ワード execute 後、 外部インタープリター(別名 テキスト・インタプリタ)によってチェックされます。 アンダーフローした場合は、 -4 throw (Stack underflow) が発出されます。 それとは別に、 オペレーティング・システムやインストール時の設定や(OSからの gforth)起動時の設定に応じて、 スタックがチェックされるかどうかが異なります。 チェックで検出された場合、 通常は、プラットフォームや、 どのスタックがどの程度アンダーフローしたかに応じて、 通常は -4 throw (Stack underflow) または -6 throw (Return stack underflow) または -9 throw (Invalid memory address) が発生します。 注意: システムが(MMU を介した)チェックを使用している場合でも、 リアクションをトリガーするには、 プログラムがかなりの数のスタック項目をアンダーフローする必要がある場合があることに注意してください(その理由は、 MMU 絡みチェックがページ・サイズの粒度で動作するためです)。 チェックが行われない場合、アンダーフローによる症状はオーバーフローによる症状と似ています。 アンバランスなリターン スタック エラーは -9 throw (Invalid memory address) や不正な命令(通常は -260 throw)など、さまざまな症状を引き起こす可能性があります。

予期せず入力バッファの終わりに達したため、 長さ 0 の文字列を名前として使用しようとした:

Create とその子孫は -16 throw (Attempt to use zero-length string as a name;長さ 0 の文字列を名前として使用しようとします)を発出します。 ' のようなワードは検索しても見つからない可能性があります。 注意: nextname を使用して長さゼロの名前を作成できることに注意してください(ホントにそうすべきかどうかをよく確認してください)。

>IN が指し示す値が入力バッファの長さより大きい:

次にパース・ワードを呼び出すと、 長さ 0 の文字列が返されます。

RECURSEDOES> の後に現れた:

DOES> の後のコードへの再帰呼び出しをコンパイルします。

RESTORE-INPUT の引数の入力ソースが現在の入力ソースと異なります:

-12 THROW 注意: 入力ファイルが閉じられると(たとえば、 ファイルの終わりに達したため、 その source-id は再利用される可能性があることに注意してください。 したがって、 閉じられたファイルを参照する入力ソース仕様(input source specification)を復元(restore)すると、 -12 THROW ではなく、 予期しない結果が発生する可能性があります。

将来的には、 Gforth は現在の入力ソース以外から入力ソースの仕様を復元(input source specifications)できるようになる可能性があります。

定義を含むデータス・ペースの割り当てを解除:

allot による割り当て解除はチェックされません。 これにより、通常、 メモリ・アクセス・フォールト(memory access faults)や不正な命令の実行(execution of illegal instructions)が発生します。

不正なアライメント(alignment)によるデータ空間の 読み取り/書き込み:

プロセッサに依存します。 通常、 -23 throw (Address alignment exception) が発生します。 アライメントがオンになっている 486 以降のプロセッサ上の Linux-Intel では、 アライメントが正しくないと -9 throw (Invalid memory address) が発生します。 報告によると、 アライメント制限があるのに違反を報告しない一部のプロセッサがあるとのことです。

データ空間ポインターが正しくアライメントされていない ,, C,:

他のアライメント・エラーと同様。

u+2 未満のスタック項目で PICK および ROLL を実行:

他のスタック・アンダーフローと同様。

ループ制御パラメーターが無効:

チェックされていません。 カウンタ付きループのワード群は、 リターン・スタック項目の先頭がループ制御パラメーターであると単純に想定し、 それに応じて動作します。

最新の定義は名前を持っていません(IMMEDIATE):

abort" last word was headerless".

TO で使用される VALUE で名前が定義されていません:

-32 throw (Invalid name argument) (名前がローカル変数であるか、 CONSTANT によって定義されている場合を除きます。 後者の場合は、 定数が変更されるだけです)。

名前が見つかりません(', POSTPONE, ['], [COMPILE]):

-13 throw (Undefined word)

パラメーターが同じ型(type)ではありません(DO, ?DO, WITHIN):

Gforth は、 それらが同じタイプであるかのように振る舞います。 つまり、 あなたは、 すべてのパラメーターを符号付きなどとして解釈することで、振る舞いを予測できます。

POSTPONE または [COMPILE]TO に適用されました:

: X POSTPONE TO ; IMMEDIATE と仮定します。 XTO のコンパイル機能(compilation semantics)を実行します。

WORD によって返されるカウンタ付き文字列よりも長い文字列:

チェックされていません。 文字列は問題ありませんが、 当然ながら、 そのカウントにはその長さの最下位ビット達のみが含まれます。

指定の u がセル内のビット数以上(LSHIFTRSHIFT):

プロセッサに依存します。 一般的な振る舞いは、 0 を返し、 シフト・カウントの下位ビット達のみを使用することです。

CREATE によって定義されたワードではない:

>BODY は、 ワードの定義方法に関係なく、 ワードの PFA を生成します。

DOES> は、 定義方法に関係なく、 最後に定義されたワードの実行機能(execution semantics)を変更します。 たとえば、 CONSTANT DOES>CREATE , DOES> と同等です。

<##> の外で不適切に使用されているワード:

チェックされていません。 いつものように、 メモリー障害が発生することが予想されます。


9.1.3 Other system documentation

PAD を使用した非標準ワード:

無し。

利用可能なオペレータの端末設備(operator’s terminal facilities):

OS のコマンド・ライン処理後、 Gforth は対話モードになり、 Gforth に対話的にコマンドを与えることができます。 実際に利用できる機能は、Gforth の呼び出し方法によって異なります。

利用可能なプログラム・データ空間:

UNUSED . は残りのディクショナリー空間を与えます。 合計ディクショナリー空間は、 Gforth の起動時に -m スイッチで指定できます(see Invoking Gforth)。

使用可能なリターン・スタック空間:

s" RETURN-STACK-CELLS" environment? drop . を使用して、 セル単位での合計リターン・スタック空間を計算できます。 あなたは gforth 起動時に -r スイッチを使用して指定できます(see Invoking Gforth)。

利用可能なデータ・スタック空間:

s" STACK-CELLS" environment? drop . を使用して、 セル単位での合計データ・スタック空間を計算できます。 あなたは gforth 起動時に -d スイッチを使用して指定できます(see Invoking Gforth)。

必要なシステム・ディクショナリー空間(アドレス単位;aus):

起動直後に here first-start - . と入力します。 この記事の執筆時点では、 32 ビット システムでは 80080 (バイト) になります(訳注: Gforth 0.7.9_20240418 amd64 では 649000 aus(address unit))


9.2 The optional Block word set


9.2.1 実装毎オプション(Implementation Defined Options;idef)

LIST 使用時の表示書式:

最初にスクリーン番号が表示され、次に 1 行当たり 64 文字からなる 16 行が表示され、 各行の前に行番号が表示されます。

\ の影響を受ける行の長さ:

64 文字。


9.2.2 Ambiguous conditions

ブロックを正常に読み取りできませんでした:

通常、 OS 由来の値 (-512 ~ -2048) の throw が返されます。 ブロック・ファイルの長さが十分でない場合、 足りない部分は空白を返します。

ブロックの転送時の I/O 例外:

通常、 OS 由来の値 (-512 ~ -2048) の throw が返されます。

不正なブロック番号:

-35 throw (Invalid block number)

プログラムが BLK の値を直接書き換えた場合:

入力ストリームは、 同じ位置で別のブロックに切り替えられます。 非ブロック入力の解釈(interpret)時に BLK への値の格納が行われた場合、 ブロックの終了時にシステムがかなり混乱することでしょう。

current block buffer が無い状態で UPDATE を実行した:

UPDATE は何もしません。


9.2.3 Other system documentation

マルチ・プログラミング・システムがバッファ・アドレスの使用に課す制限:

(今のところ) 制限はありません。

ソース・コードとデータに使用できるブロックの数:

あなたのディスク容量によります。


9.3 The optional Double Number word set


9.3.1 Ambiguous conditions

D>S において、 dn の範囲を超えている:

d の最下位セルだけを使います。


9.4 The optional Exception word set


9.4.1 実装毎オプション(Implementation Defined Options;idef)

THROW コード値:

コード -256〜-511 は、 シグナルに使用されます。 OS シグナル番号から throw コードへのマッピングは、-256−signal です。 コード -512〜-2047 は、OS エラー(ファイルおよびメモリー割り当て操作)に使用されます。 OS エラー番号から throw コードへのマッピングは、-512−errno です。 このマッピングの副作用の 1 つは、 未定義の OS エラーにより、 奇妙な番号のメッセージが生成されることです。 例: -1000 THROW の場合、 私のシステムでは Unknown error 488 が発生します。


9.5 The optional Facility word set


9.5.1 実装毎オプション(Implementation Defined Options;idef)

キーボード・イベントのエンコード (EKEY):

ASCII 文字に対応するキーは ASCII 文字としてエンコードされます。 他のキーは次の定数でエンコードされます: k-left, k-right, k-up, k-down, k-home, k-end, k1, k2, k3, k4, k5, k6, k7, k8, k9, k10, k11, k12, k-winch, k-eof

システム・クロック・ティックの間隔:

システムに依存します。 ワード MS に関しては、 時間はマイクロ秒単位で指定されます。 OS とハードウェアがこれをどの程度うまく実装するかは別の問題です。

ワード MS の execute で期待される再現性:

システムに依存します。 Unix では、多くのことが負荷に依存します。 システムの負荷が軽く、 Gforth がスワップアウトされないほど遅延が短い場合、 パフォーマンスは許容範囲内です。 MS-DOS やその他のシングルタスク・システムではパフォーマンスは良好です。


9.5.2 Ambiguous conditions

AT-XY は、 あなたの出力デバイスでは実行できませんした:

主に端末に依存します。 引数の範囲チェックは行われません。 エラーは報告されません。 ゴミが表示される場合もあれば、 何も起こらない場合もあります。


9.6 The optional File-Access word set


9.6.1 実装毎オプション(Implementation Defined Options;idef)

ファイル・アクセス・モード:

R/OR/WBIN は期待どおりに機能します。 W/O は C言語のファイル・オープニング・モード w (または wb) に変換され、 ファイルが存在する場合はクリアされ、 存在しない場合は作成されます( open-filecreate-file 両方とも)。 Unix では、 create-file は、 あなたの umask によって変更された 666 権限を持つファイルを作成します。

ファイルの例外:

file ワード群は例外を発出しません(たぶん、 不正なアドレスまたは不正なファイル ID を渡した場合のメモリー・アクセス失敗(memory access faults)例外が発生します)。

ファイルの行末文字:

システムに依存します。 Gforth は C言語の改行文字を行終端文字として使用します。 実際の文字コードが何であるかはシステムによって異なります。

ファイル名形式:

システムに依存します。 Gforth はあなたの OS のファイル名形式を使用するだけです。

FILE-STATUS によって返される情報:

FILE-STATUS は、 ファイルに許可されている最も強力なファイル・アクセス・モードを返します。 それは R/O または W/O または R/W のいずれかです。 ファイルにアクセスできない場合は、 R/O BIN が返されます。 BIN は、 返されたモードと一緒に適用されます。

ソース・コードのインクルード時の例外後の入力ファイルの状態:

例外の発出により、 残されたすべてのファイルはクローズされます。

ior の値と意味:

ファイルおよびメモリー割り当てワード群によって返される ior は throw コードとして意図されています。 通常、これらは OS エラーの -512〜-2047 の範囲内にあります。 OS エラー番号から ior へのマッピングは -512−errno です。

ファイル入力の入れ子の最大深さ:

リターン・スタックと、 ローカル変数/TIB スタックと、 オープン可能なファイルの数によって制限されます。 この制限内である限り問題は起きないはずです。

入力行の最大文字数:

/line で得られます。 現在は 255

ブロックの範囲をファイルにマッピングする方法:

デフォルトでは、 ブロックは現在の作業ディレクトリ内のファイル blocks.fb 内でアクセスされます。 ファイルは USE で切り替えることができます。

S" によって提供される文字列バッファの数:

利用可能なメモリー量だけ。 文字列は、 ALLOCATE で割り当てられたメモリー・ブロックに無期限に保存されます。

S" によって使用される文字列バッファのサイズ:

/line でられます。 現在 255


9.6.2 Ambiguous conditions

ファイル内の位置をファイルの長さを超えて設定しようとしています:

REPOSITION-FILE は通常どおり実行されます。 その後、 FILE-POSITIONREPOSITION-FILE で指定された値を返します(訳注: それがEOFを超えた値であっても)。

まだ書き込まれていないファイル内位置から読み取ろうとしています:

EOF 、 つまりゼロ文字(zero characters)が読み取られ、 エラーは報告されません。

file-id が不正 (INCLUDE-FILE):

適切な例外が throw される可能性もありますが、 メモリー障害またはその他の問題が発生する可能性の方が高いです。

指定の file-id での読み取りまたはクローズでの I/O 例外(INCLUDE-FILE, INCLUDED):

問題を発見した操作によって生成された ior が throw されます。

指定の名前のファイルを開けません (INCLUDED):

open-file によって生成された ior が throw されます。

マップされていないブロック番号をリクエストしています:

マップされていない正当なブロック番号はありません。 一部のオペレーティング・システムでは、 大きな番号のブロックを書き込むとファイル・システムがオーバーフローし、 その結果エラー・メッセージが表示される場合があります。

blk がゼロ以外の場合に source-id を使用した:

source-id は自身の機能を実行します。 通常、 ブロックをロードしたソースの ID が表示されます。 (作者たちが考えたのより良いアイデアがあれば教えてください)


9.7 The optional Floating-Point word set


9.7.1 実装毎オプション(Implementation Defined Options;idef)

浮動小数点数の形式と範囲:

システムに依存します。 C言語の double 型 です。

float が範囲外の場合の REPRESENT の結果:

システムに依存します。 REPRESENT は C言語のライブラリー関数 ecvt() を使用して実装されているので、 この点においてその振る舞いを継承します。

浮動小数点数の丸めまたは切り捨て:

丸めの振る舞いは、 ホストしている C 言語のコンパイラーから継承されます。 IEEE-FP ベースの(つまり、ほとんどの)システムは、 デフォルトで最も近い値に丸められ、 偶数に丸める(つまり、 仮数の最後のビットを 0 にする)ことによって決着させます。

浮動小数点スタックのサイズ:

s" FLOATING-STACK" environment? drop . は、 浮動小数点スタックの合計サイズ(float 単位)を示します。 これは、 gforth 起動時にコマンドライン・オプション -f を使用して指定できます(see Invoking Gforth)。

浮動小数点スタックの各項目の大きさ:

1 floats で得られます。


9.7.2 Ambiguous conditions

df@ または df! が 2倍長 float にアライメントされていないアドレスで使用されています:

システムに依存します。 通常、 他のアライメント違反と同様に -23 THROW が発生します。

f@ または f! が float にアライメントされてないアドレスで使用されています:

システムに依存します。 通常、 他のアライメント違反と同様に -23 THROW が発生します。

浮動小数点の結果が範囲外です:

システムに依存します。 -43 throw (floating point overflow) または -54 throw (floating point underflow) または -41 throw (floating point inexact result;浮動小数点の不正確な結果) または -55 THROW (Floating-point unidentified fault;浮動小数点の未確認の障害) を発出する可能性があり、 または、 例えば無限大(Infinity)を表す特別な値が生成される可能性があります。

sf@ または sf! が1倍長浮動小数点数にアライメントされていないアドレスで使用されています:

システムに依存します。 通常、 他のアライメント違反と同様にアライメント違反が発生します。

base が 10 進数ではありません (REPRESENTF.FE.FS.):

それでもなお、 浮動小数点数は 10 進数に変換されます。

両方の引数がゼロに等しい(FATAN2):

システムに依存します。 FATAN2 は、 C言語のライブラリー関数 atan2() を使用して実装されます。

引数 r1FTAN を使用する場合、 cos(r1) はゼロです:

システムに依存します。 いずれにせよ、通常、 r1 の cos は小さな誤差によりゼロにはならず、 tan は非常に大きい(または非常に小さい)ものの有限の数になります。

d は、 ワード D>F で 2倍長整数から不動小数点数に変換しようとしますが、 浮動小数点数として正確に表すことができません:

結果は最も近い浮動小数点数に丸められます。

ゼロ除算:

プラットフォームに依存します。 Infinity または NaN または -42 throw (floating point divide by zero) または -55 throw (Floating-point unidentified fault) が生成される可能性があります。

変換のための指数が大きすぎます (DF!DF@SF!SF@):

システムに依存します。 IEEE-FP ベースのシステムでは、 数値は infinity (無限大)に変換されます。

float<1 (FACOSH):

プラットフォームに依存します。 IEEE-FP システムでは通常、 NaN が生成されます。

float<=-1 (FLNP1):

プラットフォームに依存します。 IEEE-FP システムでは通常、NaN (または float=-1 の場合は negative infinity(負の無限大))が生成されます。

float<=0 (FLN, FLOG):

プラットフォームに依存します。 IEEE-FP システムでは通常、 NaN (または float=0 の場合はnegative infinity(負の無限大))が生成されます。

float<0 (FASINH, FSQRT):

プラットフォームに依存します。 fsqrt の場合、 これは通常 NaN を返します。 fasinh の場合、一部のプラットフォームでは NaN が生成され、 その他のプラットフォームでは数値が生成されます(C ライブラリーのバグかな?)。

|float|>1 (FACOS, FASIN, FATANH):

プラットフォームに依存します。 IEEE-FP システムは通常、 NaN を生成します。

float の整数部分は、 F>Dd で表すことができません:

プラットフォームに依存します。 通常、 2倍帳の数値が生成されますが、 エラーは報告されません。

表示数値数値出力(pictured numeric output)領域より大きい文字列(f.fe.fs.):

数値出力領域の Precision 文字が使用されます。 precision が高すぎる場合、 これらのワードは here に近いデータまたはコードを破壊します。


9.8 The optional Locals word set


9.8.1 実装毎オプション(Implementation Defined Options;idef)

定義内のローカル変数の最大数:

s" #locals" environment? drop . で得られます。 現在 15 です。 これは下限です。たとえば、 32 ビット・マシンでは、最大 8 文字のローカル変数が 41 個存在する可能性があります。 定義内のローカルの数は、 ローカル変数の名前を含むローカル変数バッファ(locals-buffer)のサイズによって制限されます。


9.8.2 Ambiguous conditions

名前付きローカル変数をインタープリター状態(interpretation state)で実行:

ローカル変数は現在の定義内コンパイルします(つまりコンパイル状態です)。 さらに、 ローカル変数をインタープリター状態でテキスト解釈(text-interpreting)すると、 “is compile-only” という警告が表示されます。

nameVALUE または (LOCAL) で定義されていません(TO):

-32 throw (Invalid name argument)


9.9 The optional Memory-Allocation word set


9.9.1 実装毎オプション(Implementation Defined Options;idef)

ior の値と意味:

ファイルおよびメモリー割り当てワード群によって返される ior は throw コードとして意図されています。 通常、これらは OS エラーの -512〜-2047 の範囲内にあります。 OS エラー番号から ior へのマッピングは -512−errno です。


9.10 The optional Programming-Tools word set


9.10.1 実装毎オプション(Implementation Defined Options;idef)

;CODECODE に続く入力シーケンスの終端:

END-CODE

;CODE および CODE に続く入力の処理方法:

ASSEMBLER ボキャブラリーが検索順序(the search order)スタックにプッシュされ、 入力はテキスト・インタープリターによって(開始時に)インタープリター状態で処理されます。

EDITOR および ASSEMBLER の検索順序:

Search-Order ワードセット参照

SEE による表示形式と表示ソース:

see のソースは、 内部インタープリターによって使用される実行可能コードです。 現在の see は、 Forth ソース・コード(および一部のプラットフォームではプリミティブのアセンブリ・コード)を可能な限り表示しようとします。


9.10.2 Ambiguous conditions

コンパイル済みワード・リストの削除(FORGET):

(まだ)実装されていません。

制御フロー・スタック上の項目の数が u+1 未満です(CS-PICK, CS-ROLL):

通常、 これにより、 説明的なエラー・メッセージを含む abort" が表示されます(将来的には -22 throw (Control structure mismatch) に変更される可能性があります)。 また、 メモリー・アクセス・エラーが発生する可能性もあります。 残念なことに、 この曖昧な状態はキャッチできません。

FORGET が見つかりません:

(まだ)実装されていません。

nameCREATE によって定義されていません:

この点において、 ;CODEDOES> と同様に動作します。 つまり、 定義方法に関係なく、 最後に定義されたワードの実行機能(execution semantics)を変更します。

POSTPONE[IF] に適用されました:

: X POSTPONE [IF] ; IMMEDIATE 定義後の X[IF] と同等です。

一致する [ELSE] または [THEN] に到達する前に入力ソースの末尾に到達しました:

次の外部入力ソースでも同一の条件付きコンパイル状態を継続します。 現時点では、 これに関するユーザーへの警告はありません。

必要とされる定義の削除(FORGET):

(まだ)実装されていません。


9.11 The optional Search-Order word set


9.11.1 実装毎オプション(Implementation Defined Options;idef)

検索順序内のワードリストの最大数:

s" wordlists" environment? drop . で得られます。 現在 16

最小の検索順序:

root root.


9.11.2 Ambiguous conditions

コンパイル・ワードリストの変更(コンパイル中):

ワードは、 定義の先頭時点でコンパイル・ワードリストであるワードリストに追加されます。 名前フィールドへの変更(たとえば immediate)またはコード・フィールドへの変更(例えば DOES> の execute)は、 それらが変更可能であれば、 コンパイル・ワードリストに関係なく、 もっとも最新に定義されたワードに適用されます。

検索順序が空です (previous):

abort" Vocstack empty"

検索順序内のワードリストが多すぎます(also):

abort" Vocstack full"


10 Should I use Gforth extensions?

このマニュアルの残りの部分を読み進めると、 標準ワードのドキュメントと、 いくつかの魅力的な Gforth 拡張のドキュメントがあります。 あなたは、 「標準に限定すべきか、 それとも gforth での拡張を使用すべきでしょうか?」(Should I restrict myself to the standard, or should I use the extensions?)

その答えは、 あなたが取り組んでいるプログラムの目標(goal)によって異なります:

プログラムを Gforth のみに制限しても問題ない場合、 Gforth 拡張を使用しない理由はありません。 移植性を保ちたい別のプログラムでこれらのパーツを再利用したい場合に備えて、 標準を守るのが簡単なところでは標準を守るのは良い考えです。

プログラムを他の Forth システムに移植できるようにしたい場合は、 以下の点を考慮する必要があります:

これらの考慮事項を実行するには、 何が標準で何が標準ではないかを知る必要があります。 このマニュアルには通常、 何が非標準であるかが記載されていますが、 信頼できる情報源は standard document です。 Appendix A of the Standard (Rationale) は、技術委員会の思考プロセスについての貴重な洞察を提供します。

注意: Forth システム間の移植性だけが移植性の問題ではないことにも注意してください。 異なるプラットフォーム (プロセッサと OS の組み合わせ)間の移植性の問題もあります。


11 Model

(著者達は)この章はまだ記述していません。 これには、 信頼できる内部構造に関する情報が含まれます。


12 Integrating Gforth into C programs

C または C++ またはその他の言語で書かれているアプリケーションのスクリプト言語として Forth を使用することを好む人もいます。

Forth システム ATLAST は、 Forth システムをアプリケーションに組み込むための機能を提供します。 残念ながら、 これにはいくつかの欠点があります。 最も重要なのは、 標準 Forth に基づいておらず、 明らかに死んでいる(つまり、 これ以降開発されておらず、 サポートされていない)ことです。 この分野で Gforth が提供する機能は ATLAST の機能からインスピレーションを得ているため、 ATLAST からの切り替えは難しくはありません。

また、 著者達は、 いつか標準化されたインターフェイスに到達できるように、 他の Forth システムで簡単に実装できるようにインターフェイスを設計することも試みました。 このような標準インターフェイスを使用すると、 C 言語のコードを書き直すことなく他の Forth システムに置き換えることができます。

Gforth インタープリターを埋め込むには、 ライブラリー libgforth.a または libgforth.so とリンクします(コンパイラーにオプション -lgforth を指定するか、 他のエンジンのいずれかの場合は -lgforth-fast または -lgforth-itc または -lgforth-ditc を指定します)。 このライブラリ内のインターフェイスに属するすべてのグローバル・シンボルには、 接頭辞 gforth_ が付いています。 共通のインターフェイスが出現した場合、 接頭辞 forth_ を付けた #define を通じて関数を利用できる可能性もあります。

#include <gforth.h> を使用して、 Forth 型の宣言と、 インターフェイス関数と、 変数をインクルードすることができます。

いまや、 あなたは gforth_main を呼び出すか、 以下のコンポーネントを使用して、 Gforth セッションを実行できるようになりました:

Cell gforth_main(int argc, char **argv, char **env)
{
  Cell retvalue=gforth_start(argc, argv);

  if(retvalue == -56) { /* throw-code for quit */
    retvalue = gforth_bootmessage();     // show boot message
    if(retvalue == -56)
      retvalue = gforth_quit(); // run quit loop
  }
  gforth_cleanup();
  gforth_printmetrics();
  // gforth_free_dict(); // if you want to restart, do this

  return retvalue;
}

Forth インタープリタと対話するには、 Xt gforth_find(Char * name)Cell gforth_execute(Xt xt) があります。

(未記述)(さらなるドキュメントをここに置く必要があります。)

12.1 Types

Cell, UCell: データ・スタック項目

Float: FPスタック項目

AddressXtLabel: メモリーと、Forthワードと、VM内部のForth命令への、 ポインター・タイプ。

12.2 Variables

データスタックポインターおよびFPスタックポインター。 領域のサイズ。スタックへのアクセス

gforth_SP, gforth_FP.

12.3 Functions

void *gforth_engine(Xt *, stackpointers *);
Cell gforth_main(int argc, char **argv, char **env);
int gforth_args(int argc, char **argv, char **path, char **imagename);
ImageHeader* gforth_loader(char* imagename, char* path);
user_area* gforth_stacks(Cell dsize, Cell rsize, Cell fsize, Cell lsize);
void gforth_free_stacks(user_area* t);
void gforth_setstacks(user_area * t);
void gforth_free_dict();
Cell gforth_go(Xt* ip0);
Cell gforth_boot(int argc, char** argv, char* path);
void gforth_bootmessage();
Cell gforth_start(int argc, char ** argv);
Cell gforth_quit();
Xt gforth_find(Char * name);
Cell gforth_execute(Xt xt);
void gforth_cleanup();
void gforth_printmetrics();
void gforth_setwinch();

12.4 Signals

Gforth は、 例外とウィンドウ・サイズの変更をキャッチするためにシグナル・ハンドラーを設定します。 これにより、C言語プログラムが妨げられる可能性があります。


13 Emacs and Gforth

Gforth には、 Goran Rydqvist による forth.el の改良版である gforth.el が付属しています(TILE パッケージ を利用しています)。 改善点は以下のとおり:

これらの機能の基本的な説明を取得するには、 forth-mode に入り、 C-h m と入力します。

さらに、 Gforth は Emacs を全力サポートします。 エラー・メッセージや、 (~~ からの)デバッグ出力や、 失敗した assert のメッセージで指定されるソース・コードの場所は、 Emacs のコンパイル・モード(see Running Compilations under Emacs in Emacs Manual)に適切な形式になっているため、 エラーまたはその他のメッセージに対応するソースの場所は数回のキーストロークだけでアクセスできます(例えば、 次のエラーは C-x ` カーソル下のエラーは C-c C-c)。

さらに加えて、 このマニュアルに記載されているワードについては、 C-h TAB を使用して用語集のエントリをすばやく検索できます(info-lookup-symbol, see Documentation Commands in Emacs Manual)。 この機能には Emacs 20.3 以降が必要で、 : を含むワードに対しては機能しません。


13.1 Installing gforth.el

gforth.el の機能を Emacs で利用できるようにするには、 .emacs ファイルに次の行を追加します(訳注: 2024年時点では .emacs.d/init.el ):

(autoload 'forth-mode "gforth.el")
(setq auto-mode-alist (cons '("\\.fs\\'" . forth-mode) 
			    auto-mode-alist))
(autoload 'forth-block-mode "gforth.el")
(setq auto-mode-alist (cons '("\\.fb\\'" . forth-block-mode) 
			    auto-mode-alist))
(add-hook 'forth-mode-hook (function (lambda ()
   ;; customize variables here:
   (setq forth-indent-level 4)
   (setq forth-minor-indent-level 2)
   (setq forth-hilight-level 3)
   ;;; ...
)))

;; 訳注: とりあえずインストール( .emacs.d/init.el ):
;; /path/to/gforth-dev フォルダに gforth.el gforth.elc があるとして
(add-to-list 'load-path "/path/to/gforth-dev")
(require 'forth-mode "gforth")

13.2 Emacs Tags

etags.fsrequire すると、 その後に定義されるすべてのワードの定義を含む新しい TAGS ファイル(see Tags Tables in Emacs Manual)が生成されます。 あなたは M-. を使用してワードのソースを見つけることができます。 注意: Emacs は複数の TAG ファイルを同時に使用できることに注意してください(たとえば、 1 つは Gforth ソース用、 もう 1 つはあなたのプログラム用、 などです。 see Selecting a Tags Table in Emacs Manual)。 事前にロード(preload)されるワードの TAGS ファイルは $(datadir)/gforth/$(VERSION)/TAGS です(例えば /usr/local/share/gforth/0.2.0/TAGS)。 etags.fs で最良の動作を得るには、 require などの前後の両方に定義を置かないようにすべきです。 そうしないと、 tags-search のようなコマンドによって同一ファイルが何度もアクセスされることになります。


13.3 Hilighting

gforth.el には、カスタム・ソース・ハイライティング・エンジンが付属しています。 ファイルを forth-mode で開くと、 そのファイルは完全にパースされ、 キーワードやコメントや文字列などに face を割り当てます。 ファイルを編集している間、 変更されたリージョンはその場(on-the-fly)でパース・更新されます。

Emacs の変数 ‘forth-hilight-level’ を使用して、 装飾レベルを 0 (まったく強調表示なし) から 3 (デフォルト) に変更します。 強調表示レベルを 0 に設定した場合でも、 パーサーはバックグラウンドで動作し、 テキストの領域が「コンパイル」(compiled)されているか「解釈」(interpreted)されているかに関する情報を収集します。 これらの情報は、 自動インデントが適切に機能するために必要です。 コンピュータが遅すぎてパース処理できない場合は、 Emacs の変数 ‘forth-disable-parser’ を非 nil に設定します。 ただし、 これは自動インデント・エンジンのスマートさに影響します。

しばしば Forth のソース・コードでは、 強調表示する必要がある新しい機能として、 新しい制御構造や定義ワードなどが定義される場合があります。 変数 ‘forth-custom-words’ を使用して、 forth-mode で追加のワードや構造を強調表示させることができます。 詳細については、‘forth-words’ の docstring を参照してください(Emacs では、C-h v out-words と入力します)。

‘forth-custom-words’ は .emacs.d/init.el ファイル内でカスタマイズすることを目的としています。 ファイル固有の方法でハイライトをカスタマイズするには、 あなたのソース・ファイルの最後のEmacsローカル変数セクションで ‘forth-local-words’ を設定します(see Variables in Emacs Manual)。

Example:

0 [IF]
   Local Variables:
   forth-local-words:
      ((("t:") definition-starter (font-lock-keyword-face . 1)
        "[ \t\n]" t name (font-lock-function-name-face . 3))
       ((";t") definition-ender (font-lock-keyword-face . 1)))
   End:
[THEN]

13.4 Auto-Indentation

TAB を入力するか、C-m で改行するたびに、 forth-mode は自動的にスマートな方法で行をインデントしようとします。

簡単なカスタマイズは、 .emacs.d/init.el ファイルで ‘forth-indent-level’ と ‘forth-minor-indent-level’ を設定することで実現できます。 歴史的な理由により、 gforth.el はデフォルトでは 4 の倍数の桁数インデントされます。 より伝統的な 3 桁のインデントを使用するには、 .emacs.d/init.el に以下の行を追加します:

(add-hook 'forth-mode-hook (function (lambda ()
   ;; customize variables here:
   (setq forth-indent-level 3)
   (setq forth-minor-indent-level 1)
)))

インデントでデフォルト以外のワードを認識したい場合は、 .emacs.d/init.el で ‘forth-custom-indent-words’ を設定してカスタマイズします。 詳細については、 ‘forth-indent-words’ の docstring を参照してください(Emacs では、C-h v four-indent-words と入力します)。

ファイル固有の方法でインデントをカスタマイズするには、 ソースファイルの最後のEmacsローカル変数セクションで ‘forth-local-indent-words’ を設定します(see Variables in Emacs Manual)。

Example:

0 [IF]
   Local Variables:
   forth-local-indent-words:
      ((("t:") (0 . 2) (0 . 2))
       ((";t") (-2 . 0) (0 . -2)))
   End:
[THEN]

13.5 Blocks Files

forth-mode は、 最初の行の長さが 1023 文字を超えているかどうかをチェックして、 ブロック・ファイルを自動検出します。 次に、 ファイルを通常のテキスト形式に変換しようと試みます。 あなたがファイルを保存すると、 通常のストリーム・ソース・ファイルとしてディスクに書き込まれます。

ブロック・ファイルへ書き込みたい場合は、 forth-blocks-mode を使用します。 これは forth-mode からすべての機能を継承しており、 さらにいくつかの追加機能があります:

  • ファイルはブロック・ファイル形式でディスクに書き込まれます。
  • スクリーン番号はモードラインに表示されます(‘forth-block-base’ で始まる番号です。 訳注:後方互換性の為に 0 または 1 から始まります)
  • 行の長さが 64 文字を超えると警告が表示されます。
  • 現在編集されているブロックの先頭は、 オーバーレイ矢印(overlay-arrow)でマークされます。

注意すべき制限がいくつかあります。 タブ文字または改行文字を含むブロック・ファイルを開くと、 ファイルがディスクに書き戻されるときに、 これらの文字はスペースに変換されます。 ブロック・ファイルの読み取り中にタブまたは改行が検出された場合、 エラーがエコーエリアに表示されます。 なので、 読み取り中に Emacs のベルが鳴ったら、‘*Messages*’ バッファを見てください。

詳細については、 C-h v forth-blocks-mode と入力して、 forth-blocks-mode の docstring を参照してください。


14 Image Files

イメージ・ファイルは、 Forth のディクショナリーのイメージ、 つまり、 ディクショナリ内に存在するコンパイルされた Forth コードとデータを含むファイルです。 慣例により、 イメージ・ファイルには拡張子 .fi を使用します。


14.1 Image Licensing Issues

gforthmi (see gforthmi) または savesystem (see Non-Relocatable Image Files) で作成されたイメージには、 オリジナルのイメージが含まれます。 つまり、 著作権法(copyright law)に従うと、 それはオリジナルのイメージの派生物となります。

Gforth は GNU GPL に基づいて配布されるため、 新しく作成されたイメージも GNU GPL に当てはまります。 特に、これは、 あなたがイメージを配布する場合、 あなた自身が書いたものも含め、 あなたのイメージのすべてのソースを利用可能にする必要があることを意味します。 詳細については、 GNU General Public License (Section 3) を参照してください。

cross (see cross.fs) を使用してイメージを作成した場合、 そのイメージには、 指定したソースからコンパイルされたコードのみが含まれます。 これらのソースのいずれも GPL に準拠していない場合、 上記の条件はこのイメージには適用されません。 ただし、 そのイメージに GPL に基づくエンジン(gforth バイナリ)が必要な場合に、 そのイメージに GPL の条件を適用したくない場合、 それらが一体にならないよう、 せいぜい単なる集合体にすぎない方法で両方を配布する必要があります。


14.2 Image File Background

Gforth は、 (エンジン内の) リミティブだけでなく、 Forth で書かれた定義でも構成されます。 Forth コンパイラー自体もこれらの定義に含まれているため、 エンジンと Forth ソースだけでシステムを起動することはできません。したがって、 Forth コードをほぼ実行可能な形式のイメージ・ファイルとして提供します。 Gforth が起動すると、 C言語のルーチンがイメージ・ファイルをメモリーにロードし、 オプションでアドレスを再配置し、 イメージ・ファイル内の情報に従ってメモリー(スタックなど)を設定し、 (最終的に) Forth コードの実行を開始します。

デフォルトのイメージ・ファイルは gforth.fi (GFORTHPATH 上の何処か)です。 -i または --image-file または --appl-image オプションを使用すると、 別のイメージを使用できます(see Invoking Gforth)。 例:

gforth-fast -i myimage.fi

イメージ・ファイルにはさまざまなバリエーションがあり、 イメージ・ファイルの生成を容易にするという目標と、 イメージ・ファイルを移植できるようにするという目標の間のさまざまな妥協点を表しています。

Win32Forth 3.4 と、 Mitch Bradley の cforth は実行時に再配置を使用します。 これにより、 以下で説明する複雑な問題の多くが回避されます(イメージ・ファイルは、 特に手間をかけることなくデータを再配置できます)が、 しかし、 パフォーマンスが低下し(メモリー・アクセスごとに 1 回の追加)、 そして、 Forth とライブラリー呼び出しまたは他のプログラムの間でアドレスを渡すことが困難になります。

対照的に、 Gforth ローダーはイメージのロード時に再配置を実行します。 また、 ローダーは、 プリミティブ呼び出しを表すトークンを適切なコード・フィールド・アドレス(直接スレッドの場合はコード・アドレス)に置き換える必要があります。

イメージ・ファイルには、 再配置の程度が異なる 3 種類があります。 つまり、 再配置不可能なイメージ・ファイルと、 データ再配置可能なイメージ・ファイルと、 完全に再配置可能なイメージ・ファイルです。

これらのイメージ・ファイルのバリエーションには、 いくつかの共通の制限があります。 それらは、 イメージ・ファイル・ローダーの設計によって引き起こされます:

  • セグメントは 1 つだけです。 これは、特に、 イメージ・ファイルが ALLOCATE されたメモリー・チャンク(およびそれらへのポインター)を表すことができないことを意味します。 スタックの内容も表現されません。
  • サポートされている再配置の種類は次のとおりです: データ・アドレスを表すすべてのセルに同じオフセットを追加します。 そして、 特別なトークンをコード・アドレスまたはマシン・コードの一片に置き換えます。

    アドレスを含む複雑な計算が実行される場合、 結果はイメージ・ファイルで表現できません。 このような計算を使用するアプリケーションがいくつか思い浮かびます:

    • テーブル・ルックアップのためのアドレス(またはアドレスを含むデータ構造)のハッシュ化。 この目的に Gforth の table または wordlist を使用する場合、 システムの起動時にハッシュ・テーブルが自動的に再計算されるため、問題はありません。 あなた独自のハッシュ・テーブルを使用する場合は、 同様のことをあなた自身で行う必要があります。
    • XOR されたアドレスを使用する二重リンク・リストの小さい実装があります。 このようなリストをイメージ・ファイル内で単一リンクとして表現し、 起動時に二重リンク表現を復元することもできます36
    • docol: のようなランタイム・ルーチンのコード・アドレスは、 イメージ・ファイルでは表現できません(直接スレッド実装ではトークンがマシン・コードに置き換えられるため)。 回避策として、 実行時に >code-address を使用して適切なワードの実行トークンからこれらのアドレスを計算します(kernel/getdoers.fsdocol: とそのファミリーの定義を参照してください)。
    • 多くのアーキテクチャーでは、 アドレスは何らかのシフトされた形式または切り刻んだ(mangle)形式でマシン・コードで表現されます。 この形式の絶対アドレスを含む CODE ワードを再配置可能イメージ・ファイルに含めることはできません。 回避策は、アドレスを何らかの相対形式(たとえば、 レジ​​スタに存在する CFA を基準とした相対形式)で表現するか、 アドレスが切り刻んでない(mangle)形式で格納されている場所からロードすることです。

14.3 Non-Relocatable Image Files

これらのファイルは、 ディクショナリーの単純なメモリー・ダンプです。 これらは、 作成された実行可能ファイル(つまり、gforth ファイル)に固有のものです。さらに悪いことに、 それらは、 イメージが作成されたときにディクショナリーが存在していた場所に固有のものです。 ええ、はい、 次回 Gforth を起動するときにディクショナリが同じ場所に存在するという保証はありません。 したがって、 再配置不可能なイメージが次回動作するという保証もありません(ただし、 Gforth はクラッシュする代わりにエラーを出します)。 実際、 アドレス空間のランダム化(が有効になっている) OS では、 再配置不可能なイメージが機能する可能性は低いです。

savesystem を使用して、 再配置不可能なイメージ・ファイルを作成できます。例:

gforth app.fs -e "savesystem app.fi bye"
savesystem ( "image" –  ) gforth-0.2 “savesystem”

14.4 Data-Relocatable Image Files

これらのファイルには、 再配置可能なデータ・アドレスが含まれていますが、 (トークンではなく)固定コード・アドレスが含まれています。 これらは、 作成された実行可能ファイル(つまり、gforth ファイル)に固有のものです。 また、 動的ネイティブ・コード生成も無効になります(通常、 速度が 2 倍になります)。 GFORTHD 環境変数を通じて使用するエンジンを gforthmi (see gforthmi) に渡すと、 データ再配置可能なイメージを取得できます。

GFORTHD="/usr/bin/gforth-fast --no-dynamic" gforthmi myimage.fi source.fs

注意: ここでイメージが機能するには --no-dynamic が必要であることに注意してください(そうしないと、 イメージに保存されていない動的に生成されたコードへの参照が含まれてしまいます)。


14.5 Fully Relocatable Image Files

これらのイメージ・ファイルには、 再配置可能なデータ・アドレスとコード・アドレスのトークンが含まれています。 これらは、 同じマシン上で異なるバイナリ(デバッグの有無など)で使用でき、 同一データ形式(バイト・オーダー、 セル・サイズ、 浮動小数点形式)を持つ複数のマシン間でも使用でき、 動的なネイティブ・コード生成で動作します。 ただし、 これらは通常、 作成された Gforth のバージョンに固有です。 ファイル gforth.fikernl*.fi は完全に再配置可能です。

完全に再配置可能なイメージ・ファイルを作成するには 2 つの方法があります:


14.5.1 gforthmi

通常は gforthmi を使用します。 gforth options で Gforth を呼び出してロードするすべてのものを含むイメージ file を作成したい場合は、以下のように言うだけです:

gforthmi file options

たとえば、 通常のものに加えてファイル asm.fs がロードされたイメージ asm.fi を作成したい場合は、 以下のように実行できます:

gforthmi asm.fi asm.fs

gforthmi は sh スクリプトとして実装され、 次のように動作します: 異なるアドレスに対して 2 つの再配置不可能なイメージを生成し、 それらを比較します。 その出力はこの比較を反映しています: まず、 再配置不可能なイメージ・ファイルを生成する 2 つの Gforth 呼び出しの出力(存在する場合)が表示され、 次に比較プログラムの出力が表示されます: データ・アドレスに使用されるオフセットとコードアドレスに使用されるオフセットを表示します。 データ・アドレスに使用されるオフセットと使用されるオフセットが表示されます。 さらに、 イメージ・ファイル内で正しく表現できないセルごとに、 以下のような行が表示されます:

     78DC         BFFFFA50         BFFFFA40

これは、 forthstart からのオフセット $78dc に、一方の入力イメージには $bffffa50 が含まれ、 もう一方の入力イメージには $bffffa40 が含まれていることを意味します。 これらのセルは出力イメージでは正しく表現できないため、 ディクショナリ内のこれらの場所を調べて、 これらのセルが死んでいること (つまり、書き込まれる前に読み取られていないこと)を確認する必要があります。

イメージ・ファイル名の前に --application オプションを挿入すると、 --image-file オプションの代わりに --appl-image オプションを使用したイメージを取得します(see Invoking Gforth)。 (イメージ名をコマンドとして入力して、 )このようなイメージを Unix 上で実行すると、 Gforth エンジンは、 オプションをエンジン・オプションとして解釈しようとはせず、 すべてのオプションをイメージに渡します。

引数を指定せずに gforthmi と入力すると、 いくつかの使用説明が表示されます。

ちょっとだけ説明すると、 渡された gforth-options 処理後、 savesystembye というワードが現れる必要があります。 gforth 実行可能ファイルの特別な二重間接スレッド化(doubly indirect threaded)バージョンは、 再配置不可能なイメージの作成に使用されます。 この実行可能ファイルの正確なファイル名は、 環境変数 GFORTHD(デフォルト: gforth-ditc) を介して渡すことができます。 二重間接スレッド化ではないバージョンを渡すと、 完全に再配置可能なイメージではなく、 データ再配置可能なイメージ(see Data-Relocatable Image Files)を取得します。 これは、 コード・アドレス・オフセットがないためです。 通常の gforth 実行可能ファイルは、 再配置可能イメージの作成に使用されます。 この実行可能ファイルの正確なファイル名は、 環境変数 GFORTH を介して渡すことができます。


14.5.2 cross.fs

cross という、 Forth風のプログラミング言語を受け入れるバッチ・コンパイラーを使用することもできます(see Cross Compiler)。

cross を使用すると、 イメージ・ファイルの生成に使用したものとは異なるデータ・サイズとデータ形式を持つマシン用のイメージ・ファイルを作成できます。これを使用して、 Forth コンパイラーを含まないアプリケーション・イメージを作成することもできます。 これらの機能は、 プログラミング上の制限と不便を許容することで利用可能になります。 たとえば、 コードを再配置可能にするには、 アドレスを特別なワード (A!A, など) を使用してメモリーに保存する必要があります。


14.6 Stack and Dictionary Sizes

サイズのコマンド・ライン・フラグを指定して Gforth を呼び出すと(see Invoking Gforth)、 指定したサイズがディクショナリーに保存されます。 savesystem でディクショナリーを保存するか、 gforthmi でイメージを作成すると、 このサイズが結果のイメージ・ファイルのデフォルトになります。 たとえば、 以下の例では、 1MB のディクショナリーを備えた完全に再配置可能なバージョンの gforth.fi が作成されます。

gforthmi gforth.fi -m 1M

つまり、 ディクショナリーとイメージの、 スタックのデフォルト・サイズを設定したい場合は、 イメージの作成時に適切なオプションを指定して gforthmi を呼び出すだけです。

注意: キャッシュに優しい振る舞い(つまり、 良好なパフォーマンス)のために、 スタック達をわずかに異なるサイズにする必要があります。 キャッシュ・ラインを 2K としましょう。 例えば、 デフォルトのスタックサイズは、 2K で割ると剰余(mod)は次のようになっています: データ・スタック 16k (mod 2k=0); FPスタック 15.5k (mod 2k=1.5k); リターン・スタック 15k(mod 2k=1k); ローカル変数スタック 14.5k (mod 2k=0.5k)


14.7 Running Image Files

-i フラグを使用したデフォルトの gforth.fi の代わりに、 イメージ・ファイル image を使用して Gforth を呼び出すことができます(see Invoking Gforth):

gforth -i image

オペレーティング・システムが #! ... 形式の行によるスクリプトの開始をサポートしている場合は、 イメージ・ファイル名を入力するだけで、 そのイメージ・ファイルで Gforth を起動できます(ファイル拡張子 .fi は単なる慣例であることに注意してください)。 つまり、 イメージ・ファイル image を使用して Gforth を実行するには、 gforth -i image の代わりに image と入力するだけです。 これが機能するのは、すべての .fi ファイルが以下の形式の行で始まるためです:

#! /usr/local/bin/gforth-0.4.0 -i

この行で指定された Gforth エンジンのファイルとパス名は、 そのエンジンがビルドされた特定の Gforth 実行可能ファイルです。 つまり、 gforthmi が実行されたときの環境変数 GFORTH の値です。

同じシェル機能を利用して、 Forth ソース・ファイルを実行可能ファイルにできます。 たとえば、 以下のテキストをファイルに配置します:

#! /usr/local/bin/gforth

." Hello, world" CR
bye

そして、 ファイルを実行可能にすると(Unix の場合は chmod +x)、 コマンド・ラインから直接実行できます。 シーケンス #! は 2 つの側面から使用されます。 まず、 オペレーティング・システムによって「マジック・シーケンス」として認識され37、 次に Gforth によってコメント文字として扱われます。 2 番目の使用法のため、#! と実行可能ファイルへのパスの間に空白が必要です(さらに、 一部の Unix ではシーケンス #! / が必要です)。

ほとんどの Unix システム(Linux を含む)は、 バイナリ名の後​​に 1 つのオプションだけをサポートします。 それでは足りない場合は、 以下のトリックを使用できます:

#! /bin/sh
: ## ; 0 [if]
exec gforth -m 10M -d 1M $0 "$@"
[then]
." Hello, world" cr
bye \ caution: this prevents (further) processing of "$@"

最初に、 このスクリプトはシェル・スクリプトとして解釈され、 最初の 2 行が(ほとんど)コメントとして扱われ、 次に 3 行目が実行されると、 このスクリプト ($0) をパラメーターとして、 そのパラメーターを追加パラメーター ("$@") として gforth を呼び出します。 さらに次に、 このスクリプトは Forth スクリプトとして解釈され、 最初にコロン定義 ## が定義され、 次に [then] までのすべてが無視され、 最後にそれに続く Forth コードが処理されます。 あなたは以下を使用することもできます

#0 [if]

2 行目ですが、 これは Gforth-0.7.0 以降でのみ機能します。

gforthmi のアプローチは最も高速で、 シェル・ベースのアプローチは最も遅くなります(追加のシェルを起動する必要があるため)。 シェル・アプローチの追加の利点は、 Gforth バイナリ が $PATH 内にある限り、 Gforth バイナリがどこにあるかを知る必要がないことです。

#! ( ) gforth-0.2 “hash-bang”

\ の alias です。


14.8 Modifying the Startup Sequence

defer されたワード 'cold を使用して、 イメージの起動シーケンスに独自の初期化を追加できます。 'cold は、 イメージ固有のコマンドライン処理 (つまり、 ファイルのロードと (-e) 文字列の評価)が開始される直前に呼び出されます。

初期化を追加するシーケンスは通常以下のようになります:

:noname
    Defers 'cold \ do other initialization stuff (e.g., rehashing wordlists)
    ... \ your stuff
; IS 'cold

'cold の後、 Gforth はイメージ用のオプションを処理し(see Invoking Gforth)、 別の defer されたワードである bootmessage を実行します。 これは通常は、 Gforth の起動メッセージを出力し、 他には何も行いません。

したがって、 ターンキー・イメージ(つまり、 拡張された Forth システムではなくアプリケーション用のイメージ)を作成したい場合は、 以下のいくつかの方法でこれを行うことができます:

  • OS コマンドライン引数を独自に解釈したい場合は、 'cold にフックします。 その場合、 エンジンが OS コマンド・ライン・オプションを処理しないように、 gforthmi --application (see gforthmi)を使用してイメージをビルドすることも必要になるでしょう。 その後、 next-arg を使用してあなた独自のコマンドライン処理を実行できます。
  • OS コマンドライン引数を通常の Gforth 処理で処理したいが、 あなた独自のコマンドライン・オプションを指定する場合、 process-option にフックします。
  • Gforth に付属しているオプションに加えてさらに多くのオプションが必要な場合は、 options ボキャブラリーでワードを定義します。
  • あなた独自のブート・メッセージを表示したい場合は、 bootmessage にフックします。

いずれの場合も、 あなたは、 おそらくこれらのフックで execute するワードを普通に終了さずに、 bye または throw を使用したいでしょう。 そうしないと、 Gforth 起動プロセスが続行され、 最終的に Forth コマンド・ラインがユーザーに表示されます。

'cold ( ) gforth-0.2 “tick-cold”

OS コマンドライン引数を解釈(interpret)する直前に何かするためのフック( defer されたワード)。 あなたが実行したい幾つかの初期化を行うためにあります。 通常、 あなたが実行したいいくつかの初期化も行います。

bootmessage ( ) gforth-0.4 “bootmessage”

OS コマンドライン引数を解釈(interpret)した直後のフック(deferされたワード)。 通常は Gforth 起動メッセージを出力します(訳注: つまり、 あなたが何かしらワードをセットすると起動メッセージは出力されなくなります)。

( ’quit :原文未記述)

process-option ( addr u – true / addr u false  ) gforth-0.7 “process-option”

オプション addr u を処理し、 オプションが処理された場合は true を返します。 未処理のオプションは required を通じてファイルとしてロードされます。


15 Engine

Gforth を使用したプログラミングにはこの章を読む必要はありません。 あなたが Gforth のソース・コード内を探索するのに役立つかもしれません。

このセクションのアイデアは次の論文にも掲載されています: Bernd Paysan, ANS fig/GNU/??? Forth (in German), Forth-Tagung ’93; M. Anton Ertl, A Portable Forth Engine, EuroForth ’93; M. Anton Ertl, Threaded code variations and optimizations (extended version), Forth-Tagung ’02.


15.1 Portability

Gforth プロジェクトの重要な目標の一つは、 様々なPCで使えることです。 fig-Forth と、 程度は低いですが F83 は、 当時人気のあったいくつかのプロセッサ向けにエンジンをアセンブリ言語で手動でコーディングすることで、 この目標を達成しました。 このアプローチは非常に労働集約的であり、 コンピュータ・アーキテクチャーの進歩につれ短命に終わってしまいます。

たとえば Mitch Bradley (cforth)や、 Mikael Patel (TILE)や、 Dirk Zoller (pfe) など、 C 言語でコーディングすることでこの問題を回避したモノもあります。 UNIX マシンのアーキテクチャーは多種多様であるため、 このアプローチは UNIX ベースの Forth で特に人気があります。 残念ながら、 C 言語での実装は、 効率の目標や、 従来の手法の使用とうまく調和しません。 間接または直接のスレッド化は C 言語では表現できず、 C 言語で利用できる最速の手法であるスイッチ・スレッディングは大幅に遅くなります。 C 言語 のもう 1 つの問題は、 2倍長整数の演算を表現するのが非常に面倒なことです。

幸いなことに、 これらの制限のない移植可能な言語が存在します。 それは、GNU C 言語コンパイラーによって処理される C 言語のバージョンである GNU Cです(see Extensions to the C Language Family in GNU C Manual)。 値としてのラベル機能(see Labels as Values in GNU C Manual)により、 直接および間接的なスレッド化が可能になり、 その long long 型(see Double-Word Integers in GNU C Manual)は、 多くのシステムで Forth の 2倍長整数に対応します。 GNU C は、 すべての重要な(および多くの重要でない) UNIX マシン や VMS や MS-DOS を実行する 80386 や Amiga や Atari ST で無料で利用できるため、 GNU C で書かれた Forth はこれらすべてのマシンで実行できます。

ポータブルな言語で記述すると、 アセンブリよりも遅いコードが生成されるという評判があります。 Forth エンジンでは、 コンパイラーによって生成されたコードを繰り返し確認し、 ソース・コードを適切に変更することで、 コンパイラーによって引き起こされる非効率性のほとんどを排除しています。

しかしながら、 プログラマはレジスター割り当てに移植可能な影響を及ぼすことができないため、 レジスターが不足しているマシンでは非効率が発生します。 一部のマシンでは速度を向上させるために、 明示的なレジスター宣言を使用しています(see Variables in Specified Registers in GNU C Manual)。 これらは、 構成フラグ --enable-force-reg (gcc スイッチ -DFORCE_REG) を使用してオンにします。 残念ながら、 この機能はマシンだけでなくコンパイラーのバージョンにも依存します。 一部のマシンでは、 特定の明示的なレジスター宣言が使用されると、 一部のコンパイラー・バージョンでは不正なコードを生成します。 したがって、 デフォルトでは -DFORCE_REG は使用されません。


15.2 Threading

GNU C の「ラベルとしての値」の拡張機能(gcc-2.0 以降で有効;see Labels as Values in GNU C Manual)により、 &&label と書くことにより、 label のアドレスを取得できるようになります。 このアドレスは、 goto *address のようなステートメントで使用できます。 つまり、 goto *&&xgoto x と同じです。

この機能を使用すると、間接スレッド化された NEXT は以下のようになります:

cfa = *ip++;
ca = *cfa;
goto *ca;

上記の言葉に馴染みのない人のために説明すると、 ip は Forth 命令ポインタです。 cfa (コード・フィールド・アドレス) は標準 Forth の実行トークンに対応するもので次に実行されるワードのコード・フィールドを指します。 そこからフェッチされた ca (コード・アドレス)は、 プリミティブやコロン定義ハンドラー(docol)などの実行可能コードを指します。

直接スレッド化はさらに簡単です:

ca = *ip++;
goto *ca;

もちろん、 我々は、 NEXTNEXT1 (cfa を取得した後の NEXT の部分) というマクロに、 全体をきちんとパッケージ化しています。


15.2.1 Scheduling

スケジューリングについては少々複雑です。 パイプライン・プロセッサとスーパースカラ・プロセッサ、 つまり RISC と一部の最新の CISC マシンは、 ある命令の結果が出るのを待つ間に別の命令を処理できます。 通常、 コンパイラーは、 これらの遅延スロットを有効に使用できるように命令を並べ替え(スケジューリング)します。 ただし、 我々の最初の挑戦では、 コンパイラーはプリミティブのスケジューリングをうまく機能させられませんでした。 たとえば、 + は以下のように実装されます

n=sp[0]+sp[1];
sp++;
sp[0]=n;
NEXT;

NEXT は厳密に他のコードの後に配置されます。 つまり、 スケジューリングはまずありえません。 ちょっと考えると問題が明らかになってきます: コンパイラーは、 spip が異なるアドレスを指していることを認識できないため(そして、 たとえ認識が可能だったとしても、 私たちが使用した gcc のバージョンはそれを知りません。)、 ストア上の cfa の負荷を TOS に移動できませんでした。 実際、 スタックのTOSまたはTOS付近に対するコードが実行された場合、 これらのポインターは同一になる可能性があります。 速度を重視して、 私達は、 この、 おそらく未使用の「認識機能」を禁止した上でコンパイラーのスケジューリングを支援します(NEXT を幾つかの部分、 すなわち、 NEXT_P0NEXT_P1NEXT_P2 に分割します): そうすると、 + は以下のようになります:

NEXT_P0;
n=sp[0]+sp[1];
sp++;
NEXT_P1;
sp[0]=n;
NEXT_P2;

さまざまな操作を NEXT の部分間でいくつかの方法で分散するさまざまな仕組みがあります。 一般に、 異なる仕組みは異なるプロセッサ上で最高のパフォーマンスを発揮します。 私たちは、 ほとんどのアーキテクチャーに対して、 そのアーキテクチャーのほとんどのプロセッサで良好に動作する仕組みを使用しています。 将来的には、 ベンチマークを実施し、 インストール時に仕組みを選択するように切り替える可能性があります。


15.2.2 Direct or Indirect Threaded?

スレッド化された forth コードは、 プリミティブ(+ のような単純なマシン・コード・ルーチン)と非プリミティブ(コロン定義、 変数、 定数など)への参照で構成されます。 非プリミティブの特定の種類(変数など)では、 コード・ルーチン(dovar など) は 1 つだけですが、 各変数は各変数それぞれのデータへの個別の参照が必要です。

伝統的に、 Forth は間接スレッド化コードとして実装されてきました。 これにより、 非プリミティブの参照は 1 つのセルのみの使用で済ませられるためです(基本的に、 データをポイントし、 ポイントした先でコード・アドレスを見つけます)。

しかしながら Gforth (0.6.0 以降) のスレッド化コードは、 非プリミティブに 2 つのセルを使用します。 1 つはコード・アドレス用で、 もう 1 つはデータ・アドレス用です。 データ・ポインターは、 コード・アドレスで表される仮想マシン命令の直接の引数です。 すべてのコード・アドレスが単純なプリミティブを指しているため、 これを「プリミティブ指向」スレッド化コードと呼びます。 たとえば、 変数の場合、 コード・アドレスは lit 用です(99 などの整数リテラルにも使用されます)。

プリミティブ指向スレッド化コードにより、 ディスパッチ手法として(より高速な)直接スレッド化を完全に移植可能な状態で使用できます(0.6.0 より前の Gforth の直接スレッド化コードにはアーキテクチャー固有のコードが必要でした)。 また、386 実装で直接スレッド・コードによる I キャッシュの一貫性に関連するパフォーマンスの問題も解消され、 追加の最適化が可能になります。

ただし、 問題があります。 executext パラメーターは 1 つのセルしか占有できないため、 「コードとデータ・アドレス」を持つ非プリミティブをどのように渡せばよいのでしょうか? これに対する私達の答えは、 execute や単一セル xt を使用する他のワードに間接スレッド化ディスパッチを使用することです。 したがって、 コロン定義内の通常のスレッド化コードは直接スレッドを使用し、 データ・スタック上の xt にディスパッチする execute や同様のワードは間接スレッド化コードを使用します。 これを 「ハイブリッド 直接/間接 スレッド化コード」と呼びます。

gforth エンジンと gforth-fast エンジンは、 ハイブリッド 直接/間接 スレッド化コードを使用します。 つまり、 これらのエンジンでは , を使用して xt をコンパイルできないことを意味します。 代わりに、 compile, を使用する必要があります。

, を使用して xt をコンパイルする場合は、 gforth-itc エンジンを使用します。 このエンジンは、 単純な古い間接スレッド化コードを使用します。 しかし、 それでもプリミティブ指向のスタイルでコンパイルされるため、 , の代わりに compile, を使用することはできません(たとえば、 ] word1 word2 ... [ で xt 達のテーブルを生成する場合)。 それを行いたい場合は、 gforth-itc を使用して ' , is compile, を実行する必要があります。 あなたのプログラムでは、 threading-method を使用して、 ハイブリッド 直接/間接 スレッド化エンジンで実行されているか、 純粋な間接スレッド化エンジンで実行されているかを確認できます。


15.2.3 Dynamic Superinstructions

gforth エンジンと gforth-fast エンジンは、 別の最適化、 つまり、 レプリケーションを伴う動的スーパー命令(Dynamic superinstructions with replication)を使用します。 例として、 以下のコロン定義について考えてみましょう:

: squared ( n1 -- n2 )
  dup * ;

Gforth はこれを以下のスレッド化コード・シーケンスにコンパイルします

dup
*
;s

simple-see (see Examining compiled code)を使用して、 コロン定義のスレッド化コードを確認します。

通常の直接スレッド化コードでは、 上記のプリミティブごとに 1 つのセルを占めるコード・アドレスがあります。 各コード・アドレスはマシン・コード・ルーチンを指し、 インタープリターはプリミティブを実行するためにこのマシン・コードにジャンプします。 上記 3 つのプリミティブのルーチンは以下のとおりです(386 の gforth-fast の場合):

Code dup  
( $804B950 )  add     esi , # -4  \ $83 $C6 $FC 
( $804B953 )  add     ebx , # 4  \ $83 $C3 $4 
( $804B956 )  mov     dword ptr 4 [esi] , ecx  \ $89 $4E $4 
( $804B959 )  jmp     dword ptr FC [ebx]  \ $FF $63 $FC 
end-code
Code *  
( $804ACC4 )  mov     eax , dword ptr 4 [esi]  \ $8B $46 $4 
( $804ACC7 )  add     esi , # 4  \ $83 $C6 $4 
( $804ACCA )  add     ebx , # 4  \ $83 $C3 $4 
( $804ACCD )  imul    ecx , eax  \ $F $AF $C8 
( $804ACD0 )  jmp     dword ptr FC [ebx]  \ $FF $63 $FC 
end-code
Code ;s  
( $804A693 )  mov     eax , dword ptr [edi]  \ $8B $7 
( $804A695 )  add     edi , # 4  \ $83 $C7 $4 
( $804A698 )  lea     ebx , dword ptr 4 [eax]  \ $8D $58 $4 
( $804A69B )  jmp     dword ptr FC [ebx]  \ $FF $63 $FC 
end-code

動的なスーパー命令とレプリケーションを使用すると、 コンパイラーはスレッド化されたコードを配置しないだけでなく、 コード断片のコピーも行い、 通常は各コード断片最後で行うジャンプを行いません。

( $4057D27D )  add     esi , # -4  \ $83 $C6 $FC 
( $4057D280 )  add     ebx , # 4  \ $83 $C3 $4 
( $4057D283 )  mov     dword ptr 4 [esi] , ecx  \ $89 $4E $4 
( $4057D286 )  mov     eax , dword ptr 4 [esi]  \ $8B $46 $4 
( $4057D289 )  add     esi , # 4  \ $83 $C6 $4 
( $4057D28C )  add     ebx , # 4  \ $83 $C3 $4 
( $4057D28F )  imul    ecx , eax  \ $F $AF $C8 
( $4057D292 )  mov     eax , dword ptr [edi]  \ $8B $7 
( $4057D294 )  add     edi , # 4  \ $83 $C7 $4 
( $4057D297 )  lea     ebx , dword ptr 4 [eax]  \ $8D $58 $4 
( $4057D29A )  jmp     dword ptr FC [ebx]  \ $FF $63 $FC 

スレッド化コードの制御フローに変更が発生した場合(;s など)にのみジャンプが追加されます。 この最適化により、 これらのジャンプの多くが排除され、 残りの部分がより予測可能になります。 高速化はプロセッサとアプリケーションによって異なります。 Athlon および Pentium III では、 この最適化により通常 2 倍の速度向上が得られます。

直接スレッド化コード内のコード・アドレスは、 コピーされたマシン・コード内の適切なポイントを指すように設定されます。 この例では以下のようになります:

primitive  code address
   dup       $4057D27D
   *         $4057D286
   ;s        $4057D292

したがって、 このコード部分の任意の場所にスレッド化コードからのジャンプ先が存在する可能性があります。 これにより、 逆コンパイルもかなり簡素化されます。

See-code (see Examining compiled code)は、 動的スーパー命令(dynamic superinstructions)のネイティブ・コードと混在するスレッド化コードを示します。 最近では、 動的に生成されたネイティブ・コードに追加の最適化が適用されているため、 特定の AMD64 インストール上の gforth-fast での see-code squared の出力は以下のようになります:

$7FB689C678C8 dup    1->2 
7FB68990C1B2:   mov     r15,r8
$7FB689C678D0 *    2->1 
7FB68990C1B5:   imul    r8,r15
$7FB689C678D8 ;s    1->1 
7FB68990C1B9:   mov     rbx,[r14]
7FB68990C1BC:   add     r14,$08
7FB68990C1C0:   mov     rax,[rbx]
7FB68990C1C3:   jmp     eax

--no-dynamic を使用してこの最適化を無効にできます。 --no-super を使用すると、 ジャンプを排除せずにコピーを使用できます(つまり、 動的レプリケーション、 しかしスーパー命令は使用しません)。 これは分岐予測のみに利点をもたらします。 パフォーマンスへの影響は CPU によって異なります。 Athlon および Pentium III では、 レプリケーションを伴う動的スーパー命令(dynamic superinstructions with replication)よりも高速化が若干損なわれます。

これらのオプションの用途の 1 つは、 スレッド化されたコードにパッチを適用する場合です。 スーパー命令を使用すると、 ディスパッチ・ジャンプの多くが削除されるため、 パッチを当てても効果がなくなることがよくあります。 これらのオプションは、 すべてのディスパッチ・ジャンプを保持します。

一部のマシンでは動的スーパー命令(dynamic superinstructions)は安全ではないため、 デフォルトで無効になっています。 しかしながら、 あなたが冒険してみたい場合は、 --dynamic を使用して有効にすることができます。


15.2.4 DOES>

Forth エンジンの最も複雑な部分の 1 つは dodoes です。つまり、 CREATE...DOES> ペアで定義されたすべてのワードによって実行されるコードの塊(chunk)です。 実際のところプリミティブ指向のコードでは、 CREATE...DOES> ペアで定義されたすべてのワードによって実行されるコードの塊(chunk)は、 ワードの xt が execute された場合にのみ必要です。 ここでの主な問題は、 実行される Forth コード、 つまり DOES> の後のコード(the DOES>-code) をどのようにして見つけるかということです。 この解決策は 2 つあります:

fig-Forth では、 コード・フィールドは dodoes を直接指しており、 DOES> コード・アドレスはコード・アドレスの後のセル(つまり CFA cell+)に格納されます。 この解決策は、 Forth-79 以降のすべての標準では違法であるように見えるかもしれません。 fig-Forth ではこのアドレスがボディ内にあるためです(標準では違法です)。 しかしながら、 すべてのワードに対してコード・フィールドを大きくすることで、 この解決策は再び有効になります。 私たちはこのアプローチを採用しています。 ほとんどの場合、 セルを未使用のままにしておくのはちょっと無駄ですが、 私たちが対象としているマシンでは、 これはほとんど問題になりません。


15.3 Primitives


15.3.1 Automatic Generation

プリミティブは移植可能な言語(portable language)で実装されているため、 最早プリミティブの数を最小限に抑える必要はありません。 逆に、 多くのプリミティブを持つことには速度という利点があります。 プリミティブでのエラーの数を減らし、 プリミティブのプログラミングを容易にするために、 プリミティブ・ジェネレーター (prims2x.fs 別名 Vmgen see Introduction in Vmgen)というツールを提供しており、 それは、 スタック効果の表記からプリミティブの C言語コードの大部分(場合によってはすべて)を自動的に生成します。 プリミティブのソースの形式は以下のとおりです:

Forth-name  ( stack-effect )        category    [pronounce]
[""glossary entry""]
C code
[:
Forth code]

角括弧(brackets)内の項目はオプションです。 カテゴリ(category)と発音(pronounce)と用語集(glossary)のフィールドはドキュメントを生成するためにあり、 Forth コード(Forth code)は GNU C を持たないマシンでの手動実装のためにあります。 たとえば、 プリミティブ + のソースは以下のとおりです:

+    ( n1 n2 -- n )   core    plus
n = n1+n2;

これは仕様のように見えますが、 実際には n = n1+n2 は C言語のコードです。 私たちのプリミティブ生成ツールは、 スタック効果の表記から多くの情報を抽出します38: それらは、 スタックからポップされた項目とスタックにプッシュされた項目の数、 その型、 および C言語のコード内で参照される名前 です。 その次に、 各プリミティブの C言語コードの prelude と postlude を生成します。 + の最終的な C言語コードは以下のようになります:

I_plus: /* + ( n1 n2 -- n ) */  /* label, stack effect */
/*  */                          /* documentation */
NAME("+")                       /* debugging output (with -DDEBUG) */
{
DEF_CA                          /* definition of variable ca (indirect threading) */
Cell n1;                        /* definitions of variables */
Cell n2;
Cell n;
NEXT_P0;                        /* NEXT part 0 */
n1 = (Cell) sp[1];              /* input */
n2 = (Cell) TOS;
sp += 1;                        /* stack adjustment */
{
n = n1+n2;                      /* C code taken from the source */
}
NEXT_P1;                        /* NEXT part 1 */
TOS = (Cell)n;                  /* output */
NEXT_P2;                        /* NEXT part 2 */
}

これは長くて非効率に見えますが、 GNU C コンパイラーは非常に適切に最適化して、 例えば R3000 や HP RISC マシンなどで + に最適なコードを生成します。 n 達を定義してもコードは生成されませんし、 また、 それら中間ストレージとして使用してもコストはかかりません。

この例では示されていない他の最適化もあります。 単純な変数間の代入は通常はコストがかかりません(コピー伝播)。 スタック項目の 1 つがプリミティブによって使用されていない場合(drop など)、 コンパイラーはスタックのその部分の負荷を削除します(デッド・コードの削除)。 一方、 コンパイラーが実行しない処理もいくつかあり、 そのため、 それらは prims2x.fs によって実行されます。 コンパイラは、 スタック項目を元の場所に格納するコードを最適化しません(例: over).

プリミティブのプログラミングは通常は簡単ですが、 最も顕著な ?dup だけでなく、 NEXT に(常に)フローが流れていかないワードについても、 プログラマがジェネレーターのアクションを考慮する必要があるケースがいくつかあります。

詳細について(原文未記述)


15.3.2 TOS Optimization

Forth エンジンなどのスタック・マシン・エミュレータの重要な最適化は、 1 つ以上の最上位側スタック項目をレジスターに保持することです。 ワードにスタック効果 ( in1...inx -- out1...outy ) がある場合、 上位の n 項目をレジスターに保持します:

  • x>=n かつ y>=n の場合、 スタックへのロードとスタックへのストアがより少なくなるため、 n 項目をレジスターに保持する方が n-1 項目をレジスターに保持するよりも優れています。
  • x<>y かつ x<n かつ y<n の場合、 レジスター間の追加の移動により、 n 項目をレジスターに保持することは n-1 項目を保持するよりも遅くなります。

特に、 レジスターの数が十分であれば、 1 つの項目をレジスターに保持しておくことは決して不利にはなりません。 ?branch や定数や変数やリテラルや i などの頻繁に使用されるワードにとって、 2 つの項目をレジスターに保持することは不利になります。 したがって、 ジェネレーターは、 レジスターに 0 個または 1 つの項目を保持するコードのみを生成します。 生成された C言語のコードは両方のケースをカバーします。 これらの選択肢の選択は、 C言語のコンパイル時にスイッチ -DUSE_TOS を使用して行われます。 + の C言語コードの TOS は、 1 項目の場合は単なる変数名ですが、 それ以外の場合は sp[0] に展開されるマクロです。 注意: GNU C コンパイラーは TOS のような単純な変数をレジスターに保持しようと試み、 十分な数のレジスターがあれば、 それは通常は成功することに注意してください。

プリミティブ・ジェネレーターは、 浮動小数点数スタックの TOS 最適化も実行します(-DUSE_FTOS)。 浮動小数点数演算の場合、 この最適化の利点はさらに大きくなります。 浮動小数点数演算は、 ほとんどのプロセッサで非常に時間がかかりますが、 その結果が利用されない間は他の演算と並行して実行できます。 FP-TOS がレジスターに保持されている場合、 他の演算との並行しての実行が機能します。 スタック上、 つまりメモリー内に保持されている場合、 メモリーへのストアは浮動小数点数演算の結果を待つ必要があり、 プリミティブの実行時間が大幅に長くなります。

TOS の最適化により、 プリミティブの自動生成がちょっぴり複雑になります。 sp[0] のすべての出現を TOS に置き換えるだけでは十分ではありません。 考慮すべき特殊なケースがいくつかあります:

  • dup ( w -- w w ) で、 TOS 最適化がオンになっている場合、 ジェネレーターはスタック上の項目の元の場所へのストアを削除してはなりません。
  • (-- out1...outy) の形式のスタック効果を持つプリミティブは、 開始時に TOS をスタックに保存する必要があります。 同様に、 スタック効果 in1...inx --) を持つプリミティブは、 最後にスタックから TOS をロードする必要があります。 ただし、 null スタック効果 -- の場合、 ストアやロードは生成されません。

15.3.3 Produced code

コンパイラーとフラグ設定を使用してマシン上のプリミティブに対してどのようなアセンブリ・コードが生成されるかを確認するには、 make engine.s と入力し、結果のファイル engine.s を確認します。 あるいは、 一部のアーキテクチャでは see を使用してプリミティブのコードを逆アセンブルすることもできます。


15.4 Performance

RISC では、Gforth エンジンは限りなく最適な状態に近くなります。 逆に、 通常、 RISC以外では、 顕著に高速なスレッド化コード・エンジンを作成することは不可能です。

386 アーキテクチャー・プロセッサのようにレジスターの数が不足しているマシンでは、 明示的なレジスター宣言があっても gcc が人間ほどにはレジスターを利用しないため、 人間による改善が可能です。 たとえば、Bernd Beuster は Forth システムの断片をアセンブリ言語で作成し、 486 用に手動で調整しました。 このシステムは、 486DX2/66 での Sieve ベンチマークでは、 -DFORCE_REG を使用して gcc-2.6.3 でコンパイルした Gforth よりも 1.19 倍高速です。 この状況は gcc-2.95 と gforth-0.4.9 で改善されました。 これで、 最も重要な仮想マシンのレジスターが実際のレジスターに収まるようになり(更に、 TOS 最適化を使用する余裕さえあります)、 結果として以前の結果よりも 1.14 高速化されました。 また、動的スーパー命令によりさらに高速化が実現されます(ただし、 486 では約 1.2 倍にすぎません)。

アセンブリ言語で実装する潜在的な利点は、 Forth システムでは必ずしも現実のモノにはなりません。 Gforth をアセンブリ言語で書かれたシステムと比較しました: Gforth-0.5.9 (直接スレッド化、 gcc-2.95.1-DFORCE_REG でコンパイル)、 Gforth-0.5.9 の Win32Forth 版 1.2093 (新しいバージョンははるかに高速であると報告されています))、 LMI の NT Forth (1994年5月のベータ版)、 Eforth(スレッド化されたコードののぞき穴(peephole; 別名 針の穴(pinhole))最適化の有無)。 また、Gforth を C言語で書かれた 3 つのシステムと比較しました: PFE-0.9.14 (Linux のデフォルト構成で gcc-2.6.3 でコンパイル: -O2 -fomit-frame-pointer -DUSE_REGS -DUNROLL_NEXT)、 ThisForth Beta (gcc-2.6.3 -O3 -fomit-frame-pointer でコンパイル; ThisForth はスレッド化されたコードののぞき穴最適化を採用)、 TILE (make opt でコンパイル)。 Linux 上の 486DX2/66 で、 Gforth と PFE と ThisForth と TILE のベンチマークを実行しました。 Kenneth O’Heskin は、 Windows NT で同様のメモリー・パフォーマンスを備えた 486DX2/66 上の Win32Forth と NT Forth の結果を親切に提供してくれました。 Marcel Hendrix は Eforth を Linux に移植し、 ベンチマークを実行できるように拡張し、 覗き穴・オプティマイザーを追加してベンチマークを実行し、 結果を報告しました。

私たちは 4 つの小さなベンチマークを使用しました。 ユビキタスのふるい。 バブルソートと行列乗算はスタンフォード整数ベンチマーク由来で、 Martin Fraeman によって Forth に変換されました。 TILE Forth パッケージに含まれているバージョンを使用しましたが、 データ・セット・サイズが大きくなりました。 そして、 呼び出しパフォーマンス(calling performance)のベンチマークのための再帰的フィボナッチ数計算です。 以下の表は、 ベンチマークにかかる時間を Gforth にかかった時間で換算したものです(つまり、 Gforth が他のシステムに対して達成した高速化係数を示しています)。

relative       Win32-    NT       eforth       This-      
time     Gforth Forth Forth eforth  +opt   PFE Forth  TILE
sieve      1.00  2.16  1.78   2.16  1.32  2.46  4.96 13.37
bubble     1.00  1.93  2.07   2.18  1.29  2.21        5.70
matmul     1.00  1.92  1.76   1.90  0.96  2.06        5.32
fib        1.00  2.32  2.03   1.86  1.31  2.64  4.55  6.54

あなたは、 アセンブリ言語で書かれたシステムと比較したとき、 Gforth の優れたパフォーマンスに驚かれるかもしれません。 これら他のシステムのパフォーマンスが期待外れである重要な理由の 1 つは、 おそらく、 それらが 486 用に最適化して書かれていないことです(たとえば、lods 命令を使用している)。 さらに、 Win32Forth は、 Forth イメージを再配置するために快適ではありますがコストのかかる方法を使用します: これは cforth と同様に、 実行時に実際のアドレスが計算され、 NEXT 毎に 2 つのアドレス計算が行われます(see Image File Background)。

PFE や ThisForth や TILE に対する Gforth の高速化は、 前者達のシステムが標準 C に対して自ら課した制限によって簡単に説明できます: これにより、 効率的なスレッド化が不可能になります(ただし、 観測された PFE の実装では GNU C 拡張機能が使用されています see Defining Global Register Variables in GNU C Manual)。 さらに言えば、 現在の C コンパイラーは、 ThisForth や TILE ソースの他の側面を最適化するのに苦労しています。

386 アーキテクチャー・プロセッサ上の Gforth のパフォーマンスは、 使用する gcc のバージョンによって大きく異なります。 たとえば、gcc-2.5.8 は、 それ自身では仮想マシン・レジスターを実マシン・レジスターに割り当てることに失敗し、 かつ、 明示的なレジスター宣言では正しく動作しないため、 (Sieve ベンチマークを実行している 486DX2/66 では、)上記で測定したエンジンよりも大幅に遅いエンジンが出来上がります。

注意: ここで紹介したリリース以降、 Win32Forth のリリースがいくつかあるため、 上記の結果は現在の Win32Forth のパフォーマンスを予測する値がほとんどない可能性があることに注意してください(i486DX2/66 での現在のリリースの結果報告をお待ちしています)。

Translating Forth to Efficient C by M. Anton Ertl and Martin Maierhofer (presented at EuroForth ’95) では、 Gforth の間接スレッド化バージョンが、 Win32Forth や NT Forth や PFE や ThisForth や、 いくつかのネイティブ・コード・システムと比較されます。 そのバージョンの Gforth は、 486 ではここで使用されているバージョンよりも遅くなります。 これらの測定値の新しいバージョンは https://www.complang.tuwien.ac.at/forth/performance.html で見つけることができます。 あなたは Benchres でさまざまなマシン上の Gforth の数値を見つけることができます。


16 Cross Compiler

クロス・コンパイラーは、 Forth カーネルをブート・ストラップするために使用されます。 Gforth は、 外部インタープリターやコンパイラなどの重要な部分を含め、 ほとんどが Forth で書かれているため、 開始するにはコンパイル済みの Forth コードが必要です。 クロス・コンパイラを使用すると、 他のアーキテクチャー用の新しいイメージを作成でき、 別の Forth システムで実行することもできます。


16.1 Using the Cross Compiler

クロス・コンパイラは、 Forth そっくりの言語を使用しますが、 Forth ではありません。 主な違いは、 Forth コードは定義後に実行できるのに対し、 cross によってコンパイルされたコードは通常は実行できないことです。 これは、 コンパイルしているコードは通常、 コンパイルしているコンピュータとは異なるコンピュータ用であるためです。

Makefile はすでにセットアップされており、 簡単な make コマンドで新しいアーキテクチャ用のカーネルを作成できるようになります。 GCC でコンパイルされた仮想マシンを使用する汎用カーネルは、 make を使用した通常のビルド・プロセスで作成されます。 たとえば、 8086 プロセッサ用の埋め込み Gforth 実行可能ファイル(embedded Gforth executable)(DOS マシン上で実行)を作成するには、 以下のようにタイプします

make kernl-8086.fi

これにより、 arch/8086 ディレクトリのマシンの説明(machine description)が使用されて、 新しいカーネルが作成されます。 マシン・ファイルは以下のようになります:

\ Parameter for target systems                         06oct92py

    4 Constant cell             \ cell size in bytes
    2 Constant cell<<           \ cell shift to bytes
    5 Constant cell>bit         \ cell shift to bits
    8 Constant bits/char        \ bits per character
    8 Constant bits/byte        \ bits per byte [default: 8]
    8 Constant float            \ bytes per float
    8 Constant /maxalign        \ maximum alignment in bytes
false Constant bigendian        \ byte order
( true=big, false=little )

include machpc.fs               \ feature list

この部分はクロス・コンパイラにとって必須であり、 機能リスト(feature list)は、 ターゲットがこれらの機能をサポートしているかどうかに応じて、 いくつかの機能を条件付きでコンパイルしたりしなかったりするためにカーネルによって使用される。

あなた独自のプリミティブを定義する場合、 またはアセンブラーを使用する場合、 またはブート・プロセスを機能させるために特別な非標準の準備が必要な場合は、 オプションの機能(feature)がいくつかあります。 asm-include にはアセンブラーが含まれ、 prims-include にはプリミティブが含まれ、 >boot はブートの準備をします。

: asm-include    ." Include assembler" cr
  s" arch/8086/asm.fs" included ;

: prims-include  ." Include primitives" cr
  s" arch/8086/prim.fs" included ;

: >boot          ." Prepare booting" cr
  s" ' boot >body into-forth 1+ !" evaluate ;

これらのワードは、 ファイル kernel/main.fs をクロス・コンパイルする時に一種のマクロとして使用されます。 これらのマクロを使用する代わりに、 新しいカーネル・プロジェクト・ファイルを作成することも可能ですが、 より複雑になります。

kernel/main.fs はスタック上にマシン記述ファイル名(machine description file name)を期待します。 クロス・コンパイラ自体(cross.fs)は、 mach-file がカウンタ付き文字列をスタックに残すか、 または machine-file がファイル名のアドレスとカウントのペアをスタックに残すかのいずれかを想定します。

機能リスト(feature list)は通常​​ SetValue を使用して制御されますが、 複数のプロジェクトで使用される汎用ファイルは代わりに DefaultValue を使用できます。 どちらのワードも、 値が定義されていない場合は Value のように動作しますが、 値が定義されている場合は SetValueto のように動作し、 値が定義されている場合は DefaultValue は何も設定しません。

\ generic mach file for pc gforth                       03sep97jaw

true DefaultValue NIL  \ relocating

>ENVIRON

true DefaultValue file          \ controls the presence of the
                                \ file access wordset
true DefaultValue OS            \ flag to indicate a operating system

true DefaultValue prims         \ true: primitives are c-code

true DefaultValue floating      \ floating point wordset is present

true DefaultValue glocals       \ gforth locals are present
                                \ will be loaded
true DefaultValue dcomps        \ double number comparisons

true DefaultValue hash          \ hashing primitives are loaded/present

true DefaultValue xconds        \ used together with glocals,
                                \ special conditionals supporting gforths'
                                \ local variables
true DefaultValue header        \ save a header information

true DefaultValue backtrace     \ enables backtrace code

false DefaultValue ec
false DefaultValue crlf

cell 2 = [IF] &32 [ELSE] &256 [THEN] KB DefaultValue kernel-size

&16 KB          DefaultValue stack-size
&15 KB &512 +   DefaultValue fstack-size
&15 KB          DefaultValue rstack-size
&14 KB &512 +   DefaultValue lstack-size

16.2 How the Cross Compiler Works(原文未記述)


17 MINOS2, a GUI library


17.1 MINOS2 object framework

MINOS2 は、 mini-oof2.fs のオブジェクト・モデルで書かれた GUI ライブラリです。 これには 2 つの主要なクラス階層があります:

actor ( – class  ) minos2 “actor”

コンポーネントに結び付けられたされたアクションのクラス。

widget ( – class  ) minos2 “widget”

ビジュアル・コンポーネントのクラス


17.1.1 actor methods:

caller-w ( – optr  ) minos2 “caller-w”

アクターを埋め込むウィジェットへのポインター

active-w ( – optr  ) minos2 “active-w”

アクターを埋め込むアクティブなサブウィジェットへのポインター

act-name$ ( – addr u  ) minos2 “act-name-string”

デバッグ支援: アクターの名前

clicked ( rx ry bmask n –  ) minos2 “clicked”

処理されたクリック(達)

scrolled ( axis dir –  ) minos2 “scrolled”

スクロール処理

touchdown ( $rxy*n bmask –  ) minos2 “touchdown”

生のクリック・ダウン

touchup ( $rxy*n bmask –  ) minos2 “touchup”

生のクリック・アップ

ukeyed ( addr u –  ) minos2 “ukeyed”

キーイベント。 印刷可能な Unicode 文字の文字列

ekeyed ( ekey –  ) minos2 “ekeyed”

キーイベント。 印刷不可能なキー

?inside ( rx ry – act / 0  ) minos2 “query-inside”

座標がウィジェット内にあるかどうかをチェック。

focus ( ) minos2 “focus”

ウィジェットにフォーカスする

defocus ( ) minos2 “defocus”

ウィジェットをフォーカスから外す

entered ( ) minos2 “entered”

カーソルがウィジェット領域に入ると反応します

left ( ) minos2 “left”

カーソルがウィジェット領域から離れると反応します

show ( ) minos2 “show”

ウィジェットが表示されます

hide ( ) minos2 “hide”

ウィジェットを非表示にします

get ( – something  ) minos2 “get”

ウィジェットの背後にある値のゲッター

set ( something –  ) minos2 “set”

ウィジェットの背後にある値のセッター

show-you ( ) minos2 “show-you”

ウィジェットを表示させる


17.1.2 widget methods:

parent-w ( – optr  ) minos2 “parent-w”

親ウィジェットへのポインター

act ( – optr  ) minos2 “act”

アクターへのポインター

name$ ( – addr u  ) minos2 “name-string”

デバッグと検索用のウィジェット名

x ( – r  ) minos2 “x”

ウィジェット x 座標

y ( – r  ) minos2 “y”

ウィジェットの y 座標

w ( – r  ) minos2 “w”

ウィジェットの幅

h ( – r  ) minos2 “h”

ベースラインより上のウィジェットの高さ

d ( – r  ) minos2 “d”

ベースラインより下のウィジェットの深さ

gap ( – r  ) minos2 “gap”

行間の隙間

baseline ( – r  ) minos2 “baseline”

行ごとの最小スキップ

kerning ( – r  ) minos2 “kerning”

カーニングを追加

raise ( – r  ) minos2 “raise”

ボックスを 上げる/下げる

border ( – r  ) minos2 “border”

周囲の境界線、全方向

borderv ( – r  ) minos2 “borderv”

垂直境界線のオフセット

bordert ( – r  ) minos2 “bordert”

上枠のオフセット

borderl ( – r  ) minos2 “borderl”

左境界オフセット

w-color ( – r  ) minos2 “w-color”

ウィジェットのカラー・インデックス(カラーマップ内)(存在する場合)

draw-init ( ) minos2 “draw-init”

draw 初期化

draw ( ) minos2 “draw”

ウィジェットを描く

split ( firstflag rstart1 rx – o rstart2  ) minos2 “split”

ウィジェットを段落を植字するための部分(parts for typesetting paragraphs)に分割します

lastfit ( ) minos2 “lastfit”

最後のウィジェット要素をボックスに収めます(fit)

hglue ( – rtyp rsub radd  ) minos2 “hglue”

水平グルーを計算

dglue ( – rtyp rsub radd  ) minos2 “dglue”

ベースラインの下の垂直グルーを計算

vglue ( – rtyp rsub radd  ) minos2 “vglue”

ベースラインより上の垂直グルーを計算

hglue@ ( – rtyp rsub radd  ) minos2 “hglue-fetch”

hglue のキャッシュされたバリエーション

dglue@ ( – rtyp rsub radd  ) minos2 “dglue-fetch”

dglue のキャッシュされたバリエーション

vglue@ ( – rtyp rsub radd  ) minos2 “vglue-fetch”

vglue のキャッシュされたバリエーション

xywh ( – rx0 ry0 rw rh  ) minos2 “xywh”

ウィジェット境界(bound)ボックス、左上隅から開始

xywhd ( – rx ry rw rh rd  ) minos2 “xywhd”

左のベースライン点から始まるウィジェット境界(bound)ボックス

!resize ( rx ry rw rh rd –  ) minos2 “store-resize”

ウィジェットのサイズ変更

!size ( ) minos2 “store-size”

ウィジェットにサイズを自己決定させます

dispose-widget ( ) minos2 “dispose-widget”

ウィジェットを削除

.widget ( ) minos2 “print-widget”

デバッグ: ウィジェットに関する情報を出力

par-split ( rw –  ) minos2 “par-split”

段落を幅 rw で分割します

resized ( ) minos2 “resized”

ウィジェットのサイズが変更されました

コンポーネントは、 段落区切りを含め、 LaTeX に似た box&glue モデルを使用して構成されます。 簡素化と移植性のため、 MINOS2 は 1 つのウィンドウのみをサポートし、 レンダリングには OpenGL を使用します。

MINOS2 はさらに、 animation クラスを使用したアニメーションをサポートします。 カラー・インデックス・テクスチャはさまざまなカラー・スキームに使用され、 隣接するスキーム間の遷移をアニメーション化することもできます。

>animate ( rdelta addr xt –  ) minos2 “to-animate”

rdelta のタイムアウトが期限切れになるまで、 スタック効果 ( addr r0..1 -- ) を指定して xt を繰り返し呼び出して、 新しいアニメーションを作成します。 最後の呼び出しは常に引数 1e を使用して行われます。

名前付きカラー・インデックスを作成し、 現在アクティブなカラー・スキームのカラー値を割り当てることができます。

color: ( rgba "name" –  ) minos2 “color:”

rgba で初期化された(おそらく共有される)カラー・インデックスを作成します。

new-color: ( rgba "name" –  ) minos2 “new-color:”

rgba で初期化された一意(unique)のカラー・インデックスを作成します。

text-color: ( rgba "name" –  ) minos2 “text-color:”

rgba で初期化された一意のテキスト・カラー・インデックスを作成します。 対応する絵文字の色は白に設定されます。

text-emoji-color: ( rgbatext rgbaemoji "name" –  ) minos2 “text-emoji-color:”

rgbatext で初期化された一意のテキスト・カラー・インデックスを作成します。 対応する絵文字の色は rgbaemoji に設定されます。

fade-color: ( rgba1 rgba2 "name" –  ) minos2 “fade-color:”

rgba1rgba2 で初期化されたテキスト・カラー・インデックスの一意のペアを作成します。 対応する絵文字の色は白に設定されます。 インデックスをあるインデックスから次のインデックスにゆっくりと移動すると、 オブジェクトは再描画時に線形補間(linear interpolation)を使用してその色を変更します。

text-emoji-fade-color: ( rgbatext1 ~2 rgbaemoji1 ~2 "name" –  ) minos2 “text-emoji-fade-color:”

rgbatext1~2 で初期化されたテキスト・カラー・インデックスの一意のペアを作成します。 対応する絵文字カラー・ペアは rgbaemoji1 から ~2 に設定されます。 インデックスをあるインデックスから次のインデックスにゆっくりと移動すると、 オブジェクトは再描画時に線形補間を使用してその色を変更します。

re-color ( rgba "name" –  ) minos2 “re-color”

現在のカラースキームの名前付きカラーインデックス "name" に値 rgba を割り当てます。

re-text-color ( rgba "name" –  ) minos2 “re-text-color”

現在のカラー・スキームの名前付きテキスト・カラー・インデックス "name" に値 rgba を割り当てます。

re-emoji-color ( rgbatext rgbaemoji "name" –  ) minos2 “re-emoji-color”

現在のカラー・スキームの名前付きテキスト・カラー・インデックスと名前付き絵文字カラー・インデックス "name" に値 rgbatextrgbaemoji を割り当てます。

re-fade-color ( rgba1 rgba2 "name" –  ) minos2 “re-fade-color”

現在のカラー・スキーム内の名前付きカラー・インデックスのペア "name" に値 rgba1 および rgba2 を割り当てます。

re-text-emoji-fade-color ( rgbatext1 ~2 rgbaemoji1 ~2 "name" –  ) minos2 “re-text-emoji-fade-color”

現在のカラー・スキーム内の名前付きカラー・インデックスのペア "name" にそれぞれ値 rgbatext1~2 を割り当てます。 rgbaemoji1~2.

多くの特定のオブジェクトに対して、 それらのオブジェクトでのみ機能する早期結び付けメソッド(early bound method)があります

  • Viewport
    vp-top ( o:vp –  ) minos2 “vp-top”
    

    ビューポートを一番上までスクロール

    vp-bottom ( o:vp –  ) minos2 “vp-bottom”
    

    ビューポートを一番下までスクロール

    vp-left ( o:vp –  ) minos2 “vp-left”
    

    ビューポートを左にスクロール

    vp-right ( o:vp –  ) minos2 “vp-right”
    

    ビューポートを右にスクロール

    vp-reslide ( o:vp –  ) minos2 “vp-reslide”
    

    スクロール後にビューポートのスライダーを調整します

    vp-needed ( xt –  ) minos2 “vp-needed”
    

    ビューポートの vp-need でニーズ(needs)を収集します


17.2 MINOS2 tutorial

チュートリアルは小さなファイルであり、 MINOS2 がちょびっとずつ示されています。 共通フレームワークの場合、 最初にファイル minos2/tutorial/tutorial.fs をロードする必要があります。 コマンドライン引数内の他のすべてのチュートリアルは、 そのファイル内に含まれます。 スクロール・ホイールまたは 前/次 のマウス・ボタンや、 ウィンドウの左端または右端をクリックすると、 読み込まれたさまざまなチュートリアル間を移動できます。

つまり、 buttons チュートリアルをロードするには以下のようにして Gforth を開始します。

gforth minos2/tutorial/tutorial.fs buttons.fs

利用可能なチュートリアル:

  • buttons.fs: クリック可能なボタン達
  • plots.fs: プロット機能
  • markdown.fs: マークダウン・ドキュメント・ビュワー
  • screenshot.fs: スクリーンショット機能

Appendix A Bugs

既知のバグは、 Gforth ディストリビューションのファイル BUGS に記載されています。

バグを見つけた場合は、 https://savannah.gnu.org/bugs/?func=addbug&group=gforth を通じてバグ・レポートを送信してください。

バグ報告の詳細なガイドについては、 How to Report Bugs in GNU C Manual を参照してください。


Appendix B Authors and Ancestors of Gforth

B.1 Authors and Contributors

Gforth プロジェクトは、 1992 年の半ば Bernd Paysan と Anton Ertl によって開始されました。 3番目の主要な著者は Jens Wilke でした。 Neal Crook はマニュアルに多大な貢献をしました。 アセンブラーと逆アセンブラーは、 Andrew McKewan と Christian Pirker と Bernd Thallner と Michal Revucky によって提供されました。 Lennart Benschop (1993 年半ばの Gforth の最初のユーザーの 1 人) と Stuart Ramsden は、 継続的なフィードバックで私たちにインスピレーションを与えてくれました。 Lennart Benshop は glosgen.fs を寄稿し、 Stuart Ramsden は C 言語ライブラリーの呼び出しの自動サポートに取り組んできました。 Paul Kleinrubatscher, Christian Pirker, Dirk Zoller, Marcel Hendrix, John Wavrik, Barrie Stott, Marc de Groot, Jorge Acerada, Bruce Hoyt, Robert Epprecht, Dennis Ruffer, David N からも有益なコメントが寄せられました。 Gforth-0.2.1 のリリース以来、 他の多くの人からも有益なコメントがありました。 みんなありがとう。 ここにあなたの名前を載せられなくてごめんなさい(でも、 メールボックスを漁ってあなたの名前を抽出するのは私のTODOリストに入っています)。

また、 Gforth は、私たちが使用したツール (GCC や CVS やautoconf など)の作成者と、 インターネットの作成者に多くの恩恵を受けています。 Gforth はインターネット上で開発されたため、 開発の最初の 4 年間、 作成者は物理的に会うことはありませんでした。

B.2 Pedigree

Gforth は、 bigFORTH (1993) と fig-Forth から派生したものです。 もちろん、Gforth の設計の重要な部分は標準 Forth によって規定されました。

Bernd Paysan が書いた bigFORTH は、 主に Dietrich Weineck によって作成された、 Atari ST 用の VolksForth の未発表32ビット・ネイティブ・コード版である TurboForth の子孫です。

VolksForth は、 80 年代半ばに C64 (そこでは UltraForth と呼ばれていました) 用に Klaus Schleisiek と Bernd Pennemann と Georg Rehfeld と Dietrich Weineck によって作成され、 1986 年に Atari ST に移植されました。

Bill Ragsdale が率いるチームは、 1979 年に多くのプロセッサに fig-Forth を実装しました。 Robert Selzer と Bill Ragsdale は、 microForth に基づいて 6502 用のオリジナルの fig-Forth 実装を開発しました。

microForth の主な設計者は Dean Sanderson でした。 microForth は、 FORTH, Inc. の最初の既製製品でした。 これは 1976 年に 1802 用に開発され、 その後 8080 や 6800 や Z80 に実装されました。

初期の Forth システムはすべてカスタム・メイドで、 通常は 60 年代後半に(Charles Moore 本人曰く)「Forth を発見した」(discover Forth) Charles Moore によって作られました。 最初の完全な Forth は 1971 年に存在しました。

このセクションの情報の一部は、HOPL-II 会議で発表され、 SIGPLAN Notices 28(3), 1993 年にプレプリントされた、 Elizabeth D. Rather と Donald R. Colburn と Charles H. Moore による The Evolution of Forth からのものです。そこにはフォースに関する歴史的および系図的な情報が含まれています。 より一般的な (そしてグラフィカルな) Forth 家系図については、 https://www.complang.tuwien.ac.at/forth/family-tree/, Forth Family Tree and Timeline を参照してください。


Appendix C Other Forth-related information

Forth (Gforth を含む) および 関連の問題について議論する活発なニュース・グループ (comp.lang.forth) があります。 その FAQs (よくある質問とその回答) には、Forth に関する多くの情報が含まれています。 comp.lang.forth に投稿する前に必ず読んでください。

Forth 標準については、 HTML form がもっとも利用しやすいです。


Appendix D Licenses


D.1 GNU Free Documentation License

Version 1.2, November 2002
Copyright © 2000,2001,2002 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA  02111-1307, USA

Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
  1. PREAMBLE

    The purpose of this License is to make a manual, textbook, or other functional and useful document free in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.

    This License is a kind of “copyleft”, which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.

    We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.

  2. APPLICABILITY AND DEFINITIONS

    This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The “Document”, below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as “you”. You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law.

    A “Modified Version” of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.

    A “Secondary Section” is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document’s overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.

    The “Invariant Sections” are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none.

    The “Cover Texts” are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words.

    A “Transparent” copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not “Transparent” is called “Opaque”.

    Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only.

    The “Title Page” means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, “Title Page” means the text near the most prominent appearance of the work’s title, preceding the beginning of the body of the text.

    A section “Entitled XYZ” means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as “Acknowledgements”, “Dedications”, “Endorsements”, or “History”.) To “Preserve the Title” of such a section when you modify the Document means that it remains a section “Entitled XYZ” according to this definition.

    The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License.

  3. VERBATIM COPYING

    You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.

    You may also lend copies, under the same conditions stated above, and you may publicly display copies.

  4. COPYING IN QUANTITY

    If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document’s license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.

    If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.

    If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.

    It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.

  5. MODIFICATIONS

    You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:

    1. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.
    2. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement.
    3. State on the Title page the name of the publisher of the Modified Version, as the publisher.
    4. Preserve all the copyright notices of the Document.
    5. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.
    6. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.
    7. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document’s license notice.
    8. Include an unaltered copy of this License.
    9. Preserve the section Entitled “History”, Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled “History” in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.
    10. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the “History” section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.
    11. For any section Entitled “Acknowledgements” or “Dedications”, Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.
    12. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.
    13. Delete any section Entitled “Endorsements”. Such a section may not be included in the Modified Version.
    14. Do not retitle any existing section to be Entitled “Endorsements” or to conflict in title with any Invariant Section.
    15. Preserve any Warranty Disclaimers.

    If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version’s license notice. These titles must be distinct from any other section titles.

    You may add a section Entitled “Endorsements”, provided it contains nothing but endorsements of your Modified Version by various parties—for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.

    You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.

    The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.

  6. COMBINING DOCUMENTS

    You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers.

    The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.

    In the combination, you must combine any sections Entitled “History” in the various original documents, forming one section Entitled “History”; likewise combine any sections Entitled “Acknowledgements”, and any sections Entitled “Dedications”. You must delete all sections Entitled “Endorsements.”

  7. COLLECTIONS OF DOCUMENTS

    You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.

    You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.

  8. AGGREGATION WITH INDEPENDENT WORKS

    A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an “aggregate” if the copyright resulting from the compilation is not used to limit the legal rights of the compilation’s users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document.

    If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document’s Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate.

  9. TRANSLATION

    Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail.

    If a section in the Document is Entitled “Acknowledgements”, “Dedications”, or “History”, the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title.

  10. TERMINATION

    You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.

  11. FUTURE REVISIONS OF THIS LICENSE

    The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/.

    Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License “or any later version” applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation.

D.1.1 ADDENDUM: How to use this License for your documents

To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:

  Copyright (C)  year  your name.
  Permission is granted to copy, distribute and/or modify this document
  under the terms of the GNU Free Documentation License, Version 1.2
  or any later version published by the Free Software Foundation;
  with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
  Texts.  A copy of the license is included in the section entitled ``GNU
  Free Documentation License''.

If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the “with...Texts.” line with this:

    with the Invariant Sections being list their titles, with
    the Front-Cover Texts being list, and with the Back-Cover Texts
    being list.

If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation.

If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.


D.2 GNU GENERAL PUBLIC LICENSE

Version 3, 29 June 2007
Copyright © 2007 Free Software Foundation, Inc. http://fsf.org/

Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.

Preamble

The GNU General Public License is a free, copyleft license for software and other kinds of works.

The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program—to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too.

When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.

To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others.

For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.

Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it.

For the developers’ and authors’ protection, the GPL clearly explains that there is no warranty for this free software. For both users’ and authors’ sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions.

Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users’ freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users.

Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free.

The precise terms and conditions for copying, distribution and modification follow.

TERMS AND CONDITIONS

  1. Definitions.

    “This License” refers to version 3 of the GNU General Public License.

    “Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.

    “The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations.

    To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work.

    A “covered work” means either the unmodified Program or a work based on the Program.

    To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.

    To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.

    An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.

  2. Source Code.

    The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work.

    A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.

    The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.

    The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work’s System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.

    The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.

    The Corresponding Source for a work in source code form is that same work.

  3. Basic Permissions.

    All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.

    You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.

    Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.

  4. Protecting Users’ Legal Rights From Anti-Circumvention Law.

    No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.

    When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work’s users, your or third parties’ legal rights to forbid circumvention of technological measures.

  5. Conveying Verbatim Copies.

    You may convey verbatim copies of the Program’s source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.

    You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.

  6. Conveying Modified Source Versions.

    You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:

    1. The work must carry prominent notices stating that you modified it, and giving a relevant date.
    2. The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”.
    3. You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.
    4. If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.

    A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation’s users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.

  7. Conveying Non-Source Forms.

    You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:

    1. Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.
    2. Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.
    3. Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.
    4. Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.
    5. Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.

    A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.

    A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.

    “Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.

    If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).

    The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.

    Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.

  8. Additional Terms.

    “Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.

    When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.

    Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:

    1. Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or
    2. Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or
    3. Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or
    4. Limiting the use for publicity purposes of names of licensors or authors of the material; or
    5. Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or
    6. Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.

    All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.

    If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.

    Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.

  9. Termination.

    You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).

    However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.

    Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.

    Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.

  10. Acceptance Not Required for Having Copies.

    You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.

  11. Automatic Licensing of Downstream Recipients.

    Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.

    An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party’s predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.

    You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.

  12. Patents.

    A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor’s “contributor version”.

    A contributor’s “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.

    Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor’s essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.

    In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.

    If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient’s use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.

    If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.

    A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.

    Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.

  13. No Surrender of Others’ Freedom.

    If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.

  14. Use with the GNU Affero General Public License.

    Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such.

  15. Revised Versions of this License.

    The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.

    Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation.

    If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy’s public statement of acceptance of a version permanently authorizes you to choose that version for the Program.

    Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.

  16. Disclaimer of Warranty.

    THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  17. Limitation of Liability.

    IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

  18. Interpretation of Sections 15 and 16.

    If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.

END OF TERMS AND CONDITIONS

How to Apply These Terms to Your New Programs

If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.

To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found.

one line to give the program's name and a brief idea of what it does.
Copyright (C) year name of author

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see http://www.gnu.org/licenses/.

Also add information on how to contact you by electronic and paper mail.

If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:

program Copyright (C) year name of author
This program comes with ABSOLUTELY NO WARRANTY; for details type ‘show w’.
This is free software, and you are welcome to redistribute it
under certain conditions; type ‘show c’ for details.

The hypothetical commands ‘show w’ and ‘show c’ should show the appropriate parts of the General Public License. Of course, your program’s commands might be different; for a GUI interface, you would use an “about box”.

You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see http://www.gnu.org/licenses/.

The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read http://www.gnu.org/philosophy/why-not-lgpl.html.


Word Index

この索引は、 このマニュアル内に glossary 項目がある Forth ワードのリストです。 各ワードはそのスタック効果とワード・セットとともにリストされます。

Jump to:   -   ,   ;   :   !   ?   .   '   (   )   [   ]   {   }   @   *   /   \   #   %   `   +   <   =   >   |   ~   $   0   1   2  
A   B   C   D   E   F   G   H   I   J   K   L   M   N   O   P   Q   R   S   T   U   V   W   X   Y  
Index EntrySection

-
- ( n1 n2 -- n ) coreSingle precision
-- ( hmaddr u latest latestnt wid 0 ... -- ) gforth-0.2Locals definition words
--> ( -- ) gforth-0.2Blocks
-[do ( compilation -- do-sys ; run-time n1 n2 -- | loop-sys ) gforth-experimentalCounted Loops
-\d ( addr -- addr' ) regexp-patternRegular Expressions
-\s ( addr -- addr' ) regexp-patternRegular Expressions
-` ( "char" -- ) regexp-patternRegular Expressions
->here ( addr -- ) gforth-1.0Dictionary allocation
-c? ( addr class -- ) regexp-patternRegular Expressions
-char ( char -- ) regexp-cgRegular Expressions
-class ( class -- ) regexp-cgRegular Expressions
-DO ( compilation -- do-sys ; run-time n1 n2 -- | loop-sys ) gforth-0.2Counted Loops
-infinity ( -- r ) gforth-1.0Floating Point
-LOOP ( compilation do-sys -- ; run-time loop-sys1 u -- | loop-sys2 ) gforth-0.2Counted Loops
-ltrace ( -- ) gforth-1.0Debugging
-rot ( w1 w2 w3 -- w3 w1 w2 ) gforth-0.2Data stack
-trailing ( c_addr u1 -- c_addr u2 ) stringString words
-trailing-garbage ( xc-addr u1 -- xc-addr u2 ) xchar-extXchars and Unicode

,
, ( w -- ) coreDictionary allocation

;
; ( compilation colon-sys -- ; run-time nest-sys -- ) coreColon Definitions
;] ( compile-time: quotation-sys -- ; run-time: -- xt ) gforth-1.0Quotations
;> ( -- ) gforth-experimentalClosures
;abi-code ( -- ) gforth-1.0Assembler Definitions
;code ( compilation. colon-sys1 -- colon-sys2 ) tools-extAssembler Definitions
;inline ( inline:-sys -- ) gforth-experimentalColon Definitions
;m ( colon-sys --; run-time: -- ) objectsObjects Glossary
;s ( R:w -- ) gforth-0.2Calls and returns

:
: ( "name" -- colon-sys ) coreColon Definitions
:: ( class "name" -- ) mini-oofBasic Mini-OOF Usage
:} ( hmaddr u latest latestnt wid 0 xt1 ... xtn -- ) gforth-1.0Locals definition words
:}d ( hmaddr u latest latestnt wid 0 a-addr1 u1 ... -- ) gforth-1.0Closures
:}h ( hmaddr u latest latestnt wid 0 a-addr1 u1 ... -- ) gforth-1.0Closures
:}h1 ( hmaddr u latest latestnt wid 0 a-addr1 u1 ... -- ) gforth-1.0Closures
:}l ( hmaddr u latest latestnt wid 0 a-addr1 u1 ... -- ) gforth-1.0Closures
:}xt ( hmaddr u latest latestnt wid 0 a-addr1 u1 ... -- ) gforth-1.0Closures
:m ( "name" -- xt; run-time: object -- ) objectsObjects Glossary
:noname ( -- xt colon-sys ) core-extAnonymous Definitions

!
! ( w a-addr -- ) coreMemory Access
!!FIXME!! ( -- ) gforth-1.0Debugging
!@ ( u1 a-addr -- u2 ) gforth-experimentalHardware operations for multi-tasking
!resize ( rx ry rw rh rd -- ) minos2widget methods
!size ( -- ) minos2widget methods

?
? ( a-addr -- ) toolsExamining data
?!@ ( unew uold a-addr -- uprev ) gforth-experimentalHardware operations for multi-tasking
??? ( -- ) gforth-0.2Debugging
?cov+ ( flag -- flag ) gforth-experimentalCode Coverage
?DO ( compilation -- do-sys ; run-time w1 w2 -- | loop-sys ) core-extCounted Loops
?dup ( w -- S:... w ) coreData stack
?DUP-0=-IF ( compilation -- orig ; run-time n -- n| ) gforth-0.2Arbitrary control structures
?dup-IF ( compilation -- orig ; run-time n -- n| ) gforth-0.2Arbitrary control structures
?events ( -- ) gforth-experimentalMessage queues
?EXIT ( -- ) gforth-0.2Calls and returns
?inside ( rx ry -- act / 0 ) minos2actor methods
?LEAVE ( compilation -- ; run-time f | f loop-sys -- ) gforth-0.2Counted Loops
?of ( compilation -- of-sys ; run-time f -- ) gforth-1.0Arbitrary control structures

.
. ( n -- ) coreSimple numeric output
.? ( addr -- addr' ) regexp-patternRegular Expressions
... ( x1 .. xn -- x1 .. xn ) gforth-1.0Examining data
..char ( start end -- ) regexp-cgRegular Expressions
." ( compilation 'ccc"' -- ; run-time -- ) coreMiscellaneous output
.( ( compilation&interpretation "ccc<paren>" -- ) core-extMiscellaneous output
.\" ( compilation 'ccc"' -- ; run-time -- ) gforth-0.6Miscellaneous output
.cover-raw ( -- ) gforth-experimentalCode Coverage
.coverage ( -- ) gforth-experimentalCode Coverage
.debugline ( nfile nline -- ) gforth-0.6Debugging
.fpath ( -- ) gforth-0.4Source Search Paths
.hm ( nt -- ) gforth-1.0Header methods
.id ( nt -- ) gforth-0.6Name token
.included ( -- ) gforth-0.5Forth source files
.locale-csv ( -- ) gforth-experimentali18n and l10n
.path ( path-addr -- ) gforth-0.4General Search Paths
.r ( n1 n2 -- ) core-extSimple numeric output
.recognizers ( -- ) gforth-experimentalDefault Recognizers
.s ( -- ) toolsExamining data
.substitute ( addr1 len1 -- n / ior ) gforth-experimentalSubstitute
.unresolved ( -- ) gforth-1.0Forward
.voc ( wid -- ) gforth-0.2Word Lists
.widget ( -- ) minos2widget methods

'
' ( "name" -- xt ) coreExecution token
'cold ( -- ) gforth-0.2Modifying the Startup Sequence
's ( addr1 task -- addr2 ) gforth-experimentalTask-local data

(
( ( compilation 'ccc<close-paren>' -- ; run-time -- ) core,fileComments
(( ( addr u -- ) regexp-patternRegular Expressions
(local) ( addr u -- ) localStandard Forth locals
(to) ( val operation xt -- ) gforth-1.0User-defined TO and DEFER@

)
) ( -- ) gforth-0.2Assertions
)) ( -- flag ) regexp-patternRegular Expressions

[
[ ( -- ) coreLiterals
[: ( compile-time: -- quotation-sys flag colon-sys ) gforth-1.0Quotations
[?DO] ( n-limit n-index -- ) gforth-0.2Interpreter Directives
['] ( compilation. "name" -- ; run-time. -- xt ) coreExecution token
[{: ( -- hmaddr u latest latestnt wid 0 ) gforth-experimentalClosures
[+LOOP] ( n -- ) gforth-0.2Interpreter Directives
[AGAIN] ( -- ) gforth-0.2Interpreter Directives
[BEGIN] ( -- ) gforth-0.2Interpreter Directives
[bind] ( compile-time: "class" "selector" -- ; run-time: ... object -- ... ) objectsObjects Glossary
[char] ( compilation '<spaces>ccc' -- ; run-time -- c ) core,xchar-extString and character literals
[COMP'] ( compilation "name" -- ; run-time -- w xt ) gforth-0.2Compilation token
[compile] ( compilation "name" -- ; run-time ? -- ? ) core-extMacros
[current] ( compile-time: "selector" -- ; run-time: ... object -- ... ) objectsObjects Glossary
[defined] ( "<spaces>name" -- flag ) tools-extInterpreter Directives
[DO] ( n-limit n-index -- ) gforth-0.2Interpreter Directives
[ELSE] ( -- ) tools-extInterpreter Directives
[ENDIF] ( -- ) gforth-0.2Interpreter Directives
[FOR] ( n -- ) gforth-0.2Interpreter Directives
[I] ( run-time -- n ) gforth-0.2Interpreter Directives
[IF] ( flag -- ) tools-extInterpreter Directives
[IFDEF] ( "<spaces>name" -- ) gforth-0.2Interpreter Directives
[IFUNDEF] ( "<spaces>name" -- ) gforth-0.2Interpreter Directives
[LOOP] ( -- ) gforth-0.2Interpreter Directives
[NEXT] ( n -- ) gforth-0.2Interpreter Directives
[parent] ( compile-time: "selector" -- ; run-time: ... object -- ... ) objectsObjects Glossary
[REPEAT] ( -- ) gforth-0.2Interpreter Directives
[THEN] ( -- ) tools-extInterpreter Directives
[to-inst] ( compile-time: "name" -- ; run-time: w -- ) objectsObjects Glossary
[undefined] ( "<spaces>name" -- flag ) tools-extInterpreter Directives
[UNTIL] ( flag -- ) gforth-0.2Interpreter Directives
[WHILE] ( flag -- ) gforth-0.2Interpreter Directives

]
] ( -- ) coreLiterals
]] ( -- ) gforth-0.6Macros
]L ( compilation: n -- ; run-time: -- n ) gforth-0.5Literals
]nocov ( -- ) gforth-1.0Code Coverage

{
{ ( -- hmaddr u latest latestnt wid 0 ) gforth-0.2Locals definition words
{: ( -- hmaddr u latest latestnt wid 0 ) local-extLocals definition words
{{ ( addr -- addr addr ) regexp-patternRegular Expressions
{* ( addr -- addr addr ) regexp-patternRegular Expressions
{** ( addr -- addr addr ) regexp-patternRegular Expressions
{+ ( addr -- addr addr ) regexp-patternRegular Expressions
{++ ( addr -- addr addr ) regexp-patternRegular Expressions

}
} ( hmaddr u latest latestnt wid 0 xt1 ... xtn -- ) gforth-0.2Locals definition words
}} ( addr addr -- addr ) regexp-patternRegular Expressions

@
@ ( a-addr -- w ) coreMemory Access
@localn ( noffset -- w ) gforth-internalLocals implementation

*
* ( n1 n2 -- n ) coreSingle precision
*} ( addr addr' -- addr' ) regexp-patternRegular Expressions
**} ( sys -- ) regexp-patternRegular Expressions
*/ ( ( n1 n2 n3 -- n4 ) coreInteger division
*/f ( n1 n2 n3 -- n4 ) gforth-1.0Integer division
*/mod ( n1 n2 n3 -- n4 n5 ) coreInteger division
*/modf ( n1 n2 n3 -- n4 n5 ) gforth-1.0Integer division
*/mods ( n1 n2 n3 -- n4 n5 ) gforth-1.0Integer division
*/s ( n1 n2 n3 -- n4 ) gforth-1.0Integer division
*align ( n -- ) gforth-1.0Address arithmetic
*aligned ( addr1 n -- addr2 ) gforth-1.0Address arithmetic

/
/ ( n1 n2 -- n ) coreInteger division
// ( -- ) regexp-patternRegular Expressions
//g ( ptr addr u -- addr' u' ) regexp-replaceRegular Expressions
//o ( ptr addr u -- addr' u' ) regexp-replaceRegular Expressions
//s ( ptr -- ) regexp-replaceRegular Expressions
/COUNTED-STRING ( -- n ) environmentEnvironmental Queries
/f ( n1 n2 -- n ) gforth-1.0Integer division
/f-stage1m ( n addr-reci -- ) gforth-1.0Two-stage integer division
/f-stage2m ( n1 a-reci -- nquotient ) gforth-1.0Two-stage integer division
/HOLD ( -- n ) environmentEnvironmental Queries
/l ( -- u ) gforth-0.7Address arithmetic
/mod ( n1 n2 -- n3 n4 ) coreInteger division
/modf ( n1 n2 -- n3 n4 ) gforth-1.0Integer division
/modf-stage2m ( n1 a-reci -- umodulus nquotient ) gforth-1.0Two-stage integer division
/mods ( n1 n2 -- n3 n4 ) gforth-1.0Integer division
/PAD ( -- n ) environmentEnvironmental Queries
/s ( n1 n2 -- n ) gforth-1.0Integer division
/string ( c-addr1 u1 n -- c-addr2 u2 ) stringString words
/w ( -- u ) gforth-0.7Address arithmetic
/x ( -- u ) gforth-1.0Address arithmetic

\
\ ( compilation 'ccc<newline>' -- ; run-time -- ) core-ext,block-extComments
\( ( addr -- addr ) regexp-patternRegular Expressions
\) ( addr -- addr ) regexp-patternRegular Expressions
\\\ ( -- ) gforth-1.0Forth source files
\^ ( addr -- addr ) regexp-patternRegular Expressions
\$ ( addr -- addr ) regexp-patternRegular Expressions
\0 ( -- addr u ) regexp-patternRegular Expressions
\c ( "rest-of-line" -- ) gforth-0.7Declaring C Functions
\d ( addr -- addr' ) regexp-patternRegular Expressions
\G ( compilation 'ccc<newline>' -- ; run-time -- ) gforth-0.2Comments
\s ( addr -- addr' ) regexp-patternRegular Expressions

#
# ( ud1 -- ud2 ) coreFormatted numeric output
#! ( -- ) gforth-0.2Running Image Files
#> ( xd -- addr u ) coreFormatted numeric output
#>> ( -- ) gforth-0.5Formatted numeric output
#bell ( -- c ) gforth-0.2String and character literals
#bs ( -- c ) gforth-0.2String and character literals
#cr ( -- c ) gforth-0.2String and character literals
#del ( -- c ) gforth-0.2String and character literals
#eof ( -- c ) gforth-0.7String and character literals
#esc ( -- c ) gforth-0.5String and character literals
#ff ( -- c ) gforth-0.2String and character literals
#lf ( -- c ) gforth-0.2String and character literals
#line ( "u" "["file"]" -- ) gforth-1.0Interpreter Directives
#loc ( nline nchar "file" -- ) gforth-1.0Debugging
#locals ( -- n ) environmentEnvironmental Queries
#s ( ud -- 0 0 ) coreFormatted numeric output
#tab ( -- c ) gforth-0.2String and character literals
#tib ( -- addr ) core-ext-obsolescentThe Text Interpreter

%
%align ( align size -- ) gforth-0.4Structure Glossary
%alignment ( align size -- align ) gforth-0.4Structure Glossary
%alloc ( align size -- addr ) gforth-0.4Structure Glossary
%allocate ( align size -- addr ior ) gforth-0.4Structure Glossary
%allot ( align size -- addr ) gforth-0.4Structure Glossary
%size ( align size -- size ) gforth-0.4Structure Glossary

`
` ( "char" -- ) regexp-patternRegular Expressions
`? ( "char" -- ) regexp-patternRegular Expressions

+
+ ( n1 n2 -- n ) coreSingle precision
+! ( n a-addr -- ) coreMemory Access
+!@ ( u1 a-addr -- u2 ) gforth-experimentalHardware operations for multi-tasking
+} ( addr addr' -- addr' ) regexp-patternRegular Expressions
++} ( sys -- ) regexp-patternRegular Expressions
+char ( char -- ) regexp-cgRegular Expressions
+chars ( addr u -- ) regexp-cgRegular Expressions
+class ( class -- ) regexp-cgRegular Expressions
+DO ( compilation -- do-sys ; run-time n1 n2 -- | loop-sys ) gforth-0.2Counted Loops
+field ( noffset1 nsize "name" -- noffset2 ) facility-extForth200x Structures
+fmode ( fam1 rwxrwxrwx -- fam2 ) gforth-1.0General files
+load ( i*x n -- j*x ) gforth-0.2Blocks
+LOOP ( compilation do-sys -- ; run-time loop-sys1 n -- | loop-sys2 ) coreCounted Loops
+ltrace ( -- ) gforth-1.0Debugging
+thru ( i*x n1 n2 -- j*x ) gforth-0.2Blocks
+TO ( value "name" -- ) gforth-1.0Values
+x/string ( xc-addr1 u1 -- xc-addr2 u2 ) xchar-extXchars and Unicode

<
< ( n1 n2 -- f ) coreNumeric comparison
<{: ( -- hmaddr u latest latestnt wid 0 ) gforth-experimentalClosures
<# ( -- ) coreFormatted numeric output
<< ( run-addr addr u -- run-addr ) regexp-replaceRegular Expressions
<<" ( "string<">" -- ) regexp-replaceRegular Expressions
<<# ( -- ) gforth-0.5Formatted numeric output
<= ( n1 n2 -- f ) gforth-0.2Numeric comparison
<> ( n1 n2 -- f ) core-extNumeric comparison
<bind> ( class selector-xt -- xt ) objectsObjects Glossary
<to-inst> ( w xt -- ) objectsObjects Glossary

=
= ( n1 n2 -- f ) coreNumeric comparison
=mkdir ( c-addr u wmode -- wior ) gforth-0.7Directories

>
> ( n1 n2 -- f ) coreNumeric comparison
>= ( n1 n2 -- f ) gforth-0.2Numeric comparison
>> ( addr -- addr ) regexp-replaceRegular Expressions
>addr ( xt -- addr ) gforth-experimentalClosures
>animate ( rdelta addr xt -- ) minos2widget methods
>body ( xt -- a-addr ) coreCREATE..DOES> details
>code-address ( xt -- c_addr ) gforth-0.2Threading Words
>compile ( translator -- ) gforth-experimentalDealing with existing Recognizers
>definer ( xt -- definer ) gforth-0.2Threading Words
>does-code ( xt1 -- xt2 ) gforth-0.2Threading Words
>float ( c-addr u -- f:... flag ) floatingLine input and conversion
>float1 ( c-addr u c -- f:... flag ) gforth-1.0Line input and conversion
>in ( -- addr ) coreThe Text Interpreter
>interpret ( translator -- ) gforth-experimentalDealing with existing Recognizers
>l ( w -- ) gforth-0.2Locals implementation
>name ( xt -- nt|0 ) gforth-0.2Name token
>number ( ud1 c-addr1 u1 -- ud2 c-addr2 u2 ) coreLine input and conversion
>o ( c-addr -- r:c-old ) newMini-OOF2
>order ( wid -- ) gforth-0.5Word Lists
>postpone ( translator -- ) gforth-experimentalDealing with existing Recognizers
>pow2 ( u1 -- u2 ) gforth-1.0Bitwise operations
>r ( w -- R:w ) coreReturn stack
>string-execute ( ... xt -- ... addr u ) gforth-1.0String words
>time&date&tz ( udtime -- nsec nmin nhour nday nmonth nyear fdst ndstoff c-addrtz utz ) gforth-1.0Keeping track of Time

|
| ( -- ) gforth-1.0Locals definition words
|| ( addr addr -- addr addr ) regexp-patternRegular Expressions

~
~~ ( -- ) gforth-0.2Debugging
~~1bt ( -- ) gforth-1.0Debugging
~~bt ( -- ) gforth-1.0Debugging
~~Value ( n "name" -- ) gforth-1.0Debugging
~~Variable ( "name" -- ) gforth-1.0Debugging

$
$! ( addr1 u $addr -- ) gforth-0.7$tring words
$!len ( u $addr -- ) gforth-0.7$tring words
$? ( -- n ) gforth-0.2Passing Commands to the OS
$. ( addr -- ) gforth-1.0$tring words
$[] ( u $[]addr -- addr' ) gforth-1.0$tring words
$[]! ( c-addr u n $[]addr -- ) gforth-1.0$tring words
$[]. ( addr -- ) gforth-1.0$tring words
$[]@ ( n $[]addr -- addr u ) gforth-1.0$tring words
$[]# ( addr -- len ) gforth-1.0$tring words
$[]+! ( c-addr u n $[]addr -- ) gforth-1.0$tring words
$[]boot ( addr -- ) gforth-1.0$tring words
$[]free ( addr -- ) gforth-1.0$tring words
$[]map ( addr xt -- ) gforth-1.0$tring words
$[]save ( addr -- ) gforth-1.0$tring words
$[]saved ( addr -- ) gforth-1.0$tring words
$[]slurp ( fid addr -- ) gforth-1.0$tring words
$[]slurp-file ( addr u $addr -- ) gforth-1.0$tring words
$[]Variable ( -- ) gforth-1.0$tring words
$@ ( $addr -- addr2 u ) gforth-0.7$tring words
$@len ( $addr -- u ) gforth-0.7$tring words
$+! ( addr1 u $addr -- ) gforth-0.7$tring words
$+!len ( u $addr -- addr ) gforth-1.0$tring words
$+[]! ( c-addr u $[]addr -- ) gforth-1.0$tring words
$+slurp ( fid addr -- ) gforth-1.0$tring words
$+slurp-file ( c-addr u addr -- ) gforth-1.0$tring words
$boot ( $addr -- ) gforth-1.0$tring words
$del ( addr off u -- ) gforth-0.7$tring words
$exec ( xt addr -- ) gforth-1.0$tring words
$free ( $addr -- ) gforth-1.0$tring words
$init ( $addr -- ) gforth-1.0$tring words
$ins ( addr1 u $addr off -- ) gforth-0.7$tring words
$iter ( .. $addr char xt -- .. ) gforth-0.7$tring words
$over ( addr u $addr off -- ) gforth-1.0$tring words
$save ( $addr -- ) gforth-1.0$tring words
$saved ( addr -- ) gforth-1.0$tring words
$slurp ( fid addr -- ) gforth-1.0$tring words
$slurp-file ( c-addr u addr -- ) gforth-1.0$tring words
$split ( addr u char -- addr1 u1 addr2 u2 ) gforth-0.7$tring words
$substitute ( addr1 len1 -- addr2 len2 n/ior ) gforth-experimentalSubstitute
$tmp ( xt -- addr u ) gforth-1.0$tring words
$unescape ( addr1 u1 -- addr2 u2 ) gforth-experimentalSubstitute
$Variable ( -- ) gforth-1.0$tring words

0
0< ( n -- f ) coreNumeric comparison
0<= ( n -- f ) gforth-0.2Numeric comparison
0<> ( n -- f ) core-extNumeric comparison
0= ( n -- f ) coreNumeric comparison
0> ( n -- f ) core-extNumeric comparison
0>= ( n -- f ) gforth-0.2Numeric comparison

1
1- ( n1 -- n2 ) coreSingle precision
1/f ( r1 -- r2 ) gforth-0.2Floating Point
1+ ( n1 -- n2 ) coreSingle precision

2
2, ( w1 w2 -- ) gforth-0.2Dictionary allocation
2! ( w1 w2 a-addr -- ) coreMemory Access
2@ ( a-addr -- w1 w2 ) coreMemory Access
2* ( n1 -- n2 ) coreBitwise operations
2/ ( n1 -- n2 ) coreBitwise operations
2>r ( w1 w2 -- R:w1 R:w2 ) core-extReturn stack
2Constant ( w1 w2 "name" -- ) doubleConstants
2drop ( w1 w2 -- ) coreData stack
2dup ( w1 w2 -- w1 w2 w1 w2 ) coreData stack
2field: ( u1 "name" -- u2 ) gforth-0.7Forth200x Structures
2Literal ( compilation w1 w2 -- ; run-time -- w1 w2 ) doubleLiterals
2nip ( w1 w2 w3 w4 -- w3 w4 ) gforth-0.2Data stack
2over ( w1 w2 w3 w4 -- w1 w2 w3 w4 w1 w2 ) coreData stack
2r@ ( R:w1 R:w2 -- R:w1 R:w2 w1 w2 ) core-extReturn stack
2r> ( R:w1 R:w2 -- w1 w2 ) core-extReturn stack
2rdrop ( R:w1 R:w2 -- ) gforth-0.2Return stack
2rot ( w1 w2 w3 w4 w5 w6 -- w3 w4 w5 w6 w1 w2 ) double-extData stack
2swap ( w1 w2 w3 w4 -- w3 w4 w1 w2 ) coreData stack
2tuck ( w1 w2 w3 w4 -- w3 w4 w1 w2 w3 w4 ) gforth-0.2Data stack
2Value ( d "name" -- ) double-extValues
2Variable ( "name" -- ) doubleVariables
2varue ( x1 x2 "name" -- ) gforth-1.0Varues

A
A, ( addr -- ) gforth-0.2Dictionary allocation
abi-code ( "name" -- colon-sys ) gforth-1.0Assembler Definitions
abort ( ?? -- ?? ) core,exception-extException Handling
ABORT" ( compilation 'ccc"' -- ; run-time f -- ) core,exception-extException Handling
abs ( n -- u ) coreSingle precision
absolute-file? ( addr u -- flag ) gforth-1.0Search Paths
accept ( c-addr +n1 -- +n2 ) coreLine input and conversion
AConstant ( addr "name" -- ) gforth-0.2Constants
act ( -- optr ) minos2widget methods
act-name$ ( -- addr u ) minos2actor methods
action-of ( interpretation "name" -- xt; compilation "name" -- ; run-time -- xt ) core-extDeferred Words
activate ( run-time nest-sys1 task -- ) gforth-experimentalBasic multi-tasking
active-w ( -- optr ) minos2actor methods
actor ( -- class ) minos2MINOS2 object framework
add-cflags ( c-addr u -- ) gforth-1.0Declaring OS-level libraries
add-framework ( c-addr u -- ) gforth-1.0Declaring OS-level libraries
add-incdir ( c-addr u -- ) gforth-1.0Declaring OS-level libraries
add-ldflags ( c-addr u -- ) gforth-1.0Declaring OS-level libraries
add-lib ( c-addr u -- ) gforth-0.7Declaring OS-level libraries
add-libpath ( c-addr u -- ) gforth-0.7Declaring OS-level libraries
addr ( "name" -- addr ) gforth-1.0Varues
ADDRESS-UNIT-BITS ( -- n ) environmentEnvironmental Queries
adjust-buffer ( u addr -- ) gforth-experimentalHeap Allocation
after-locate ( -- u ) gforth-1.0Locating source code definitions
AGAIN ( compilation dest -- ; run-time -- ) core-extArbitrary control structures
AHEAD ( compilation -- orig ; run-time -- ) tools-extArbitrary control structures
Alias ( xt "name" -- ) gforth-0.2Aliases
align ( -- ) coreDictionary allocation
aligned ( c-addr -- a-addr ) coreAddress arithmetic
ALiteral ( compilation addr -- ; run-time -- addr ) gforth-0.2Literals
allocate ( u -- a-addr wior ) memoryHeap Allocation
allot ( n -- ) coreDictionary allocation
also ( -- ) search-extWord Lists
also-path ( c-addr len path-addr -- ) gforth-0.4General Search Paths
and ( w1 w2 -- w ) coreBitwise operations
annotate-cov ( -- ) gforth-experimentalCode Coverage
append ( c-addr1 u1 c-addr2 u2 -- c-addr u ) gforth-0.7String words
arg ( u -- addr count ) gforth-0.2OS command line arguments
argc ( -- addr ) gforth-0.2OS command line arguments
argv ( -- addr ) gforth-0.2OS command line arguments
arshift ( n1 u -- n2 ) gforth-1.0Bitwise operations
asptr ( class -- ) oofClass Declaration
assembler ( -- ) tools-extAssembler Definitions
assert-level ( -- a-addr ) gforth-0.2Assertions
assert( ( -- ) gforth-0.2Assertions
assert0( ( -- ) gforth-0.2Assertions
assert1( ( -- ) gforth-0.2Assertions
assert2( ( -- ) gforth-0.2Assertions
assert3( ( -- ) gforth-0.2Assertions
ASSUME-LIVE ( orig -- orig ) gforth-0.2Where are locals visible by name?
at-deltaxy ( dx dy -- ) gforth-0.7Terminal output
at-xy ( x y -- ) facilityTerminal output
AUser ( "name" -- ) gforth-0.2Task-local data
authors ( -- ) gforth-1.0Help on Gforth
AValue ( w "name" -- ) gforth-0.6Values
AVariable ( "name" -- ) gforth-0.2Variables

B
b ( -- ) gforth-1.0Locating source code definitions
barrier ( -- ) gforth-experimentalHardware operations for multi-tasking
base ( -- a-addr ) coreNumber Conversion
base-execute ( i*x xt u -- j*x ) gforth-0.7Number Conversion
baseline ( -- r ) minos2widget methods
basename ( c-addr1 u1 -- c-addr2 u2 ) gforth-0.7Directories
before-line ( -- ) gforth-1.0Text Interpreter Hooks
before-locate ( -- u ) gforth-1.0Locating source code definitions
before-word ( -- ) gforth-0.7Text Interpreter Hooks
BEGIN ( compilation -- dest ; run-time -- ) coreArbitrary control structures
begin-structure ( "name" -- struct-sys 0 ) facility-extForth200x Structures
bin ( fam1 -- fam2 ) fileGeneral files
bind ( ... "class" "selector" -- ... ) objectsObjects Glossary
bind' ( "class" "selector" -- xt ) objectsObjects Glossary
bl ( -- c-char ) coreString and character literals
blank ( c-addr u -- ) stringMemory Blocks
blk ( -- addr ) blockInput Sources
block ( u -- a-addr ) blockBlocks
block-included ( a-addr u -- ) gforth-0.2Blocks
block-offset ( -- addr ) gforth-0.5Blocks
block-position ( u -- ) blockBlocks
bootmessage ( -- ) gforth-0.4Modifying the Startup Sequence
border ( -- r ) minos2widget methods
borderl ( -- r ) minos2widget methods
bordert ( -- r ) minos2widget methods
borderv ( -- r ) minos2widget methods
bounds ( addr u -- addr+u addr ) gforth-0.2Counted Loops
break: ( -- ) gforth-0.4Singlestep Debugger
break" ( 'ccc"' -- ) gforth-0.4Singlestep Debugger
broken-pipe-error ( -- n ) gforth-0.6Pipes
browse ( "subname" -- ) gforth-1.0Locating source code definitions
bt ( -- ) gforth-1.0Locating exception source
buffer ( u -- a-addr ) blockBlocks
buffer: ( u "name" -- ) core-extVariables
buffer% ( -- u1 u2 ) gforth-experimentalHeap Allocation
bw ( -- ) gforth-1.0Locating uses of a word
bw-cover ( -- ) gforth-1.0Code Coverage

C
c-callback ( "forth-name" "{type}" "---" "type" -- ) gforth-1.0Callbacks
c-callback-thread ( "forth-name" "{type}" "---" "type" -- ) gforth-1.0Callbacks
c-function ( "forth-name" "c-name" "{type}" "---" "type" -- ) gforth-0.7Declaring C Functions
c-funptr ( "forth-name" <{>"c-typecast"<}> "{type}" "---" "type" -- ) gforth-1.0Calling C function pointers
c-library ( "name" -- ) gforth-0.7Defining library interfaces
c-library-name ( c-addr u -- ) gforth-0.7Defining library interfaces
c-value ( "forth-name" "c-name" "---" "type" -- ) gforth-1.0Declaring C Functions
c-variable ( "forth-name" "c-name" -- ) gforth-1.0Declaring C Functions
c, ( c -- ) coreDictionary allocation
C: ( compilation "name" -- a-addr xt; run-time c -- ) gforth-0.2Locals definition words
c! ( c c-addr -- ) coreMemory Access
c? ( addr class -- ) regexp-patternRegular Expressions
C" ( compilation "ccc<quote>" -- ; run-time -- c-addr ) core-extCounted string words
c@ ( c-addr -- c ) coreMemory Access
C^ ( compilation "name" -- a-addr xt; run-time c -- ) gforth-0.2Locals definition words
c>s ( x -- n ) gforth-1.0Special Memory Accesses
c$+! ( char $addr -- ) gforth-1.0$tring words
CA: ( compilation "name" -- a-addr xt; run-time c -- ) gforth-1.0Locals definition words
call-c ( ... w -- ... ) gforth-0.2Low-Level C Interface Words
caller-w ( -- optr ) minos2actor methods
capscompare ( c-addr1 u1 c-addr2 u2 -- n ) gforth-0.7String words
capssearch ( c-addr1 u1 c-addr2 u2 -- c-addr3 u3 flag ) gforth-1.0String words
capsstring-prefix? ( c-addr1 u1 c-addr2 u2 -- f ) gforth-1.0String words
case ( compilation -- case-sys ; run-time -- ) core-extArbitrary control structures
catch ( x1 .. xn xt -- y1 .. ym 0 / z1 .. zn error ) exceptionException Handling
cell ( -- u ) gforth-0.2Address arithmetic
cell- ( a-addr1 -- a-addr2 ) coreAddress arithmetic
cell/ ( n1 -- n2 ) gforth-1.0Address arithmetic
cell% ( -- align size ) gforth-0.4Structure Glossary
cell+ ( a-addr1 -- a-addr2 ) coreAddress arithmetic
cells ( n1 -- n2 ) coreAddress arithmetic
cfalign ( -- ) gforth-0.2Dictionary allocation
cfaligned ( addr1 -- addr2 ) gforth-0.2Address arithmetic
cfield: ( u1 "name" -- u2 ) facility-extForth200x Structures
char ( '<spaces>ccc' -- c ) core,xchar-extString and character literals
char- ( c-addr1 -- c-addr2 ) gforth-0.7Address arithmetic
char% ( -- align size ) gforth-0.4Structure Glossary
char+ ( c-addr1 -- c-addr2 ) coreAddress arithmetic
charclass ( -- ) regexp-cgRegular Expressions
chars ( n1 -- n2 ) coreAddress arithmetic
cilk-bye ( -- ) cilkCilk
cilk-init ( -- ) cilkCilk
cilk-sync ( -- ) cilkCilk
class ( class -- class selectors vars ) mini-oofBasic Mini-OOF Usage
class ( parent-class -- align offset ) objectsObjects Glossary
class->map ( class -- map ) objectsObjects Glossary
class-inst-size ( class -- addr ) objectsObjects Glossary
class-override! ( xt sel-xt class-map -- ) objectsObjects Glossary
class-previous ( class -- ) objectsObjects Glossary
class; ( -- ) oofClass Declaration
class>order ( class -- ) objectsObjects Glossary
clear-libs ( -- ) gforth-0.7Declaring OS-level libraries
clear-path ( path-addr -- ) gforth-0.5General Search Paths
clearstack ( ... -- ) gforth-0.2Examining data
clearstacks ( ... -- ) gforth-0.7Examining data
clicked ( rx ry bmask n -- ) minos2actor methods
close-dir ( wdirid -- wior ) gforth-0.5Directories
close-file ( wfileid -- wior ) fileGeneral files
close-pipe ( wfileid -- wretval wior ) gforth-0.2Pipes
cmove ( c-from c-to u -- ) stringMemory Blocks
cmove> ( c-from c-to u -- ) stringMemory Blocks
code ( "name" -- colon-sys ) tools-extAssembler Definitions
code-address! ( c_addr xt -- ) gforth-obsoleteThreading Words
color-cover ( -- ) gforth-1.0Code Coverage
color: ( rgba "name" -- ) minos2widget methods
common-list ( list1 list2 -- list3 ) gforth-internalLocals implementation
COMP' ( "name" -- w xt ) gforth-0.2Compilation token
compare ( c-addr1 u1 c-addr2 u2 -- n ) stringMemory Blocks
compile-lp+! ( n -- ) gforth-0.2Locals implementation
compile-only ( -- ) gforth-0.2Interpretation and Compilation Semantics
compile-only? ( nt -- flag ) gforth-1.0Header fields
compile, ( xt -- ) core-extMacros
const-does> ( run-time: w*uw r*ur uw ur "name" -- ) gforth-obsoleteConst-does>
Constant ( w "name" -- ) coreConstants
construct ( ... object -- ) objectsObjects Glossary
context ( -- addr ) gforth-0.2Word Lists
contof ( compilation case-sys1 of-sys -- case-sys2 ; run-time -- ) gforth-1.0Arbitrary control structures
convert ( ud1 c-addr1 -- ud2 c-addr2 ) core-ext-obsolescentLine input and conversion
CORE ( -- f ) environmentEnvironmental Queries
CORE-EXT ( -- f ) environmentEnvironmental Queries
cores ( -- u ) cilkCilk
count ( c-addr1 -- c-addr2 u ) coreCounted string words
Country ( <lang> "name" -- ) gforth-experimentali18n and l10n
cov% ( -- ) gforth-experimentalCode Coverage
cov+ ( -- ) gforth-experimentalCode Coverage
coverage? ( -- f ) gforth-internalCode Coverage
cputime ( -- duser dsystem ) gforth-0.5Keeping track of Time
cr ( -- ) coreMiscellaneous output
Create ( "name" -- ) coreCREATE
create-file ( c-addr u wfam -- wfileid wior ) fileGeneral files
create-from ( nt "name" -- ) gforth-1.0Creating from a prototype
critical-section ( xt semaphore -- ) gforth-experimentalSemaphores
CS-DROP ( dest -- ) gforth-1.0Arbitrary control structures
CS-PICK ( orig0/dest0 orig1/dest1 ... origu/destu u -- ... orig0/dest0 ) tools-extArbitrary control structures
CS-ROLL ( destu/origu .. dest0/orig0 u -- .. dest0/orig0 destu/origu ) tools-extArbitrary control structures
cs-vocabulary ( "name" -- ) gforth-1.0Word Lists
cs-wordlist ( -- wid ) gforth-1.0Word Lists
cstring>sstring ( c-addr -- c-addr u ) gforth-0.2String words
ctz ( x -- u ) gforth-1.0Bitwise operations
current ( -- addr ) gforth-0.2Word Lists
current-interface ( -- addr ) objectsObjects Glossary
current' ( "selector" -- xt ) objectsObjects Glossary

D
d ( -- r ) minos2widget methods
d- ( d1 d2 -- d ) doubleDouble precision
D: ( compilation "name" -- a-addr xt; run-time x1 x2 -- ) gforth-0.2Locals definition words
d. ( d -- ) doubleSimple numeric output
d.r ( d n -- ) doubleSimple numeric output
D^ ( compilation "name" -- a-addr xt; run-time x1 x2 -- ) gforth-0.2Locals definition words
d+ ( ud1 ud2 -- ud ) doubleDouble precision
d< ( d1 d2 -- f ) doubleNumeric comparison
d<= ( d1 d2 -- f ) gforth-0.2Numeric comparison
d<> ( d1 d2 -- f ) gforth-0.2Numeric comparison
d= ( d1 d2 -- f ) doubleNumeric comparison
d> ( d1 d2 -- f ) gforth-0.2Numeric comparison
d>= ( d1 d2 -- f ) gforth-0.2Numeric comparison
d>f ( d -- r ) floatingFloating Point
d>s ( d -- n ) doubleDouble precision
d0< ( d -- f ) doubleNumeric comparison
d0<= ( d -- f ) gforth-0.2Numeric comparison
d0<> ( d -- f ) gforth-0.2Numeric comparison
d0= ( d -- f ) doubleNumeric comparison
d0> ( d -- f ) gforth-0.2Numeric comparison
d0>= ( d -- f ) gforth-0.2Numeric comparison
d2* ( d1 -- d2 ) doubleBitwise operations
d2/ ( d1 -- d2 ) doubleBitwise operations
DA: ( compilation "name" -- a-addr xt; run-time x1 x2 -- ) gforth-1.0Locals definition words
dabs ( d -- ud ) doubleDouble precision
dark-mode ( -- ) gforth-1.0Terminal output
darshift ( d1 u -- d2 ) gforth-1.0Bitwise operations
dbg ( "name" -- ) gforth-0.2Singlestep Debugger
debug-fid ( -- file-id ) gforth-1.0Debugging
dec. ( n -- ) gforth-0.2Simple numeric output
dec.r ( u n -- ) gforth-0.5Simple numeric output
decimal ( -- ) coreNumber Conversion
default-color ( -- ) gforth-1.0Terminal output
default-w: ( -- ) gforth-experimentalGforth locals
default-wa: ( -- ) gforth-experimentalGforth locals
defer ( -- ) oofClass Declaration
Defer ( "name" -- ) core-extDeferred Words
defer! ( xt xt-deferred -- ) core-extDeferred Words
defer@ ( xt-deferred -- xt ) core-extDeferred Words
defers ( compilation "name" -- ; run-time ... -- ... ) gforth-0.2Deferred Words
definer! ( definer xt -- ) gforth-obsoleteThreading Words
defines ( xt class "name" -- ) mini-oofBasic Mini-OOF Usage
definitions ( -- ) searchWord Lists
defocus ( -- ) minos2actor methods
delete ( buffer size u -- ) gforth-0.7Memory Blocks
delete-file ( c-addr u -- wior ) fileGeneral files
delta-i ( r:ulimit r:u -- r:ulimit r:u u2 ) gforth-1.0Counted Loops
depth ( -- +n ) coreExamining data
df! ( r df-addr -- ) floating-extMemory Access
df@ ( df-addr -- r ) floating-extMemory Access
dfalign ( -- ) floating-extDictionary allocation
dfaligned ( c-addr -- df-addr ) floating-extAddress arithmetic
dffield: ( u1 "name" -- u2 ) floating-extForth200x Structures
dfloat/ ( n1 -- n2 ) gforth-1.0Address arithmetic
dfloat% ( -- align size ) gforth-0.4Structure Glossary
dfloat+ ( df-addr1 -- df-addr2 ) floating-extAddress arithmetic
dfloats ( n1 -- n2 ) floating-extAddress arithmetic
dglue ( -- rtyp rsub radd ) minos2widget methods
dglue@ ( -- rtyp rsub radd ) minos2widget methods
dict-new ( ... class -- object ) objectsObjects Glossary
dirname ( c-addr1 u1 -- c-addr1 u2 ) gforth-0.7Directories
discode ( addr u -- ) gforth-0.2Common Disassembler
dispose-widget ( -- ) minos2widget methods
dlshift ( ud1 u -- ud2 ) gforth-1.0Bitwise operations
dmax ( d1 d2 -- d ) doubleDouble precision
dmin ( d1 d2 -- d ) doubleDouble precision
dnegate ( d1 -- d2 ) doubleDouble precision
DO ( compilation -- do-sys ; run-time w1 w2 -- loop-sys ) coreCounted Loops
doabicode, ( -- ) gforth-1.0Threading Words
doabicode: ( -- addr ) gforth-1.0Threading Words
docol, ( -- ) gforth-1.0Threading Words
docol: ( -- addr ) gforth-0.2Threading Words
docon, ( -- ) gforth-1.0Threading Words
docon: ( -- addr ) gforth-0.2Threading Words
dodefer, ( -- ) gforth-1.0Threading Words
dodefer: ( -- addr ) gforth-0.2Threading Words
dodoes: ( -- addr ) gforth-0.6Threading Words
does-code! ( xt2 xt1 -- ) gforth-0.2Threading Words
DOES> ( compilation colon-sys1 -- colon-sys2 ) coreCREATE..DOES> details
dofield, ( -- ) gforth-1.0Threading Words
dofield: ( -- addr ) gforth-0.2Threading Words
DONE ( compilation do-sys -- ; run-time -- ) gforth-0.2Counted Loops
double% ( -- align size ) gforth-0.4Structure Glossary
douser, ( -- ) gforth-1.0Threading Words
douser: ( -- addr ) gforth-0.2Threading Words
dovalue, ( -- ) gforth-1.0Threading Words
dovalue: ( -- addr ) gforth-0.7Threading Words
dovar, ( -- ) gforth-1.0Threading Words
dovar: ( -- addr ) gforth-0.2Threading Words
dpl ( -- a-addr ) gforth-0.2Number Conversion
draw ( -- ) minos2widget methods
draw-init ( -- ) minos2widget methods
drol ( ud1 u -- ud2 ) gforth-1.0Bitwise operations
drop ( w -- ) coreData stack
dror ( ud1 u -- ud2 ) gforth-1.0Bitwise operations
drshift ( ud1 u -- ud2 ) gforth-1.0Bitwise operations
du/mod ( d u -- n u1 ) gforth-1.0Integer division
du< ( ud1 ud2 -- f ) double-extNumeric comparison
du<= ( ud1 ud2 -- f ) gforth-0.2Numeric comparison
du> ( ud1 ud2 -- f ) gforth-0.2Numeric comparison
du>= ( ud1 ud2 -- f ) gforth-0.2Numeric comparison
dump ( addr u -- ) toolsExamining data
dup ( w -- w w ) coreData stack

E
early ( -- ) oofClass Declaration
edit ( "name" -- ) gforth-1.0Locating source code definitions
edit-line ( c-addr n1 n2 -- n3 ) gforth-0.6Line input and conversion
ekey ( -- u ) facility-extSingle-key input
ekey? ( -- flag ) facility-extSingle-key input
ekey>char ( u -- u false | c true ) facility-extSingle-key input
ekey>fkey ( u1 -- u2 f ) facility-extSingle-key input
ekey>xchar ( u -- u false | xc true ) xchar-extSingle-key input
ekeyed ( ekey -- ) minos2actor methods
ELSE ( compilation orig1 -- orig2 ; run-time -- ) coreArbitrary control structures
emit ( c -- ) coreDisplaying characters and strings
emit-file ( c wfileid -- wior ) gforth-0.2General files
empty-buffer ( buffer -- ) gforth-0.2Blocks
empty-buffers ( -- ) block-extBlocks
end-c-library ( -- ) gforth-0.7Defining library interfaces
end-class ( align offset "name" -- ) objectsObjects Glossary
end-class ( class selectors vars "name" -- ) mini-oofBasic Mini-OOF Usage
end-class-noname ( align offset -- class ) objectsObjects Glossary
end-code ( colon-sys -- ) gforth-0.2Assembler Definitions
end-interface ( "name" -- ) objectsObjects Glossary
end-interface-noname ( -- interface ) objectsObjects Glossary
end-methods ( -- ) objectsObjects Glossary
end-struct ( align size "name" -- ) gforth-0.2Structure Glossary
end-structure ( struct-sys +n -- ) facility-extForth200x Structures
endcase ( compilation case-sys -- ; run-time x -- ) core-extArbitrary control structures
ENDIF ( compilation orig -- ; run-time -- ) gforth-0.2Arbitrary control structures
endof ( compilation case-sys1 of-sys -- case-sys2 ; run-time -- ) core-extArbitrary control structures
endscope ( compilation scope -- ; run-time -- ) gforth-0.2Where are locals visible by name?
endtry ( compilation -- ; run-time R:sys1 -- ) gforth-0.5Exception Handling
endtry-iferror ( compilation orig1 -- orig2 ; run-time R:sys1 -- ) gforth-0.7Exception Handling
entered ( -- ) minos2actor methods
environment ( -- ) gforth-0.6Environmental Queries
environment-wordlist ( -- wid ) gforth-0.2Environmental Queries
environment? ( c-addr u -- false / ... true ) coreEnvironmental Queries
erase ( addr u -- ) core-extMemory Blocks
error-color ( -- ) gforth-1.0Terminal output
error-hl-inv ( -- ) gforth-1.0Terminal output
error-hl-ul ( -- ) gforth-1.0Terminal output
evaluate ( ... addr u -- ... ) core,blockInput Sources
event-loop ( -- ) gforth-experimentalMessage queues
exception ( addr u -- n ) gforth-0.2Exception Handling
exceptions ( xt n1 -- n2 ) gforth-1.0Exception Handling
execute ( xt -- ) coreExecution token
execute-exit ( compilation -- ; run-time xt nest-sys -- ) gforth-1.0Execution token
execute-parsing ( ... addr u xt -- ... ) gforth-0.6The Input Stream
execute-parsing-file ( i*x fileid xt -- j*x ) gforth-0.6The Input Stream
execute-task ( xt -- task ) gforth-experimentalBasic multi-tasking
EXIT ( compilation -- ; run-time nest-sys -- ) coreCalls and returns
exitm ( -- ) objectsObjects Glossary
expand-where ( -- ) gforth-1.0Locating uses of a word
expect ( c-addr +n -- ) core-ext-obsolescentLine input and conversion
extend-mem ( addr1 u1 u -- addr addr2 u2 ) gforth-experimentalHeap Allocation
extend-structure ( n "name" -- struct-sys n ) gforth-1.0Forth200x Structures

F
f- ( r1 r2 -- r3 ) floatingFloating Point
f-rot ( r1 r2 r3 -- r3 r1 r2 ) floatingFloating point stack
f, ( f -- ) gforth-0.2Dictionary allocation
F: ( compilation "name" -- a-addr xt; run-time r -- ) gforth-0.2Locals definition words
f! ( r f-addr -- ) floatingMemory Access
f. ( r -- ) floating-extFloating-point output
f.rdp ( rf +nr +nd +np -- ) gforth-0.6Floating-point output
f.s ( -- ) gforth-0.2Examining data
f.s-precision ( -- u ) gforth-1.0Examining data
f@ ( f-addr -- r ) floatingMemory Access
f@localn ( noffset -- r ) gforth-1.0Locals implementation
f* ( r1 r2 -- r3 ) floatingFloating Point
f** ( r1 r2 -- r3 ) floating-extFloating Point
f/ ( r1 r2 -- r3 ) floatingFloating Point
F^ ( compilation "name" -- a-addr xt; run-time r -- ) gforth-0.2Locals definition words
f+ ( r1 r2 -- r3 ) floatingFloating Point
f< ( r1 r2 -- f ) floatingFloating Point
f<= ( r1 r2 -- f ) gforth-0.2Floating Point
f<> ( r1 r2 -- f ) gforth-0.2Floating Point
f= ( r1 r2 -- f ) gforth-0.2Floating Point
f> ( r1 r2 -- f ) gforth-0.2Floating Point
f>= ( r1 r2 -- f ) gforth-0.2Floating Point
f>buf-rdp ( rf c-addr +nr +nd +np -- ) gforth-0.6Floating-point output
f>d ( r -- d ) floatingFloating Point
f>l ( r -- ) gforth-0.2Locals implementation
f>s ( r -- n ) floating-extFloating Point
f>str-rdp ( rf +nr +nd +np -- c-addr nr ) gforth-0.6Floating-point output
f~ ( r1 r2 r3 -- flag ) floating-extFloating Point
f~abs ( r1 r2 r3 -- flag ) gforth-0.5Floating Point
f~rel ( r1 r2 r3 -- flag ) gforth-0.5Floating Point
f0< ( r -- f ) floatingFloating Point
f0<= ( r -- f ) gforth-0.2Floating Point
f0<> ( r -- f ) gforth-0.2Floating Point
f0= ( r -- f ) floatingFloating Point
f0> ( r -- f ) gforth-0.2Floating Point
f0>= ( r -- f ) gforth-0.2Floating Point
f2* ( r1 -- r2 ) gforth-0.2Floating Point
f2/ ( r1 -- r2 ) gforth-0.2Floating Point
FA: ( compilation "name" -- a-addr xt; run-time f -- ) gforth-1.0Locals definition words
fabs ( r1 -- r2 ) floating-extFloating Point
facos ( r1 -- r2 ) floating-extFloating Point
facosh ( r1 -- r2 ) floating-extFloating Point
fade-color: ( rgba1 rgba2 "name" -- ) minos2widget methods
falign ( -- ) floatingDictionary allocation
faligned ( c-addr -- f-addr ) floatingAddress arithmetic
falog ( r1 -- r2 ) floating-extFloating Point
false ( -- f ) core-extBoolean Flags
fasin ( r1 -- r2 ) floating-extFloating Point
fasinh ( r1 -- r2 ) floating-extFloating Point
fast-throw ( ... wball -- ... wball ) gforth-experimentalException Handling
fatan ( r1 -- r2 ) floating-extFloating Point
fatan2 ( r1 r2 -- r3 ) floating-extFloating Point
fatanh ( r1 -- r2 ) floating-extFloating Point
faxpy ( ra f-x nstridex f-y nstridey ucount -- ) gforth-0.5Floating Point
fclearstack ( r0 .. rn -- ) gforth-1.0Examining data
fconstant ( r "name" -- ) floatingConstants
fcopysign ( r1 r2 -- r3 ) gforth-1.0Floating Point
fcos ( r1 -- r2 ) floating-extFloating Point
fcosh ( r1 -- r2 ) floating-extFloating Point
fdepth ( -- +n ) floatingExamining data
fdrop ( r -- ) floatingFloating point stack
fdup ( r -- r r ) floatingFloating point stack
fe. ( r -- ) floating-extFloating-point output
fexp ( r1 -- r2 ) floating-extFloating Point
fexpm1 ( r1 -- r2 ) floating-extFloating Point
ffield: ( u1 "name" -- u2 ) floating-extForth200x Structures
ffourth ( r1 r2 r3 r4 -- r1 r2 r3 r4 r1 ) gforth-1.0Floating point stack
field ( align1 offset1 align size "name" -- align2 offset2 ) gforth-0.2Structure Glossary
field: ( u1 "name" -- u2 ) facility-extForth200x Structures
file-eof? ( wfileid -- flag ) gforth-0.6General files
file-position ( wfileid -- ud wior ) fileGeneral files
file-size ( wfileid -- ud wior ) fileGeneral files
file-status ( c-addr u -- wfam wior ) file-extGeneral files
file>fpath ( addr1 u1 -- addr2 u2 ) gforth-1.0Source Search Paths
file>path ( c-addr1 u1 path-addr -- c-addr2 u2 ) gforth-1.0General Search Paths
filename-match ( c-addr1 u1 c-addr2 u2 -- flag ) gforth-0.5Directories
fill ( c-addr u c -- ) coreMemory Blocks
find ( c-addr -- xt +-1 | c-addr 0 ) core,searchWord Lists
find-name ( c-addr u -- nt | 0 ) gforth-0.2Name token
find-name-in ( c-addr u wid -- nt | 0 ) gforth-1.0Name token
fkey. ( u -- ) gforth-1.0Single-key input
FLiteral ( compilation r -- ; run-time -- r ) floatingLiterals
fln ( r1 -- r2 ) floating-extFloating Point
flnp1 ( r1 -- r2 ) floating-extFloating Point
float ( -- u ) gforth-0.3Address arithmetic
float/ ( n1 -- n2 ) gforth-1.0Address arithmetic
float% ( -- align size ) gforth-0.4Structure Glossary
float+ ( f-addr1 -- f-addr2 ) floatingAddress arithmetic
floating-stack ( -- n ) environmentEnvironmental Queries
floats ( n1 -- n2 ) floatingAddress arithmetic
flog ( r1 -- r2 ) floating-extFloating Point
floor ( r1 -- r2 ) floatingFloating Point
FLOORED ( -- f ) environmentEnvironmental Queries
flush ( -- ) blockBlocks
flush-file ( wfileid -- wior ) file-extGeneral files
flush-icache ( c-addr u -- ) gforth-0.2Assembler Definitions
fm/mod ( d1 n1 -- n2 n3 ) coreInteger division
fmax ( r1 r2 -- r3 ) floatingFloating Point
fmin ( r1 r2 -- r3 ) floatingFloating Point
fnegate ( r1 -- r2 ) floatingFloating Point
fnip ( r1 r2 -- r2 ) gforth-0.2Floating point stack
focus ( -- ) minos2actor methods
FOR ( compilation -- do-sys ; run-time u -- loop-sys ) gforth-0.2Counted Loops
FORK ( compilation -- orig ; run-time f -- ) gforth-0.7Regular Expressions
form ( -- nlines ncols ) gforth-0.2Terminal output
Forth ( -- ) search-extWord Lists
forth-recognize ( c-addr u -- ... translate-xt ) recognizerDealing with existing Recognizers
forth-recognizer ( -- xt ) gforth-experimentalDealing with existing Recognizers
forth-wordlist ( -- wid ) searchWord Lists
forward ( "name" -- ) gforth-1.0Forward
fourth ( w1 w2 w3 w4 -- w1 w2 w3 w4 w1 ) gforth-1.0Data stack
fover ( r1 r2 -- r1 r2 r1 ) floatingFloating point stack
fp! ( f-addr -- f:... ) gforth-0.2Stack pointer manipulation
fp. ( r -- ) floating-extFloating-point output
fp@ ( f:... -- f-addr ) gforth-0.2Stack pointer manipulation
fp0 ( -- a-addr ) gforth-0.4Stack pointer manipulation
fpath ( -- path-addr ) gforth-0.4Source Search Paths
fpick ( f:... u -- f:... r ) gforth-0.4Floating point stack
free ( a-addr -- wior ) memoryHeap Allocation
free-closure ( xt -- ) gforth-internalClosures
free-mem-var ( addr -- ) gforth-experimentalHeap Allocation
frot ( r1 r2 r3 -- r2 r3 r1 ) floatingFloating point stack
fround ( r1 -- r2 ) floatingFloating Point
fs. ( r -- ) floating-extFloating-point output
fsin ( r1 -- r2 ) floating-extFloating Point
fsincos ( r1 -- r2 r3 ) floating-extFloating Point
fsinh ( r1 -- r2 ) floating-extFloating Point
fsqrt ( r1 -- r2 ) floating-extFloating Point
fswap ( r1 r2 -- r2 r1 ) floatingFloating point stack
ftan ( r1 -- r2 ) floating-extFloating Point
ftanh ( r1 -- r2 ) floating-extFloating Point
fthird ( r1 r2 r3 -- r1 r2 r3 r1 ) gforth-1.0Floating point stack
ftrunc ( r1 -- r2 ) floating-extFloating Point
ftuck ( r1 r2 -- r2 r1 r2 ) gforth-0.2Floating point stack
fvalue ( r "name" -- ) floating-extValues
fvariable ( "name" -- ) floatingVariables
fvarue ( r "name" -- ) gforth-1.0Varues

G
g ( -- ) gforth-0.7Locating source code definitions
gap ( -- r ) minos2widget methods
get ( -- something ) minos2actor methods
get-block-fid ( -- wfileid ) gforth-0.2Blocks
get-current ( -- wid ) searchWord Lists
get-dir ( c-addr1 u1 -- c-addr2 u2 ) gforth-0.7Directories
get-order ( -- widn .. wid1 n ) searchWord Lists
get-recognizers ( -- xt1 .. xtn n ) gforth-experimentalDealing with existing Recognizers
getenv ( c-addr1 u1 -- c-addr2 u2 ) gforth-0.2Passing Commands to the OS
gforth ( -- c-addr u ) gforth-environmentEnvironmental Queries
gg ( -- ) gforth-1.0Locating uses of a word

H
h ( -- r ) minos2widget methods
h. ( u -- ) gforth-1.0Simple numeric output
halt ( task -- ) gforth-experimentalBasic multi-tasking
heap-new ( ... class -- object ) objectsObjects Glossary
help ( "rest-of-line" -- ) gforth-1.0Help on Gforth
here ( -- addr ) coreDictionary allocation
hex ( -- ) core-extNumber Conversion
hex. ( u -- ) gforth-0.2Simple numeric output
hglue ( -- rtyp rsub radd ) minos2widget methods
hglue@ ( -- rtyp rsub radd ) minos2widget methods
hide ( -- ) minos2actor methods
hmcopy, ( xt -- ) gforth-experimentalThreading Words
hold ( char -- ) coreFormatted numeric output
holds ( addr u -- ) core-extFormatted numeric output
how: ( -- ) oofClass Declaration

I
i ( R:n -- R:n n ) coreCounted Loops
i' ( R:w R:w2 -- R:w R:w2 w ) gforth-0.2Counted Loops
id. ( nt -- ) gforth-0.6Name token
IF ( compilation -- orig ; run-time f -- ) coreArbitrary control structures
iferror ( compilation orig1 -- orig2 ; run-time -- ) gforth-0.7Exception Handling
immediate ( -- ) coreInterpretation and Compilation Semantics
immediate? ( nt -- flag ) gforth-1.0Header methods
implementation ( interface -- ) objectsObjects Glossary
include ( ... "file" -- ... ) file-extForth source files
include-file ( i*x wfileid -- j*x ) fileForth source files
include-locale ( "name" -- ) gforth-experimentali18n and l10n
included ( i*x c-addr u -- j*x ) fileForth source files
included-locale ( addr u -- ) gforth-experimentali18n and l10n
included? ( c-addr u -- f ) gforth-0.2Forth source files
infile-execute ( ... xt file-id -- ... ) gforth-0.7Redirection
infile-id ( -- file-id ) gforth-0.4Redirection
infinity ( -- r ) gforth-1.0Floating Point
info-color ( -- ) gforth-1.0Terminal output
init-asm ( -- ) gforth-0.2Assembler Definitions
init-buffer ( addr -- ) gforth-experimentalHeap Allocation
init-object ( ... class object -- ) objectsObjects Glossary
initiate ( xt task -- ) gforth-experimentalBasic multi-tasking
inline: ( "name" -- inline:-sys ) gforth-experimentalColon Definitions
input-color ( -- ) gforth-1.0Terminal output
insert ( string length buffer size -- ) gforth-0.7Memory Blocks
inst-value ( align1 offset1 "name" -- align2 offset2 ) objectsObjects Glossary
inst-var ( align1 offset1 align size "name" -- align2 offset2 ) objectsObjects Glossary
INT-[I] ( -- n ) gforth-1.0Interpreter Directives
interface ( -- ) objectsObjects Glossary
interpret ( ... -- ... ) gforth-0.2The Text Interpreter
interpret/compile: ( interp-xt comp-xt "name" -- ) gforth-0.2Combined words
invert ( w1 -- w2 ) coreBitwise operations
IS ( xt "name" -- ) core-extDeferred Words

J
j ( R:n R:w1 R:w2 -- n R:n R:w1 R:w2 ) coreCounted Loops
JOIN ( orig -- ) gforth-0.7Regular Expressions

K
k ( R:n R:w1 R:w2 R:w3 R:w4 -- n R:n R:w1 R:w2 R:w3 R:w4 ) gforth-0.3Counted Loops
k-alt-mask ( -- u ) facility-extSingle-key input
k-backspace ( -- u ) gforth-1.0Single-key input
k-ctrl-mask ( -- u ) facility-extSingle-key input
k-delete ( -- u ) facility-extSingle-key input
k-down ( -- u ) facility-extSingle-key input
k-end ( -- u ) facility-extSingle-key input
k-enter ( -- u ) gforth-1.0Single-key input
k-eof ( -- u ) gforth-1.0Single-key input
k-f1 ( -- u ) facility-extSingle-key input
k-f10 ( -- u ) facility-extSingle-key input
k-f11 ( -- u ) facility-extSingle-key input
k-f12 ( -- u ) facility-extSingle-key input
k-f2 ( -- u ) facility-extSingle-key input
k-f3 ( -- u ) facility-extSingle-key input
k-f4 ( -- u ) facility-extSingle-key input
k-f5 ( -- u ) facility-extSingle-key input
k-f6 ( -- u ) facility-extSingle-key input
k-f7 ( -- u ) facility-extSingle-key input
k-f8 ( -- u ) facility-extSingle-key input
k-f9 ( -- u ) facility-extSingle-key input
k-home ( -- u ) facility-extSingle-key input
k-insert ( -- u ) facility-extSingle-key input
k-left ( -- u ) facility-extSingle-key input
k-mute ( -- u ) gforth-1.0Single-key input
k-next ( -- u ) facility-extSingle-key input
k-pause ( -- u ) gforth-1.0Single-key input
k-prior ( -- u ) facility-extSingle-key input
k-right ( -- u ) facility-extSingle-key input
k-sel ( -- u ) gforth-1.0Single-key input
k-shift-mask ( -- u ) facility-extSingle-key input
k-tab ( -- u ) gforth-1.0Single-key input
k-up ( -- u ) facility-extSingle-key input
k-voldown ( -- u ) gforth-1.0Single-key input
k-volup ( -- u ) gforth-1.0Single-key input
k-winch ( -- u ) gforth-1.0Single-key input
kerning ( -- r ) minos2widget methods
key ( -- char ) coreSingle-key input
key-file ( fd -- key ) gforth-0.4General files
key-ior ( -- char|ior ) gforth-1.0Single-key input
key? ( -- flag ) facilitySingle-key input
key?-file ( wfileid -- f ) gforth-0.4General files
kill ( task -- ) gforth-experimentalBasic multi-tasking
kill-task ( -- ) gforth-experimentalBasic multi-tasking

L
l ( -- ) gforth-1.0Locating source code definitions
l, ( l -- ) gforth-1.0Dictionary allocation
l! ( w c-addr -- ) gforth-0.7Special Memory Accesses
L" ( "lsid<">" -- lsid ) gforth-experimentali18n and l10n
l@ ( c-addr -- u ) gforth-0.7Special Memory Accesses
l>s ( x -- n ) gforth-1.0Special Memory Accesses
lalign ( -- ) gforth-1.0Address arithmetic
laligned ( addr -- addr' ) gforth-1.0Address arithmetic
Language ( "name" -- ) gforth-experimentali18n and l10n
lastfit ( -- ) minos2widget methods
latest ( -- nt ) gforth-0.6Name token
latestnt ( -- nt ) gforth-1.0Name token
latestxt ( -- xt ) gforth-0.6Anonymous Definitions
lbe ( u1 -- u2 ) gforth-1.0Special Memory Accesses
LEAVE ( compilation -- ; run-time loop-sys -- ) coreCounted Loops
left ( -- ) minos2actor methods
lfield: ( u1 "name" -- u2 ) gforth-1.0Forth200x Structures
lib-error ( -- c-addr u ) gforth-0.7Low-Level C Interface Words
lib-sym ( c-addr1 u1 u2 -- u3 ) gforth-0.4Low-Level C Interface Words
license ( -- ) gforth-0.2Help on Gforth
light-mode ( -- ) gforth-1.0Terminal output
line-end-hook ( -- ) gforth-0.7Text Interpreter Hooks
list ( u -- ) block-extBlocks
list-size ( list -- u ) gforth-internalLocals implementation
Literal ( compilation n -- ; run-time -- n ) coreLiterals
ll ( -- ) gforth-1.0Locating uses of a word
lle ( u1 -- u2 ) gforth-1.0Special Memory Accesses
load ( i*x u -- j*x ) blockBlocks
load-cov ( -- ) gforth-experimentalCode Coverage
locale-csv ( "name" -- ) gforth-experimentali18n and l10n
locale-csv-out ( "name" -- ) gforth-experimentali18n and l10n
locale-file ( fid -- ) gforth-experimentali18n and l10n
locale! ( addr u lsid -- ) gforth-experimentali18n and l10n
locale@ ( lsid -- addr u ) gforth-experimentali18n and l10n
locate ( "name" -- ) gforth-1.0Locating source code definitions
lock ( semaphore -- ) gforth-experimentalSemaphores
log2 ( u -- n ) gforth-1.0Bitwise operations
LOOP ( compilation do-sys -- ; run-time loop-sys1 -- | loop-sys2 ) coreCounted Loops
lp! ( c-addr -- ) gforth-internalStack pointer manipulation
lp! ( c-addr -- ) gforth-internalLocals implementation
lp@ ( -- c-addr ) gforth-0.2Stack pointer manipulation
lp@ ( -- c-addr ) gforth-0.2Locals implementation
lp0 ( -- a-addr ) gforth-0.4Stack pointer manipulation
lrol ( u1 u -- u2 ) gforth-1.0Bitwise operations
lror ( u1 u -- u2 ) gforth-1.0Bitwise operations
lshift ( u1 u -- u2 ) coreBitwise operations
LU" ( "lsid<">" -- lsid ) gforth-experimentali18n and l10n

M
m: ( -- xt colon-sys; run-time: object -- ) objectsObjects Glossary
m* ( n1 n2 -- d ) coreMixed precision
m*/ ( d1 n2 u3 -- dquot ) doubleInteger division
m+ ( d1 n -- d2 ) doubleMixed precision
macros-wordlist ( -- wid ) gforth-experimentalSubstitute
magenta-input ( -- ) gforth-1.0Terminal output
make-latest ( nt -- ) gforth-1.0Making a word current
map-vocs ( ... xt -- ... ) gforth-1.0Word Lists
marker ( "<spaces> name" -- ) core-extForgetting words
max ( n1 n2 -- n ) coreSingle precision
MAX-CHAR ( -- u ) environmentEnvironmental Queries
MAX-D ( -- d ) environmentEnvironmental Queries
max-float ( -- r ) environmentEnvironmental Queries
MAX-N ( -- n ) environmentEnvironmental Queries
MAX-U ( -- u ) environmentEnvironmental Queries
MAX-UD ( -- ud ) environmentEnvironmental Queries
MAX-XCHAR ( -- xchar ) environmentEnvironmental Queries
maxalign ( -- ) gforth-0.2Dictionary allocation
maxaligned ( addr1 -- addr2 ) gforth-0.2Address arithmetic
maxdepth-.s ( -- addr ) gforth-0.2Examining data
mem-do ( compilation -- w xt do-sys; run-time addr ubytes +nstride -- ) gforth-experimentalCounted Loops
mem, ( addr u -- ) gforth-0.6Dictionary allocation
mem+do ( compilation -- w xt do-sys; run-time addr ubytes +nstride -- ) gforth-experimentalCounted Loops
method ( -- ) oofClass Declaration
method ( m v "name" -- m' v ) mini-oofBasic Mini-OOF Usage
method ( xt "name" -- ) objectsObjects Glossary
methods ( class -- ) objectsObjects Glossary
min ( n1 n2 -- n ) coreSingle precision
mkdir-parents ( c-addr u mode -- ior ) gforth-0.7Directories
mod ( n1 n2 -- n ) coreInteger division
modf ( n1 n2 -- n ) gforth-1.0Integer division
modf-stage2m ( n1 a-reci -- umodulus ) gforth-1.0Two-stage integer division
mods ( n1 n2 -- n ) gforth-1.0Integer division
move ( c-from c-to ucount -- ) coreMemory Blocks
ms ( n -- ) facility-extKeeping track of Time
mux ( u1 u2 u3 -- u ) gforth-1.0Bitwise operations
mwords ( ["pattern"] -- ) gforth-1.0Word Lists

N
n ( -- ) gforth-1.0Locating source code definitions
n/a ( -- ) gforth-experimentalUser-defined TO and DEFER@
n>r ( x1 .. xn n -- r:xn..x1 r:n ) tools-extReturn stack
name ( -- c-addr u ) gforth-obsoleteThe Input Stream
name>compile ( nt -- w xt ) tools-extName token
name>interpret ( nt -- xt ) tools-extName token
name>link ( nt1 -- nt2 / 0 ) gforth-1.0Name token
name>string ( nt -- addr u ) tools-extName token
name$ ( -- addr u ) minos2widget methods
NaN ( -- r ) gforth-1.0Floating Point
native@ ( lsid -- addr u ) gforth-experimentali18n and l10n
needs ( ... "name" -- ... ) gforth-0.2Forth source files
negate ( n1 -- n2 ) coreSingle precision
new ( class -- o ) mini-oofBasic Mini-OOF Usage
new-color: ( rgba "name" -- ) minos2widget methods
newline ( -- c-addr u ) gforth-0.5String and character literals
newtask ( stacksize -- task ) gforth-experimentalBasic multi-tasking
newtask4 ( u-data u-return u-fp u-locals -- task ) gforth-experimentalBasic multi-tasking
NEXT ( compilation do-sys -- ; run-time loop-sys1 -- | loop-sys2 ) gforth-0.2Counted Loops
next-arg ( -- addr u ) gforth-0.7OS command line arguments
next-case ( compilation case-sys -- ; run-time -- ) gforth-1.0Arbitrary control structures
nextname ( c-addr u -- ) gforth-0.2Supplying names
nip ( w1 w2 -- w2 ) core-extData stack
nocov[ ( -- ) gforth-1.0Code Coverage
noname ( -- ) gforth-0.2Anonymous Definitions
noname-from ( xt -- ) gforth-1.0Creating from a prototype
noop ( -- ) gforth-0.2Execution token
notfound ( state -- ) gforth-experimentalDealing with existing Recognizers
nothrow ( -- ) gforth-0.7Exception Handling
nr> ( r:xn..x1 r:n -- x1 .. xn n ) tools-extReturn stack
ns ( d -- ) gforth-1.0Keeping track of Time
nt ( -- ) gforth-1.0Locating exception source
ntime ( -- dtime ) gforth-1.0Keeping track of Time
nw ( -- ) gforth-1.0Locating uses of a word

O
o> ( r:c-addr -- ) newMini-OOF2
object ( -- a-addr ) mini-oofBasic Mini-OOF Usage
object ( -- class ) objectsObjects Glossary
object-: ( "name" -- ) oofThe OOF base class
object-:: ( "name" -- ) oofThe OOF base class
object-' ( "name" -- xt ) oofThe OOF base class
object-[] ( n "name" -- ) oofThe OOF base class
object-asptr ( o "name" -- ) oofThe OOF base class
object-bind ( o "name" -- ) oofThe OOF base class
object-bound ( class addr "name" -- ) oofThe OOF base class
object-class ( "name" -- ) oofThe OOF base class
object-class? ( o -- flag ) oofThe OOF base class
object-definitions ( -- ) oofThe OOF base class
object-dispose ( -- ) oofThe OOF base class
object-endwith ( -- ) oofThe OOF base class
object-init ( ... -- ) oofThe OOF base class
object-is ( xt "name" -- ) oofThe OOF base class
object-link ( "name" -- class addr ) oofThe OOF base class
object-new ( -- o ) oofThe OOF base class
object-new[] ( n -- o ) oofThe OOF base class
object-postpone ( "name" -- ) oofThe OOF base class
object-ptr ( "name" -- ) oofThe OOF base class
object-self ( -- o ) oofThe OOF base class
object-super ( "name" -- ) oofThe OOF base class
object-with ( o -- ) oofThe OOF base class
of ( compilation -- of-sys ; run-time x1 x2 -- |x1 ) core-extArbitrary control structures
off ( a-addr -- ) gforth-0.2Boolean Flags
on ( a-addr -- ) gforth-0.2Boolean Flags
once ( -- ) gforth-1.0Debugging
Only ( -- ) search-extWord Lists
open-blocks ( c-addr u -- ) gforth-0.2Blocks
open-dir ( c-addr u -- wdirid wior ) gforth-0.5Directories
open-file ( c-addr u wfam -- wfileid wior ) fileGeneral files
open-lib ( c-addr1 u1 -- u2 ) gforth-0.4Low-Level C Interface Words
open-path-file ( addr1 u1 path-addr -- wfileid addr2 u2 0 | ior ) gforth-0.2General Search Paths
open-pipe ( c-addr u wfam -- wfileid wior ) gforth-0.2Pipes
opt: ( compilation -- colon-sys2 ; run-time -- nest-sys ) gforth-1.0User-defined compile-comma
or ( w1 w2 -- w ) coreBitwise operations
order ( -- ) search-extWord Lists
os-class ( -- c-addr u ) gforth-environmentEnvironmental Queries
os-type ( -- c-addr u ) gforth-environmentEnvironmental Queries
out ( -- addr ) gforth-1.0Miscellaneous output
outfile-execute ( ... xt file-id -- ... ) gforth-0.7Redirection
outfile-id ( -- file-id ) gforth-0.2Redirection
over ( w1 w2 -- w1 w2 w1 ) coreData stack
overrides ( xt "selector" -- ) objectsObjects Glossary

P
pad ( -- c-addr ) core-extMemory Blocks
page ( -- ) facilityTerminal output
par-split ( rw -- ) minos2widget methods
parent-w ( -- optr ) minos2widget methods
parse ( xchar "ccc<xchar>" -- c-addr u ) core-ext,xchar-extThe Input Stream
parse-name ( "name" -- c-addr u ) core-extThe Input Stream
parse-word ( -- c-addr u ) gforth-obsoleteThe Input Stream
pass ( x1 .. xn n task -- ) gforth-experimentalBasic multi-tasking
path+ ( path-addr "dir" -- ) gforth-0.4General Search Paths
path= ( path-addr "dir1|dir2|dir3" -- ) gforth-0.4General Search Paths
pause ( -- ) gforth-experimentalBasic multi-tasking
perform ( a-addr -- ) gforth-0.2Execution token
pi ( -- r ) gforth-0.2Floating Point
pick ( S:... u -- S:... w ) core-extData stack
postpone ( "name" -- ) coreMacros
postpone, ( w xt -- ) gforth-0.2Compilation token
pow2? ( u -- f ) gforth-1.0Bitwise operations
precision ( -- u ) floating-extFloating-point output
prepend-where ( -- ) gforth-1.0Locating uses of a word
preserve ( "name" -- ) gforth-1.0Deferred Words
previous ( -- ) search-extWord Lists
print ( object -- ) objectsObjects Glossary
printdebugdata ( -- ) gforth-0.2Debugging
process-option ( addr u -- true / addr u false ) gforth-0.7Modifying the Startup Sequence
protected ( -- ) objectsObjects Glossary
ptr ( -- ) oofClass Declaration
public ( -- ) objectsObjects Glossary

Q
query ( -- ) core-ext-obsolescentInput Sources
quit ( ?? -- ?? ) coreMiscellaneous Words

R
r@ ( -- w ; R: w -- w ) coreReturn stack
r/o ( -- fam ) fileGeneral files
r/w ( -- fam ) fileGeneral files
r> ( R:w -- w ) coreReturn stack
raise ( -- r ) minos2widget methods
rdrop ( R:w -- ) gforth-0.2Return stack
re-color ( rgba "name" -- ) minos2widget methods
re-emoji-color ( rgbatext rgbaemoji "name" -- ) minos2widget methods
re-fade-color ( rgba1 rgba2 "name" -- ) minos2widget methods
re-text-color ( rgba "name" -- ) minos2widget methods
re-text-emoji-fade-color ( rgbatext1 ~2 rgbaemoji1 ~2 "name" -- ) minos2widget methods
read-csv ( addr u xt -- ) gforth-experimentalCSV Reader
read-dir ( c-addr u1 wdirid -- u2 flag wior ) gforth-0.5Directories
read-file ( c-addr u1 wfileid -- u2 wior ) fileGeneral files
read-line ( c_addr u1 wfileid -- u2 flag wior ) fileGeneral files
rec-body ( addr u -- xt translate-tick | translate-null ) gforth-experimentalDealing with existing Recognizers
rec-dtick ( addr u -- nt rectype-num | rectype-null ) gforth-experimentalDealing with existing Recognizers
rec-float ( addr u -- r translate-float | notfound ) gforth-experimentalDealing with existing Recognizers
rec-moof2 ( addr u -- xt translate-moof2 | notfound ) mini-oof2Mini-OOF2
rec-nt ( addr u -- nt translate-nt | notfound ) gforth-experimentalDealing with existing Recognizers
rec-num ( addr u -- n/d table | notfound ) gforth-experimentalDealing with existing Recognizers
rec-string ( addr u -- addr u' r:string | rectype-null ) gforth-experimentalDealing with existing Recognizers
rec-tick ( addr u -- xt rectype-num | rectype-null ) gforth-experimentalDealing with existing Recognizers
rec-to ( addr u -- xt n r:to | rectype-null ) gforth-experimentalDealing with existing Recognizers
recognize ( addr u rec-addr -- ... rectype ) gforth-experimentalDealing with existing Recognizers
recognizer-sequence: ( xt1 .. xtn n "name" -- ) gforth-experimentalDealing with existing Recognizers
recurse ( ... -- ... ) coreCalls and returns
recursive ( compilation -- ; run-time -- ) gforth-0.2Calls and returns
refill ( -- flag ) core-ext,block-ext,file-extThe Input Stream
rename-file ( c-addr1 u1 c-addr2 u2 -- wior ) file-extGeneral files
REPEAT ( compilation orig dest -- ; run-time -- ) coreArbitrary control structures
replace-word ( xt1 xt2 -- ) gforth-1.0Debugging
replaces ( addr1 len1 addr2 len2 -- ) string-extSubstitute
reposition-file ( ud wfileid -- wior ) fileGeneral files
represent ( r c-addr u -- n f1 f2 ) floatingFloating-point output
require ( ... "file" -- ... ) file-extForth source files
required ( i*x addr u -- i*x ) file-extForth source files
resize ( a-addr1 u -- a-addr2 wior ) memoryHeap Allocation
resize-file ( ud wfileid -- wior ) fileGeneral files
resized ( -- ) minos2widget methods
restart ( task -- ) gforth-experimentalBasic multi-tasking
restore ( compilation orig1 -- ; run-time -- ) gforth-0.7Exception Handling
restore-input ( x1 .. xn n -- flag ) core-extInput Sources
restrict ( -- ) gforth-0.2Interpretation and Compilation Semantics
return-stack-cells ( -- n ) environmentEnvironmental Queries
reveal ( -- ) gforth-0.2Creating from a prototype
rol ( u1 u -- u2 ) gforth-1.0Bitwise operations
roll ( x0 x1 .. xn n -- x1 .. xn x0 ) core-extData stack
Root ( -- ) gforth-0.2Word Lists
ror ( u1 u -- u2 ) gforth-1.0Bitwise operations
rot ( w1 w2 w3 -- w2 w3 w1 ) coreData stack
rp! ( a-addr -- ) gforth-0.2Stack pointer manipulation
rp@ ( -- a-addr ) gforth-0.2Stack pointer manipulation
rp0 ( -- a-addr ) gforth-0.4Stack pointer manipulation
rshift ( u1 u -- u2 ) coreBitwise operations

S
S" ( compilation 'ccc"' -- ; run-time -- c-addr u ) core,fileString and character literals
s// ( addr u -- ptr ) regexp-replaceRegular Expressions
s\" ( compilation 'ccc"' -- ; run-time -- c-addr u ) core-ext,file-extString and character literals
s+ ( c-addr1 u1 c-addr2 u2 -- c-addr u ) gforth-0.7String words
s>> ( addr -- addr ) regexp-replaceRegular Expressions
s>d ( n -- d ) coreDouble precision
s>f ( n -- r ) floating-extFloating Point
s>number? ( addr u -- d f ) gforth-0.5Line input and conversion
s>unumber? ( c-addr u -- ud flag ) gforth-0.5Line input and conversion
safe/string ( c-addr1 u1 n -- c-addr2 u2 ) gforth-1.0String words
save-buffer ( buffer -- ) gforth-0.2Blocks
save-buffers ( -- ) blockBlocks
save-cov ( -- ) gforth-experimentalCode Coverage
save-input ( -- x1 .. xn n ) core-extInput Sources
save-mem ( addr1 u -- addr2 u ) gforth-0.2Heap Allocation
savesystem ( "image" -- ) gforth-0.2Non-Relocatable Image Files
scan ( c-addr1 u1 c -- c-addr2 u2 ) gforth-0.2String words
scan-back ( c-addr u1 c -- c-addr u2 ) gforth-0.7String words
scope ( compilation -- scope ; run-time -- ) gforth-0.2Where are locals visible by name?
scr ( -- a-addr ) block-extBlocks
scrolled ( axis dir -- ) minos2actor methods
seal ( -- ) gforth-0.2Word Lists
search ( c-addr1 u1 c-addr2 u2 -- c-addr3 u3 flag ) stringString words
search-wordlist ( c-addr count wid -- 0 | xt +-1 ) searchWord Lists
see ( "<spaces>name" -- ) toolsExamining compiled code
see-code ( "name" -- ) gforth-0.7Examining compiled code
see-code-range ( addr1 addr2 -- ) gforth-0.7Examining compiled code
select ( u1 u2 f -- u ) gforth-1.0Boolean Flags
selector ( "name" -- ) objectsObjects Glossary
semaphore ( "name" -- ) gforth-experimentalSemaphores
send-event ( xt task -- ) gforth-experimentalMessage queues
set ( something -- ) minos2actor methods
set->comp ( xt -- ) gforth-1.0Header methods
set->int ( xt -- ) gforth-1.0Header methods
set-current ( wid -- ) searchWord Lists
set-dir ( c-addr u -- wior ) gforth-0.7Directories
set-does> ( xt -- ) gforth-1.0CREATE..DOES> details
set-execute ( ca -- ) gforth-1.0Header methods
set-forth-recognize ( xt -- ) recognizerDealing with existing Recognizers
set-name>link ( xt -- ) gforth-1.0Header methods
set-name>string ( xt -- ) gforth-1.0Header methods
set-optimizer ( xt -- ) gforth-1.0User-defined compile-comma
set-order ( widn .. wid1 n -- ) searchWord Lists
set-precision ( u -- ) floating-extFloating-point output
set-recognizers ( xt1 .. xtn n -- ) gforth-experimentalDealing with existing Recognizers
set-to ( to-xt -- ) gforth-1.0User-defined TO and DEFER@
sf! ( r sf-addr -- ) floating-extMemory Access
sf@ ( sf-addr -- r ) floating-extMemory Access
sfalign ( -- ) floating-extDictionary allocation
sfaligned ( c-addr -- sf-addr ) floating-extAddress arithmetic
sffield: ( u1 "name" -- u2 ) floating-extForth200x Structures
sfloat/ ( n1 -- n2 ) gforth-1.0Address arithmetic
sfloat% ( -- align size ) gforth-0.4Structure Glossary
sfloat+ ( sf-addr1 -- sf-addr2 ) floating-extAddress arithmetic
sfloats ( n1 -- n2 ) floating-extAddress arithmetic
sh ( "..." -- ) gforth-0.2Passing Commands to the OS
sh-get ( c-addr u -- c-addr2 u2 ) gforth-1.0Passing Commands to the OS
shift-args ( -- ) gforth-0.7OS command line arguments
short-where ( -- ) gforth-1.0Locating uses of a word
show ( -- ) minos2actor methods
show-you ( -- ) minos2actor methods
sign ( n -- ) coreFormatted numeric output
simple-fkey-string ( u1 -- c-addr u ) gforth-1.0Single-key input
simple-see ( "name" -- ) gforth-0.6Examining compiled code
simple-see-range ( addr1 addr2 -- ) gforth-0.6Examining compiled code
skip ( c-addr1 u1 c -- c-addr2 u2 ) gforth-0.2String words
SLiteral ( Compilation c-addr1 u ; run-time -- c-addr2 u ) stringLiterals
slurp-fid ( fid -- addr u ) gforth-0.6General files
slurp-file ( c-addr1 u1 -- c-addr2 u2 ) gforth-0.6General files
sm/rem ( d1 n1 -- n2 n3 ) coreInteger division
source ( -- addr u ) coreThe Text Interpreter
source-id ( -- 0 | -1 | fileid ) core-ext,fileInput Sources
sourcefilename ( -- c-addr u ) gforth-0.2Forth source files
sourceline# ( -- u ) gforth-0.2Forth source files
sp! ( a-addr -- S:... ) gforth-0.2Stack pointer manipulation
sp@ ( S:... -- a-addr ) gforth-0.2Stack pointer manipulation
sp0 ( -- a-addr ) gforth-0.4Stack pointer manipulation
space ( -- ) coreMiscellaneous output
spaces ( u -- ) coreMiscellaneous output
span ( -- c-addr ) core-ext-obsolescentLine input and conversion
spawn ( xt -- ) cilkCilk
spawn1 ( x xt -- ) cilkCilk
spawn2 ( x1 x2 xt -- ) cilkCilk
split ( firstflag rstart1 rx -- o rstart2 ) minos2widget methods
stack-cells ( -- n ) environmentEnvironmental Queries
stacksize ( -- u ) gforth-experimentalBasic multi-tasking
stacksize4 ( -- u-data u-return u-fp u-locals ) gforth-experimentalBasic multi-tasking
staged/-divisor ( addr1 -- addr2 ) gforth-1.0Two-stage integer division
staged/-size ( -- u ) gforth-1.0Two-stage integer division
static ( -- ) oofClass Declaration
status-color ( -- ) gforth-1.0Terminal output
stderr ( -- wfileid ) gforth-0.2General files
stdin ( -- wfileid ) gforth-0.4General files
stdout ( -- wfileid ) gforth-0.2General files
stop ( -- ) gforth-experimentalBasic multi-tasking
stop-ns ( timeout -- ) gforth-experimentalBasic multi-tasking
str< ( c-addr1 u1 c-addr2 u2 -- f ) gforth-0.6String words
str= ( c-addr1 u1 c-addr2 u2 -- f ) gforth-0.6String words
str=? ( addr1 addr u -- addr2 ) regexp-patternRegular Expressions
string-parse ( c-addr1 u1 "ccc<string>" -- c-addr2 u2 ) gforth-1.0The Input Stream
string-prefix? ( c-addr1 u1 c-addr2 u2 -- f ) gforth-0.6String words
string-suffix? ( c-addr1 u1 c-addr2 u2 -- f ) gforth-1.0String words
string, ( c-addr u -- ) gforth-0.2Counted string words
struct ( -- align size ) gforth-0.2Structure Glossary
sub-list? ( list1 list2 -- f ) gforth-internalLocals implementation
substitute ( addr1 len1 addr2 len2 -- addr2 len3 n/ior ) string-extSubstitute
success-color ( -- ) gforth-1.0Terminal output
swap ( w1 w2 -- w2 w1 ) coreData stack
Synonym ( "name" "oldname" -- ) tools-extAliases
system ( c-addr u -- ) gforth-0.2Passing Commands to the OS

T
table ( -- wid ) gforth-0.2Word Lists
task ( ustacksize "name" -- ) gforth-experimentalBasic multi-tasking
text-color: ( rgba "name" -- ) minos2widget methods
text-emoji-color: ( rgbatext rgbaemoji "name" -- ) minos2widget methods
text-emoji-fade-color: ( rgbatext1 ~2 rgbaemoji1 ~2 "name" -- ) minos2widget methods
THEN ( compilation orig -- ; run-time -- ) coreArbitrary control structures
third ( w1 w2 w3 -- w1 w2 w3 w1 ) gforth-1.0Data stack
this ( -- object ) objectsObjects Glossary
threading-method ( -- n ) gforth-0.2Threading Words
throw ( y1 .. ym nerror -- y1 .. ym / z1 .. zn error ) exceptionException Handling
thru ( i*x n1 n2 -- j*x ) block-extBlocks
tib ( -- addr ) core-ext-obsolescentThe Text Interpreter
time&date ( -- nsec nmin nhour nday nmonth nyear ) facility-extKeeping track of Time
TO ( value "name" -- ) core-extValues
to-method: ( xt table "name" -- ) gforth-experimentalUser-defined TO and DEFER@
to-table: ( "name" "xt1" .. "xtn" -- ) gforth-experimentalUser-defined TO and DEFER@
to-this ( object -- ) objectsObjects Glossary
touchdown ( $rxy*n bmask -- ) minos2actor methods
touchup ( $rxy*n bmask -- ) minos2actor methods
toupper ( c1 -- c2 ) gforth-0.2Characters
translate-dnum ( dx -- | dx ) gforth-experimentalDealing with existing Recognizers
translate-method: ( "name" -- ) gforth-experimentalDealing with existing Recognizers
translate-nt ( i*x nt -- j*x ) gforth-experimentalDealing with existing Recognizers
translate-num ( x -- | x ) gforth-experimentalDealing with existing Recognizers
translate-state ( xt -- ) gforth-experimentalDealing with existing Recognizers
translate: ( int-xt comp-xt post-xt "name" -- ) gforth-experimentalDealing with existing Recognizers
traverse-wordlist ( ... xt wid -- ... ) tools-extName token
true ( -- f ) core-extBoolean Flags
try ( compilation -- orig ; run-time -- R:sys1 ) gforth-0.5Exception Handling
try-recognize ( addr u xt -- results | false ) gforth-experimentalDealing with existing Recognizers
tt ( u -- ) gforth-1.0Locating exception source
tuck ( w1 w2 -- w2 w1 w2 ) core-extData stack
type ( c-addr u -- ) coreDisplaying characters and strings
typewhite ( addr n -- ) gforth-0.2Displaying characters and strings

U
u-[do ( compilation -- do-sys ; run-time u1 u2 -- | loop-sys ) gforth-experimentalCounted Loops
U-DO ( compilation -- do-sys ; run-time u1 u2 -- | loop-sys ) gforth-0.2Counted Loops
u. ( u -- ) coreSimple numeric output
u.r ( u n -- ) core-extSimple numeric output
u*/ ( u1 u2 u3 -- u4 ) gforth-1.0Integer division
u*/mod ( u1 u2 u3 -- u4 u5 ) gforth-1.0Integer division
u/ ( u1 u2 -- u ) gforth-1.0Integer division
u/-stage1m ( u addr-reci -- ) gforth-1.0Two-stage integer division
u/-stage2m ( u1 a-reci -- uquotient ) gforth-1.0Two-stage integer division
u/mod ( u1 u2 -- u3 u4 ) gforth-1.0Integer division
u/mod-stage2m ( u1 a-reci -- umodulus uquotient ) gforth-1.0Two-stage integer division
U+DO ( compilation -- do-sys ; run-time u1 u2 -- | loop-sys ) gforth-0.2Counted Loops
u< ( u1 u2 -- f ) coreNumeric comparison
u<= ( u1 u2 -- f ) gforth-0.2Numeric comparison
u> ( u1 u2 -- f ) core-extNumeric comparison
u>= ( u1 u2 -- f ) gforth-0.2Numeric comparison
uallot ( n1 -- n2 ) gforth-0.3Task-local data
ud. ( ud -- ) gforth-0.2Simple numeric output
ud.r ( ud n -- ) gforth-0.2Simple numeric output
ud/mod ( ud1 u2 -- urem udquot ) gforth-0.2Integer division
UDefer ( "name" -- ) gforth-1.0Task-local data
ukeyed ( addr u -- ) minos2actor methods
um* ( u1 u2 -- ud ) coreMixed precision
um/mod ( ud u1 -- u2 u3 ) coreInteger division
umax ( u1 u2 -- u ) gforth-1.0Single precision
umin ( u1 u2 -- u ) gforth-0.5Single precision
umod ( u1 u2 -- u ) gforth-1.0Integer division
umod-stage2m ( u1 a-reci -- umodulus ) gforth-1.0Two-stage integer division
uncolored-mode ( -- ) gforth-1.0Terminal output
under+ ( n1 n2 n3 -- n n2 ) gforth-0.3Single precision
unescape ( addr1 u1 dest -- dest u2 ) string-extSubstitute
unlock ( semaphore -- ) gforth-experimentalSemaphores
unloop ( R:w1 R:w2 -- ) coreCounted Loops
UNREACHABLE ( -- ) gforth-0.2Where are locals visible by name?
UNTIL ( compilation dest -- ; run-time f -- ) coreArbitrary control structures
unused ( -- u ) core-extDictionary allocation
unused-words ( -- ) gforth-1.0Locating uses of a word
up@ ( -- a-addr ) newTask-local data
update ( -- ) blockBlocks
updated? ( n -- f ) gforth-0.2Blocks
use ( "file" -- ) gforth-0.2Blocks
User ( "name" -- ) gforth-0.2Task-local data
user' ( "name" -- u ) gforth-experimentalTask-local data
utime ( -- dtime ) gforth-0.5Keeping track of Time
UValue ( "name" -- ) gforth-1.0Task-local data

V
v* ( f-addr1 nstride1 f-addr2 nstride2 ucount -- r ) gforth-0.5Floating Point
Value ( w "name" -- ) core-extValues
var ( m v size "name" -- m v' ) mini-oofBasic Mini-OOF Usage
var ( size -- ) oofClass Declaration
Variable ( "name" -- ) coreVariables
Varue ( w "name" -- ) gforth-1.0Varues
vglue ( -- rtyp rsub radd ) minos2widget methods
vglue@ ( -- rtyp rsub radd ) minos2widget methods
vlist ( -- ) gforth-0.2Word Lists
Vocabulary ( "name" -- ) gforth-0.2Word Lists
vocs ( -- ) gforth-0.2Word Lists
vp-bottom ( o:vp -- ) minos2widget methods
vp-left ( o:vp -- ) minos2widget methods
vp-needed ( xt -- ) minos2widget methods
vp-reslide ( o:vp -- ) minos2widget methods
vp-right ( o:vp -- ) minos2widget methods
vp-top ( o:vp -- ) minos2widget methods

W
w ( -- r ) minos2widget methods
w-color ( -- r ) minos2widget methods
w, ( w -- ) gforth-1.0Dictionary allocation
W: ( compilation "name" -- a-addr xt; run-time x -- ) gforth-0.2Locals definition words
w! ( w c-addr -- ) gforth-0.7Special Memory Accesses
w@ ( c-addr -- u ) gforth-0.5Special Memory Accesses
w/o ( -- fam ) fileGeneral files
W^ ( compilation "name" -- a-addr xt; run-time x -- ) gforth-0.2Locals definition words
w>s ( x -- n ) gforth-1.0Special Memory Accesses
WA: ( compilation "name" -- a-addr xt; run-time x -- ) gforth-1.0Locals definition words
walign ( -- ) gforth-1.0Address arithmetic
waligned ( addr -- addr' ) gforth-1.0Address arithmetic
warning-color ( -- ) gforth-1.0Terminal output
WARNING" ( compilation 'ccc"' -- ; run-time f -- ) gforth-1.0Exception Handling
warnings ( -- addr ) gforth-0.2Exception Handling
wbe ( u1 -- u2 ) gforth-1.0Special Memory Accesses
wfield: ( u1 "name" -- u2 ) gforth-1.0Forth200x Structures
where ( "name" -- ) gforth-1.0Locating uses of a word
whereg ( "name" -- ) gforth-1.0Locating uses of a word
WHILE ( compilation dest -- orig dest ; run-time f -- ) coreArbitrary control structures
widget ( -- class ) minos2MINOS2 object framework
within ( u1 u2 u3 -- f ) core-extNumeric comparison
wle ( u1 -- u2 ) gforth-1.0Special Memory Accesses
word ( char "<chars>ccc<char>-- c-addr ) coreThe Input Stream
wordlist ( -- wid ) searchWord Lists
wordlist-words ( wid -- ) gforth-0.6Word Lists
wordlists ( -- n ) environmentEnvironmental Queries
words ( -- ) toolsWord Lists
wrap-xt ( xt1 xt2 xt: xt3 -- ... ) gforth-1.0Deferred Words
write-file ( c-addr u1 wfileid -- wior ) fileGeneral files
write-line ( c-addr u wfileid -- ior ) fileGeneral files
wrol ( u1 u -- u2 ) gforth-1.0Bitwise operations
wror ( u1 u -- u2 ) gforth-1.0Bitwise operations
WTF?? ( -- ) gforth-1.0Debugging
ww ( u -- ) gforth-1.0Locating uses of a word

X
x ( -- r ) minos2widget methods
x-size ( xc-addr u1 -- u2 ) xcharXchars and Unicode
x-width ( xc-addr u -- n ) xchar-extXchars and Unicode
x, ( x -- ) gforth-1.0Dictionary allocation
x! ( w c-addr -- ) gforth-1.0Special Memory Accesses
x@ ( c-addr -- u ) gforth-1.0Special Memory Accesses
x\string- ( xc-addr u1 -- xc-addr u2 ) xchar-extXchars and Unicode
x>s ( x -- n ) gforth-1.0Special Memory Accesses
xalign ( -- ) gforth-1.0Address arithmetic
xaligned ( addr -- addr' ) gforth-1.0Address arithmetic
xbe ( u1 -- u2 ) gforth-1.0Special Memory Accesses
xc-size ( xc -- u ) xcharXchars and Unicode
xc-width ( xc -- n ) xchar-extXchars and Unicode
xc, ( xchar -- ) xcharXchars and Unicode
xc!+ ( xc xc-addr1 -- xc-addr2 ) xcharXchars and Unicode
xc!+? ( xc xc-addr1 u1 -- xc-addr2 u2 f ) xcharXchars and Unicode
xc@ ( xc-addr -- xc ) xchar-extXchars and Unicode
xc@+ ( xc-addr1 -- xc-addr2 xc ) xcharXchars and Unicode
xc@+? ( xc-addr1 u1 -- xc-addr2 u2 xc ) gforth-experimentalXchars and Unicode
xchar- ( xc-addr1 -- xc-addr2 ) xchar-extXchars and Unicode
XCHAR-ENCODING ( -- addr u ) environmentEnvironmental Queries
XCHAR-MAXMEM ( -- u ) environmentEnvironmental Queries
xchar+ ( xc-addr1 -- xc-addr2 ) xcharXchars and Unicode
xd, ( xd -- ) gforth-1.0Dictionary allocation
xd! ( ud c-addr -- ) gforth-1.0Special Memory Accesses
xd@ ( c-addr -- ud ) gforth-1.0Special Memory Accesses
xd>s ( xd -- d ) gforth-1.0Special Memory Accesses
xdbe ( ud1 -- ud2 ) gforth-1.0Special Memory Accesses
xdle ( ud1 -- ud2 ) gforth-1.0Special Memory Accesses
xemit ( xc -- ) xcharDisplaying characters and strings
xfield: ( u1 "name" -- u2 ) gforth-1.0Forth200x Structures
xhold ( xc -- ) xchar-extXchars and Unicode
xkey ( -- xc ) xcharXchars and Unicode
xkey? ( -- flag ) xcharSingle-key input
xle ( u1 -- u2 ) gforth-1.0Special Memory Accesses
xor ( w1 w2 -- w ) coreBitwise operations
xt-new ( ... class xt -- object ) objectsObjects Glossary
xt-see ( xt -- ) gforth-0.2Examining compiled code
xt-see-code ( xt -- ) gforth-1.0Examining compiled code
xt-simple-see ( xt -- ) gforth-1.0Examining compiled code
XT: ( compilation "name" -- a-addr xt; run-time xt1 -- ) gforth-1.0Locals definition words
xt>name ( xt -- nt ) gforth-1.0Name token
XTA: ( compilation "name" -- a-addr xt; run-time ... -- ... ) gforth-1.0Locals definition words
xywh ( -- rx0 ry0 rw rh ) minos2widget methods
xywhd ( -- rx ry rw rh rd ) minos2widget methods

Y
y ( -- r ) minos2widget methods


Concept and Word Index

この索引にリストされているすべての項目が本文中にそのまま記載されているわけではありません。 このインデックスは、 ワード索引にリストされているすべてのワードを省略形式で転載しています(ここではワードの名前のみがリストされています)。

Jump to:   -   ,   ;   :   !   ?   .   '   "   (   )   [   ]   {   }   @   *   /   \   &   #   %   `   +   <   =   >   |   ~   $   0   1   2  
A   B   C   D   E   F   G   H   I   J   K   L   M   N   O   P   Q   R   S   T   U   V   W   X   Y   Z                
Index EntrySection

-
-Single precision
--Locals definition words
-->Blocks
–, tutorialStack-Effect Comments Tutorial
-[doCounted Loops
-\dRegular Expressions
-\sRegular Expressions
-`Regular Expressions
->hereDictionary allocation
–appl-image, command-line optionInvoking Gforth
–application, gforthmi optiongforthmi
-c?Regular Expressions
-charRegular Expressions
-classRegular Expressions
–clear-dictionary, command-line optionInvoking Gforth
-d, command-line optionInvoking Gforth
–data-stack-size, command-line optionInvoking Gforth
–debug, command-line optionInvoking Gforth
-DFORCE_REGPortability
–dictionary-size, command-line optionInvoking Gforth
–die-on-signal, command-line-optionInvoking Gforth
-DOCounted Loops
-DUSE_FTOSTOS Optimization
-DUSE_NO_FTOSTOS Optimization
-DUSE_NO_TOSTOS Optimization
-DUSE_TOSTOS Optimization
–dynamic command-line optionDynamic Superinstructions
–dynamic, command-line optionInvoking Gforth
–enable-force-reg, configuration flagPortability
-f, command-line optionInvoking Gforth
–fp-stack-size, command-line optionInvoking Gforth
-h, command-line optionInvoking Gforth
–help, command-line optionInvoking Gforth
-i, command-line optionInvoking Gforth
-i, invoke image fileRunning Image Files
–image file, invoke image fileRunning Image Files
–image-file, command-line optionInvoking Gforth
-infinityFloating Point
-l, command-line optionInvoking Gforth
–locals-stack-size, command-line optionInvoking Gforth
-LOOPCounted Loops
-ltraceDebugging
-m, command-line optionInvoking Gforth
–no-dynamic command-line optionDynamic Superinstructions
–no-dynamic, command-line optionInvoking Gforth
–no-offset-im, command-line optionInvoking Gforth
–no-super command-line optionDynamic Superinstructions
–no-super, command-line optionInvoking Gforth
–offset-image, command-line optionInvoking Gforth
-p, command-line optionInvoking Gforth
–path, command-line optionInvoking Gforth
–print-metrics, command-line optionInvoking Gforth
-r, command-line optionInvoking Gforth
–return-stack-size, command-line optionInvoking Gforth
-rotData stack
–ss-greedy, command-line optionInvoking Gforth
–ss-min-..., command-line optionsInvoking Gforth
–ss-number, command-line optionInvoking Gforth
-trailingString words
-trailing-garbageXchars and Unicode
-v, command-line optionInvoking Gforth
–version, command-line optionInvoking Gforth
–vm-commit, command-line optionInvoking Gforth
-W, command-line optionInvoking Gforth
-Wall, command-line optionInvoking Gforth
-Werror, command-line optionInvoking Gforth
-Won, command-line optionInvoking Gforth
-Wpedantic, command-line optionInvoking Gforth

,
,Dictionary allocation

;
;Colon Definitions
;]Quotations
;>Closures
;abi-codeAssembler Definitions
;codeAssembler Definitions
;CODE ending sequenceprogramming-idef
;CODE, name not defined via CREATEprogramming-ambcond
;CODE, processing inputprogramming-idef
;inlineColon Definitions
;mObjects Glossary
;m usageMethod conveniences
;sCalls and returns

:
:Colon Definitions
:, passing data acrossLiterals
::Basic Mini-OOF Usage
:}Locals definition words
:}dClosures
:}hClosures
:}h1Closures
:}lClosures
:}xtClosures
:mObjects Glossary
:nonameAnonymous Definitions

!
!Memory Access
!!FIXME!!Debugging
!@Hardware operations for multi-tasking
!resizewidget methods
!sizewidget methods

?
?Examining data
?!@Hardware operations for multi-tasking
???Debugging
?cov+Code Coverage
?DOCounted Loops
?dupData stack
?DUP-0=-IFArbitrary control structures
?dup-IFArbitrary control structures
?eventsMessage queues
?EXITCalls and returns
?insideactor methods
?LEAVECounted Loops
?ofArbitrary control structures

.
.Simple numeric output
.?Regular Expressions
...Examining data
..charRegular Expressions
."Miscellaneous output
.", how it worksHow does that work?
.(Miscellaneous output
.\"Miscellaneous output
.cover-rawCode Coverage
.coverageCode Coverage
.debuglineDebugging
.emacsInstalling gforth.el
.fi filesImage Files
.fpathSource Search Paths
.gforth-historyCommand-line editing
.hmHeader methods
.idName token
.includedForth source files
.locale-csvi18n and l10n
.pathGeneral Search Paths
.rSimple numeric output
.recognizersDefault Recognizers
.sExamining data
.substituteSubstitute
.unresolvedForward
.vocWord Lists
.widgetwidget methods

'
'Execution token
’-prefix for characters/code pointsLiterals in source code
'coldModifying the Startup Sequence
'sTask-local data

"
", stack item typeNotation

(
(Comments
((Regular Expressions
(local)Standard Forth locals
(to)User-defined TO and DEFER@

)
)Assertions
))Regular Expressions

[
[Literals
[:Quotations
[?DO]Interpreter Directives
[']Execution token
[{:Closures
[+LOOP]Interpreter Directives
[AGAIN]Interpreter Directives
[BEGIN]Interpreter Directives
[bind]Objects Glossary
[bind] usageClass Binding
[char]String and character literals
[COMP']Compilation token
[compile]Macros
[current]Objects Glossary
[defined]Interpreter Directives
[DO]Interpreter Directives
[ELSE]Interpreter Directives
[ENDIF]Interpreter Directives
[FOR]Interpreter Directives
[I]Interpreter Directives
[IF]Interpreter Directives
[IF] and POSTPONEprogramming-ambcond
[IF], end of the input source before matching [ELSE] or [THEN]programming-ambcond
[IFDEF]Interpreter Directives
[IFUNDEF]Interpreter Directives
[LOOP]Interpreter Directives
[NEXT]Interpreter Directives
[parent]Objects Glossary
[parent] usageClass Binding
[REPEAT]Interpreter Directives
[THEN]Interpreter Directives
[to-inst]Objects Glossary
[undefined]Interpreter Directives
[UNTIL]Interpreter Directives
[WHILE]Interpreter Directives

]
]Literals
]]Macros
]LLiterals
]nocovCode Coverage

{
{Locals definition words
{:Locals definition words
{{Regular Expressions
{*Regular Expressions
{**Regular Expressions
{+Regular Expressions
{++Regular Expressions

}
}Locals definition words
}}Regular Expressions

@
@Memory Access
@localnLocals implementation

*
*Single precision
*}Regular Expressions
**}Regular Expressions
*/Integer division
*/fInteger division
*/modInteger division
*/modfInteger division
*/modsInteger division
*/sInteger division
*alignAddress arithmetic
*alignedAddress arithmetic

/
/Integer division
//Regular Expressions
//gRegular Expressions
//oRegular Expressions
//sRegular Expressions
/COUNTED-STRINGEnvironmental Queries
/fInteger division
/f-stage1mTwo-stage integer division
/f-stage2mTwo-stage integer division
/HOLDEnvironmental Queries
/lAddress arithmetic
/modInteger division
/modfInteger division
/modf-stage2mTwo-stage integer division
/modsInteger division
/PADEnvironmental Queries
/sInteger division
/stringString words
/wAddress arithmetic
/xAddress arithmetic

\
\Comments
\, editing with EmacsEmacs and Gforth
\, line length in blocksblock-idef
\(Regular Expressions
\)Regular Expressions
\\\Forth source files
\^Regular Expressions
\$Regular Expressions
\0Regular Expressions
\cDeclaring C Functions
\dRegular Expressions
\GComments
\sRegular Expressions

&
&-prefix 10新数値用(非標準)Literals in source code

#
#Formatted numeric output
#-prefix 10進数値用Literals in source code
#!Running Image Files
#>Formatted numeric output
#>>Formatted numeric output
#bellString and character literals
#bsString and character literals
#crString and character literals
#delString and character literals
#eofString and character literals
#escString and character literals
#ffString and character literals
#lfString and character literals
#lineInterpreter Directives
#locDebugging
#localsEnvironmental Queries
#sFormatted numeric output
#tabString and character literals
#tibThe Text Interpreter

%
%-prefix 2進数値用Literals in source code
%alignStructure Glossary
%alignmentStructure Glossary
%allocStructure Glossary
%allocateStructure Glossary
%allotStructure Glossary
%sizeStructure Glossary

`
`Regular Expressions
` prefixExecution token
` prefix of wordLiterals in source code
`?Regular Expressions
`` prefix of wordLiterals in source code

+
+Single precision
+!Memory Access
+!@Hardware operations for multi-tasking
+}Regular Expressions
++}Regular Expressions
+charRegular Expressions
+charsRegular Expressions
+classRegular Expressions
+DOCounted Loops
+fieldForth200x Structures
+fmodeGeneral files
+loadBlocks
+LOOPCounted Loops
+ltraceDebugging
+thruBlocks
+TOValues
+x/stringXchars and Unicode

<
<Numeric comparison
<{:Closures
<#Formatted numeric output
<<Regular Expressions
<<"Regular Expressions
<<#Formatted numeric output
<=Numeric comparison
<>Numeric comparison
<bind>Objects Glossary
<to-inst>Objects Glossary

=
=Numeric comparison
=mkdirDirectories

>
>Numeric comparison
>=Numeric comparison
>>Regular Expressions
>addrClosures
>animatewidget methods
>bodyCREATE..DOES> details
>BODY of non-CREATEd wordscore-ambcond
>code-addressThreading Words
>compileDealing with existing Recognizers
>definerThreading Words
>does-codeThreading Words
>floatLine input and conversion
>float1Line input and conversion
>inThe Text Interpreter
>IN greater than input buffercore-ambcond
>interpretDealing with existing Recognizers
>lLocals implementation
>nameName token
>numberLine input and conversion
>oMini-OOF2
>orderWord Lists
>postponeDealing with existing Recognizers
>pow2Bitwise operations
>rReturn stack
>string-executeString words
>time&date&tzKeeping track of Time

|
|Locals definition words
||Regular Expressions

~
~~Debugging
~~, removal with EmacsEmacs and Gforth
~~1btDebugging
~~btDebugging
~~ValueDebugging
~~VariableDebugging

$
$-prefix 16進数値用Literals in source code
$!$tring words
$!len$tring words
$?Passing Commands to the OS
$.$tring words
$[]$tring words
$[]!$tring words
$[].$tring words
$[]@$tring words
$[]#$tring words
$[]+!$tring words
$[]boot$tring words
$[]free$tring words
$[]map$tring words
$[]save$tring words
$[]saved$tring words
$[]slurp$tring words
$[]slurp-file$tring words
$[]Variable$tring words
$@$tring words
$@len$tring words
$+!$tring words
$+!len$tring words
$+[]!$tring words
$+slurp$tring words
$+slurp-file$tring words
$boot$tring words
$del$tring words
$exec$tring words
$free$tring words
$init$tring words
$ins$tring words
$iter$tring words
$over$tring words
$save$tring words
$saved$tring words
$slurp$tring words
$slurp-file$tring words
$split$tring words
$substituteSubstitute
$tmp$tring words
$unescapeSubstitute
$Variable$tring words

0
0<Numeric comparison
0<=Numeric comparison
0<>Numeric comparison
0=Numeric comparison
0>Numeric comparison
0>=Numeric comparison
0x-prefix 16進数値用(非標準)Literals in source code

1
1-Single precision
1/fFloating Point
1+Single precision

2
2,Dictionary allocation
2!Memory Access
2@Memory Access
2*Bitwise operations
2/Bitwise operations
2>rReturn stack
2ConstantConstants
2dropData stack
2dupData stack
2field:Forth200x Structures
2LiteralLiterals
2nipData stack
2overData stack
2r@Return stack
2r>Return stack
2rdropReturn stack
2rotData stack
2swapData stack
2tuckData stack
2ValueValues
2VariableVariables
2varueVarues

A
a_, stack item typeNotation
A,Dictionary allocation
abi-codeAssembler Definitions
abortException Handling
ABORT"Exception Handling
ABORT", exception abort sequencecore-idef
absSingle precision
absolute-file?Search Paths
abstract classBasic Objects Usage
abstract classBasic OOF Usage
acceptLine input and conversion
ACCEPT, display after end of inputcore-idef
ACCEPT, editingcore-idef
AConstantConstants
actwidget methods
act-name$actor methods
action-ofDeferred Words
activateBasic multi-tasking
active-wactor methods
actorMINOS2 object framework
add-cflagsDeclaring OS-level libraries
add-frameworkDeclaring OS-level libraries
add-incdirDeclaring OS-level libraries
add-ldflagsDeclaring OS-level libraries
add-libDeclaring OS-level libraries
add-libpathDeclaring OS-level libraries
addrVarues
address alignment exceptioncore-ambcond
address alignment exception, stack overflowcore-ambcond
address arithmetic for structuresWhy explicit structure support?
address arithmetic wordsAddress arithmetic
address unitAddress arithmetic
address unit, size in bitscore-idef
ADDRESS-UNIT-BITSEnvironmental Queries
adjust-bufferHeap Allocation
after-locateLocating source code definitions
AGAINArbitrary control structures
AHEADArbitrary control structures
AliasAliases
aliasesAliases
alignDictionary allocation
alignedAddress arithmetic
aligned addressescore-idef
alignment faultscore-ambcond
alignment of addresses for typesAddress arithmetic
alignment tutorialAlignment Tutorial
ALiteralLiterals
allocateHeap Allocation
allotDictionary allocation
alsoWord Lists
also-pathGeneral Search Paths
also, too many word lists in search ordersearch-ambcond
ambiguous conditions, block wordsblock-ambcond
ambiguous conditions, core wordscore-ambcond
ambiguous conditions, double wordsdouble-ambcond
ambiguous conditions, facility wordsfacility-ambcond
ambiguous conditions, file wordsfile-ambcond
ambiguous conditions, floating-point wordsfloating-ambcond
ambiguous conditions, locals wordslocals-ambcond
ambiguous conditions, programming-tools wordsprogramming-ambcond
ambiguous conditions, search-order wordssearch-ambcond
andBitwise operations
angles in trigonometric operationsFloating Point
annotate-covCode Coverage
ans-report.fsStandard Report
appendString words
argOS command line arguments
argcOS command line arguments
argument input source different than current input source for RESTORE-INPUTcore-ambcond
argument type mismatchcore-ambcond
argument type mismatch, RESTORE-INPUTcore-ambcond
arguments, OS command lineOS command line arguments
argvOS command line arguments
arithmetic wordsArithmetic
arithmetics tutorialArithmetics Tutorial
array, iterating overCounted Loops
arraysCREATE
arrays tutorialArrays and Records Tutorial
arshiftBitwise operations
asptrClass Declaration
assemblerAssembler and Code Words
assemblerAssembler Definitions
ASSEMBLER, search order capabilityprogramming-idef
assert-levelAssertions
assert(Assertions
assert0(Assertions
assert1(Assertions
assert2(Assertions
assert3(Assertions
assertionsAssertions
ASSUME-LIVEWhere are locals visible by name?
at-deltaxyTerminal output
at-xyTerminal output
AT-XY can’t be performed on user output devicefacility-ambcond
Attempt to use zero-length string as a namecore-ambcond
au (address unit)Address arithmetic
AUserTask-local data
authorsHelp on Gforth
authors of GforthOrigin
auto-indentation of Forth code in EmacsAuto-Indentation
AValueValues
AVariableVariables

B
bLocating source code definitions
backtraceError messages
backtrace examinationLocating exception source
backtraces with gforth-fastError messages
barrierHardware operations for multi-tasking
baseNumber Conversion
base is not decimal (REPRESENT, F., FE., FS.)floating-ambcond
base-executeNumber Conversion
baselinewidget methods
basenameDirectories
basic objects usageBasic Objects Usage
batch processing with GforthInvoking Gforth
before-lineText Interpreter Hooks
before-locateLocating source code definitions
before-wordText Interpreter Hooks
BEGINArbitrary control structures
begin-structureForth200x Structures
benchmarking Forth systemsPerformance
BenchresPerformance
big-endianSpecial Memory Accesses
binGeneral files
bindObjects Glossary
bind usageClass Binding
bind'Objects Glossary
bitwise operation wordsBitwise operations
blString and character literals
blankMemory Blocks
blkInput Sources
BLK, altering BLKblock-ambcond
blockBlocks
block buffersBlocks
block number invalidblock-ambcond
block read not possibleblock-ambcond
block transfer, I/O exceptionblock-ambcond
block words, ambiguous conditionsblock-ambcond
block words, implementation-defined optionsblock-idef
block words, other system documentationblock-other
block words, system documentationThe optional Block word set
block-includedBlocks
block-offsetBlocks
block-positionBlocks
blocksBlocks
blocks fileBlocks
blocks files, use with EmacsBlocks Files
blocks in filesfile-idef
blocks.fbBlocks
body-relative address input formatLiterals in source code
Boolean flagsBoolean Flags
bootmessageModifying the Startup Sequence
borderwidget methods
borderlwidget methods
bordertwidget methods
bordervwidget methods
boundsCounted Loops
break:Singlestep Debugger
break"Singlestep Debugger
broken-pipe-errorPipes
browseLocating source code definitions
btLocating exception source
bufferBlocks
buffer:Variables
buffer%Heap Allocation
bug reportingBugs
bwLocating uses of a word
bw-coverCode Coverage
bye during gforthmigforthmi
byte orderSpecial Memory Accesses

C
C function pointers to Forth wordsCallbacks
C function pointers, calling from ForthCalling C function pointers
C functions, calls toCalling C Functions
C functions, declarationsDeclaring C Functions
C interfaceC Interface
c_, stack item typeNotation
c-callbackCallbacks
c-callback-threadCallbacks
c-functionDeclaring C Functions
c-funptrCalling C function pointers
c-libraryDefining library interfaces
c-library-nameDefining library interfaces
c-valueDeclaring C Functions
c-variableDeclaring C Functions
c,Dictionary allocation
c, stack item typeNotation
C, using C for the enginePortability
C:Locals definition words
c!Memory Access
c?Regular Expressions
C"Counted string words
c@Memory Access
C^Locals definition words
c>sSpecial Memory Accesses
c$+!$tring words
CA:Locals definition words
call-cLow-Level C Interface Words
Callback functions written in ForthCallbacks
caller-wactor methods
calling a definitionCalls and returns
calling C functionsCalling C Functions
capscompareString words
capssearchString words
capsstring-prefix?String words
caseArbitrary control structures
case as generalized control structureGeneral control structures with CASE
CASE control structureSelection
case sensitivityCase insensitivity
case-sensitivity characteristicscore-idef
case-sensitivity for name lookupcore-idef
catchException Handling
catch and backtracesError messages
catch and thisObjects Implementation
catch in m: ... ;mMethod conveniences
cellAddress arithmetic
cell sizecore-idef
cell-Address arithmetic
cell-aligned addressescore-idef
cell/Address arithmetic
cell%Structure Glossary
cell+Address arithmetic
cellsAddress arithmetic
CFAThreading Words
cfalignDictionary allocation
cfalignedAddress arithmetic
cfield:Forth200x Structures
changing the compilation word list (during compilation)search-ambcond
charString and character literals
char sizecore-idef
char-Address arithmetic
char%Structure Glossary
char+Address arithmetic
character editing of ACCEPT and EXPECTcore-idef
character encodingCharacters
character literalsString and character literals
character setcore-idef
character strings - displayingDisplaying characters and strings
character strings - moving and copyingMemory Blocks
character strings - representationsString representations
character-aligned address requirementscore-idef
character-set extensions and matching of namescore-idef
Characters - chars/bytes vs. extended charactersCharacters
characters - displayingDisplaying characters and strings
characters tutorialCharacters and Strings Tutorial
charclassRegular Expressions
charsAddress arithmetic
child wordsUser-defined defining words using CREATE
cilk-byeCilk
cilk-initCilk
cilk-syncCilk
class bindingClass Binding
class binding as optimizationClass Binding
class binding, alternative toClass Binding
class binding, implementationObjects Implementation
class declarationClass Declaration
class definition, restrictionsBasic Objects Usage
class definition, restrictionsBasic OOF Usage
class implementationClass Implementation
class implementation and representationObjects Implementation
class scoping implementationObjects Implementation
class usageBasic Objects Usage
class usageBasic OOF Usage
class->mapObjects Glossary
class-inst-sizeObjects Glossary
class-inst-size discussionCreating objects
class-override!Objects Glossary
class-previousObjects Glossary
class;Class Declaration
class; usageBasic OOF Usage
class>orderObjects Glossary
classes and scopingClasses and Scoping
clear screenTerminal output
clear-libsDeclaring OS-level libraries
clear-pathGeneral Search Paths
clearstackExamining data
clearstacksExamining data
clickedactor methods
clock tick durationfacility-idef
close-dirDirectories
close-fileGeneral files
close-pipePipes
closuresClosures
cmoveMemory Blocks
cmove>Memory Blocks
codeAssembler Definitions
code addressThreading Words
code coverageCode Coverage
CODE ending sequenceprogramming-idef
code fieldThreading Words
code wordsAssembler and Code Words
code-address!Threading Words
CODE, processing inputprogramming-idef
colon definitionsColon Definitions
colon definitionsAnonymous Definitions
colon definitions, nestingQuotations
colon definitions, tutorialColon Definitions Tutorial
colon-sys, passing data across :Literals
color-coverCode Coverage
color:widget methods
combined wordsCombined words
command line arguments, OSOS command line arguments
command-line editingCommand-line editing
command-line optionsInvoking Gforth
comment editing commandsEmacs and Gforth
commentsComments
comments tutorialComments Tutorial
common-listLocals implementation
comp-i.fsgforthmi
comp.lang.forthForth-related information
COMP'Compilation token
compareMemory Blocks
comparison of object modelsComparison with other object models
comparison tutorialFlags and Comparisons Tutorial
compilation semanticsHow does that work?
compilation semanticsInterpretation and Compilation Semantics
compilation semantics tutorialInterpretation and Compilation Semantics and Immediacy Tutorial
compilation tokenCompilation token
compilation tokens, tutorialCompilation Tokens Tutorial
compilation word listWord Lists
compilation word list, change before definition endssearch-ambcond
compile stateThe Text Interpreter
compile-lp+!Locals implementation
compile-onlyInterpretation and Compilation Semantics
compile-only warning, for ' etc.core-ambcond
compile-only wordsInterpretation and Compilation Semantics
compile-only?Header fields
compile,Macros
compiled code examinationExamining compiled code
compiling compilation semanticsMacros
compiling wordsCompiling words
conditional compilationInterpreter Directives
conditionals, tutorialConditional execution Tutorial
const-does>Const-does>
ConstantConstants
constantsConstants
constructObjects Glossary
construct discussionCreating objects
contextWord Lists
context-sensitive helpEmacs and Gforth
contiguous regions and heap allocationHeap Allocation
contiguous regions in dictionary allocationDictionary allocation
contofArbitrary control structures
contributors to GforthOrigin
control characters as delimiterscore-idef
control structuresControl Structures
control structures for selectionSelection
control structures programming styleArbitrary control structures
control structures, user-definedArbitrary control structures
control-flow stackArbitrary control structures
control-flow stack items, locals informationLocals implementation
control-flow stack underflowprogramming-ambcond
control-flow stack, formatcore-idef
convertLine input and conversion
convertin strings to numbersLine input and conversion
COREEnvironmental Queries
core words, ambiguous conditionscore-ambcond
core words, implementation-defined optionscore-idef
core words, other system documentationcore-other
core words, system documentationThe Core Words
CORE-EXTEnvironmental Queries
coresCilk
countCounted string words
counted loopsCounted Loops
counted loops with negative incrementCounted Loops
counted string, maximum sizecore-idef
counted stringsString representations
Countryi18n and l10n
cov%Code Coverage
cov+Code Coverage
coverage?Code Coverage
cputimeKeeping track of Time
crMiscellaneous output
CreateCREATE
CREATE ... DOES>User-defined defining words using CREATE
CREATE ... DOES>, applicationsCREATE..DOES> applications
CREATE ... DOES>, detailsCREATE..DOES> details
CREATE ... SET-DOES>User-defined defining words using CREATE
CREATE and alignmentAddress arithmetic
CREATE されたワード以外の DOES>core-ambcond
create-fileGeneral files
create-fromCreating from a prototype
create...does> tutorialDefining Words Tutorial
creating objectsCreating objects
critical-sectionSemaphores
cross-compilercross.fs
cross-compilerCross Compiler
cross.fscross.fs
cross.fsCross Compiler
CS-DROPArbitrary control structures
CS-PICKArbitrary control structures
CS-PICK, fewer than u+1 items on the control flow-stackprogramming-ambcond
CS-ROLLArbitrary control structures
CS-ROLL, fewer than u+1 items on the control flow-stackprogramming-ambcond
cs-vocabularyWord Lists
cs-wordlistWord Lists
cstring>sstringString words
ct (compilation token)Compilation token
CT, tutorialCompilation Tokens Tutorial
ctzBitwise operations
currentWord Lists
current-interfaceObjects Glossary
current-interface discussionObjects Implementation
current'Objects Glossary
curryingCREATE..DOES> applications
cursor positioningTerminal output

D
dwidget methods
d-Double precision
d, stack item typeNotation
D:Locals definition words
d.Simple numeric output
d.rSimple numeric output
D^Locals definition words
d+Double precision
d<Numeric comparison
d<=Numeric comparison
d<>Numeric comparison
d=Numeric comparison
d>Numeric comparison
d>=Numeric comparison
d>fFloating Point
D>F, d cannot be presented precisely as a floatfloating-ambcond
d>sDouble precision
D>S, d out of range of ndouble-ambcond
d0<Numeric comparison
d0<=Numeric comparison
d0<>Numeric comparison
d0=Numeric comparison
d0>Numeric comparison
d0>=Numeric comparison
d2*Bitwise operations
d2/Bitwise operations
DA:Locals definition words
dabsDouble precision
dark-modeTerminal output
darshiftBitwise operations
data space - reserving someDictionary allocation
data space availablecore-other
data space containing definitions gets de-allocatedcore-ambcond
data space pointer not properly aligned, ,, C,core-ambcond
data space read/write with incorrect alignmentcore-ambcond
data stackStack Manipulation
data stack manipulation wordsData stack
data structure localsGforth locals
data-relocatable image filesData-Relocatable Image Files
data-space, read-only regionscore-idef
dbgSinglestep Debugger
debug tracer editing commandsEmacs and Gforth
debug-fidDebugging
debuggingDebugging
debugging output, finding the source location in EmacsEmacs and Gforth
debugging SinglestepSinglestep Debugger
dec.Simple numeric output
dec.rSimple numeric output
decimalNumber Conversion
declaring C functionsDeclaring C Functions
decompilation tutorialDecompilation Tutorial
default type of localsGforth locals
default-colorTerminal output
default-w:Gforth locals
default-wa:Gforth locals
DeferDeferred Words
deferClass Declaration
defer!Deferred Words
defer@Deferred Words
deferred wordsDeferred Words
defersDeferred Words
definerThreading Words
definer!Threading Words
definesBasic Mini-OOF Usage
defining defining wordsUser-defined Defining Words
defining wordsDefining Words
defining words tutorialDefining Words Tutorial
defining words without nameAnonymous Definitions
defining words, name given in a stringSupplying names
defining words, simpleCREATE
defining words, user-definedUser-defined Defining Words
definitionIntroducing the Text Interpreter
definitionsWord Lists
definitions, tutorialColon Definitions Tutorial
defocusactor methods
deleteMemory Blocks
delete-fileGeneral files
delta-iCounted Loops
depthExamining data
depth changes during interpretationStack depth changes
depth-changes.fsStack depth changes
design of stack effects, tutorialDesigning the stack effect Tutorial
dest, control-flow stack itemArbitrary control structures
df_, stack item typeNotation
df!Memory Access
df@Memory Access
df@ or df! used with an address that is not double-float alignedfloating-ambcond
dfalignDictionary allocation
dfalignedAddress arithmetic
dffield:Forth200x Structures
dfloat/Address arithmetic
dfloat%Structure Glossary
dfloat+Address arithmetic
dfloatsAddress arithmetic
dgluewidget methods
dglue@widget methods
dict-newObjects Glossary
dict-new discussionCreating objects
dictionaryThe Text Interpreter
dictionary in persistent formImage Files
dictionary overflowcore-ambcond
dictionary size defaultStack and Dictionary Sizes
digits > 35core-idef
direct threaded inner interpreterThreading
DirectoriesDirectories
dirnameDirectories
disassembler, generalCommon Disassembler
discodeCommon Disassembler
dispose-widgetwidget methods
dividing by zerocore-ambcond
dividing by zero, floating-pointfloating-ambcond
Dividing classesDividing classes
dividing integersInteger division
dividing many integers with the same divisorTwo-stage integer division
Division by zeroInteger division
Division by zeroInteger division
division roundingcore-idef
division with potentially negative operandsArithmetic
dlshiftBitwise operations
dmaxDouble precision
dminDouble precision
dnegateDouble precision
DOCounted Loops
DO loopsCounted Loops
doabicode,Threading Words
doabicode:Threading Words
docol,Threading Words
docol:Threading Words
docon,Threading Words
docon:Threading Words
dodefer,Threading Words
dodefer:Threading Words
dodoes routineDOES>
dodoes:Threading Words
does-code!Threading Words
DOES>CREATE..DOES> details
DOES> implementationDOES>
DOES> in a separate definitionCREATE..DOES> details
DOES> in interpretation stateCREATE..DOES> details
does> tutorialDefining Words Tutorial
does>-codeThreading Words
DOES>-codeDOES>
DOES>-parts, stack effectUser-defined defining words using CREATE
DOES>, visibility of current definitioncore-idef
dofield,Threading Words
dofield:Threading Words
DONECounted Loops
double precision arithmetic wordsDouble precision
double words, ambiguous conditionsdouble-ambcond
double words, system documentationThe optional Double Number word set
double-cell numbers, input formatLiterals in source code
double%Structure Glossary
doubly indirect threaded codegforthmi
douser,Threading Words
douser:Threading Words
dovalue,Threading Words
dovalue:Threading Words
dovar,Threading Words
dovar:Threading Words
dplNumber Conversion
drawwidget methods
draw-initwidget methods
drolBitwise operations
dropData stack
drorBitwise operations
drshiftBitwise operations
du/modInteger division
du<Numeric comparison
du<=Numeric comparison
du>Numeric comparison
du>=Numeric comparison
dumpExamining data
dupData stack
duration of a system clock tickfacility-idef
dynamic allocation of memoryHeap Allocation
Dynamic superinstructions with replicationDynamic Superinstructions
Dynamically linked libraries in C interfaceDeclaring OS-level libraries

E
earlyClass Declaration
early bindingClass Binding
editLocating source code definitions
edit-lineLine input and conversion
editing in ACCEPT and EXPECTcore-idef
eforth performancePerformance
ekeySingle-key input
EKEY, encoding of keyboard eventsfacility-idef
ekey?Single-key input
ekey>charSingle-key input
ekey>fkeySingle-key input
ekey>xcharSingle-key input
ekeyedactor methods
elements of a Forth systemReview - elements of a Forth system
ELSEArbitrary control structures
Emacs and GforthEmacs and Gforth
emitDisplaying characters and strings
EMIT and non-graphic characterscore-idef
emit-fileGeneral files
empty-bufferBlocks
empty-buffersBlocks
end-c-libraryDefining library interfaces
end-classObjects Glossary
end-classBasic Mini-OOF Usage
end-class usageBasic Objects Usage
end-class-nonameObjects Glossary
end-codeAssembler Definitions
end-interfaceObjects Glossary
end-interface usageObject Interfaces
end-interface-nonameObjects Glossary
end-methodsObjects Glossary
end-structStructure Glossary
end-struct usageStructure Usage
end-structureForth200x Structures
endcaseArbitrary control structures
ENDIFArbitrary control structures
endless loopSimple Loops
endofArbitrary control structures
endscopeWhere are locals visible by name?
endtryException Handling
endtry-iferrorException Handling
engineEngine
engine performancePerformance
engine portabilityPortability
engine.sProduced code
engines, gforth vs. gforth-fast vs. gforth-itcDirect or Indirect Threaded?
enteredactor methods
environmentEnvironmental Queries
environment variable input formatLiterals in source code
environment variablesEnvironment variables
environment variablesgforthmi
environment wordsetNotation
environment-wordlistEnvironmental Queries
environment?Environmental Queries
ENVIRONMENT? string length, maximumcore-idef
environmental queriesEnvironmental Queries
environmental restrictionsStandard conformance
equality of floatsFloating Point
eraseMemory Blocks
error messagesError messages
error output, finding the source location in EmacsEmacs and Gforth
error-colorTerminal output
error-hl-invTerminal output
error-hl-ulTerminal output
etags.fsEmacs Tags
evaluateInput Sources
event-loopMessage queues
examining data and codeExamining data
exceptionException Handling
exception abort sequence of ABORT"core-idef
exception source codeLocating exception source
exception when including sourcefile-idef
exception words, implementation-defined optionsexception-idef
exception words, system documentationThe optional Exception word set
exceptionsException Handling
exceptionsException Handling
exceptions tutorialExceptions Tutorial
executable image fileRunning Image Files
executeExecution token
execute-exitExecution token
execute-parsingThe Input Stream
execute-parsing-fileThe Input Stream
execute-taskBasic multi-tasking
executing code on startupInvoking Gforth
execution frequencyCode Coverage
execution semanticsInterpretation and Compilation Semantics
execution tokenIntroducing the Text Interpreter
execution tokenExecution token
execution token input formatLiterals in source code
execution token of last defined wordAnonymous Definitions
execution token of words with undefined execution semanticscore-ambcond
execution tokens tutorialExecution Tokens Tutorial
exercisesExercises
EXITCalls and returns
exit in m: ... ;mMethod conveniences
exitmObjects Glossary
exitm discussionMethod conveniences
expand-whereLocating uses of a word
expectLine input and conversion
EXPECT, display after end of inputcore-idef
EXPECT, editingcore-idef
explicit register declarationsPortability
exponent too big for conversion (DF!, DF@, SF!, SF@)floating-ambcond
extend-memHeap Allocation
extend-structureForth200x Structures
extended recordsStructure Usage

F
f_, stack item typeNotation
f-Floating Point
f-rotFloating point stack
f,Dictionary allocation
f, stack item typeNotation
F:Locals definition words
f!Memory Access
f! used with an address that is not float alignedfloating-ambcond
f.Floating-point output
f.rdpFloating-point output
f.sExamining data
f.s-precisionExamining data
f@Memory Access
f@ used with an address that is not float alignedfloating-ambcond
f@localnLocals implementation
f*Floating Point
f**Floating Point
f/Floating Point
F^Locals definition words
f+Floating Point
f<Floating Point
f<=Floating Point
f<>Floating Point
f=Floating Point
f>Floating Point
f>=Floating Point
f>buf-rdpFloating-point output
f>dFloating Point
F>D, integer part of float cannot be represented by dfloating-ambcond
f>lLocals implementation
f>sFloating Point
f>str-rdpFloating-point output
f~Floating Point
f~absFloating Point
f~relFloating Point
f0<Floating Point
f0<=Floating Point
f0<>Floating Point
f0=Floating Point
f0>Floating Point
f0>=Floating Point
f2*Floating Point
f2/Floating Point
f83name, stack item typeNotation
FA:Locals definition words
fabsFloating Point
facility words, ambiguous conditionsfacility-ambcond
facility words, implementation-defined optionsfacility-idef
facility words, system documentationThe optional Facility word set
facosFloating Point
FACOS, |float|>1floating-ambcond
facoshFloating Point
FACOSH, float<1floating-ambcond
factoringIntroduction
factoring similar colon definitionsCREATE..DOES> applications
factoring tutorialFactoring Tutorial
fade-color:widget methods
falignDictionary allocation
falignedAddress arithmetic
falogFloating Point
falseBoolean Flags
fam (file access method)General files
fasinFloating Point
FASIN, |float|>1floating-ambcond
fasinhFloating Point
FASINH, float<0floating-ambcond
fast-throwException Handling
fatanFloating Point
fatan2Floating Point
FATAN2, both arguments are equal to zerofloating-ambcond
fatanhFloating Point
FATANH, |float|>1floating-ambcond
faxpyFloating Point
fclearstackExamining data
fconstantConstants
fcopysignFloating Point
fcosFloating Point
fcoshFloating Point
fdepthExamining data
FDL, GNU Free Documentation LicenseGNU Free Documentation License
fdropFloating point stack
fdupFloating point stack
fe.Floating-point output
fexpFloating Point
fexpm1Floating Point
ffield:Forth200x Structures
ffourthFloating point stack
fieldStructure Glossary
field naming conventionStructure Naming Convention
field usageStructure Usage
field usage in class definitionBasic Objects Usage
field:Forth200x Structures
file access methods usedfile-idef
file exceptionsfile-idef
file input nesting, maximum depthfile-idef
file line terminatorfile-idef
file name formatfile-idef
file search pathSearch Paths
file words, ambiguous conditionsfile-ambcond
file words, implementation-defined optionsfile-idef
file words, system documentationThe optional File-Access word set
file-eof?General files
file-handlingGeneral files
file-positionGeneral files
file-sizeGeneral files
file-statusGeneral files
FILE-STATUS, returned informationfile-idef
file>fpathSource Search Paths
file>pathGeneral Search Paths
filename-matchDirectories
filenames in ~~ outputDebugging
filenames in assertion outputAssertions
filesFiles
files containing blocksfile-idef
files containing Forth code, tutorialUsing files for Forth code Tutorial
files tutorialFiles Tutorial
fillMemory Blocks
findWord Lists
find-nameName token
find-name-inName token
first definitionYour first definition
first field optimizationStructure Usage
first field optimization, implementationStructure Implementation
fkey.Single-key input
flags on the command lineInvoking Gforth
flags tutorialFlags and Comparisons Tutorial
flavours of localsGforth locals
FLiteralLiterals
flnFloating Point
FLN, float<=0floating-ambcond
flnp1Floating Point
FLNP1, float<=-1floating-ambcond
floatAddress arithmetic
float/Address arithmetic
float%Structure Glossary
float+Address arithmetic
floating point arithmetic wordsFloating Point
floating point numbers, format and rangefloating-idef
floating point tutorialFloating Point Tutorial
floating point unidentified fault, integer divisioncore-ambcond
floating-point arithmetic, pitfallsFloating Point
floating-point comparisonsFloating Point
floating-point constantsFloating Point
floating-point dividing by zerofloating-ambcond
floating-point numbers, input formatLiterals in source code
floating-point numbers, rounding or truncationfloating-idef
floating-point outputFloating-point output
floating-point result out of rangefloating-ambcond
floating-point stackStack Manipulation
floating-point stack in the standardStack Manipulation
floating-point stack manipulation wordsFloating point stack
floating-point stack sizefloating-idef
floating-point stack widthfloating-idef
Floating-point unidentified faultInteger division
Floating-point unidentified fault (on integer division)Integer division
floating-point unidentified fault, F>Dfloating-ambcond
floating-point unidentified fault, FACOS, FASIN or FATANHfloating-ambcond
floating-point unidentified fault, FACOSHfloating-ambcond
floating-point unidentified fault, FASINH or FSQRTfloating-ambcond
floating-point unidentified fault, FLN or FLOGfloating-ambcond
floating-point unidentified fault, FLNP1floating-ambcond
floating-point unidentified fault, FP divide-by-zerofloating-ambcond
floating-point words, ambiguous conditionsfloating-ambcond
floating-point words, implementation-defined optionsfloating-idef
floating-point words, system documentationThe optional Floating-Point word set
floating-stackEnvironmental Queries
floatsAddress arithmetic
flogFloating Point
FLOG, float<=0floating-ambcond
floorFloating Point
FLOOREDEnvironmental Queries
floored divisionInteger division
flushBlocks
flush-fileGeneral files
flush-icacheAssembler Definitions
fm/modInteger division
fmaxFloating Point
fminFloating Point
fnegateFloating Point
fnipFloating point stack
focusactor methods
FORCounted Loops
FOR loopsCounted Loops
foreign language interfaceC Interface
FORGET, deleting the compilation word listprogramming-ambcond
FORGET, name can’t be foundprogramming-ambcond
FORGET, removing a needed definitionprogramming-ambcond
forgeting wordsForgetting words
FORKRegular Expressions
formTerminal output
format and range of floating point numbersfloating-idef
format of glossary entriesNotation
formatted numeric outputFormatted numeric output
ForthWord Lists
Forth - an introductionIntroduction
Forth mode in EmacsEmacs and Gforth
Forth source filesForth source files
Forth TutorialTutorial
forth-recognizeDealing with existing Recognizers
forth-recognizerDealing with existing Recognizers
Forth-related informationForth-related information
forth-wordlistWord Lists
forth.elEmacs and Gforth
forwardForward
fourthData stack
foverFloating point stack
FP outputFloating-point output
FP tutorialFloating Point Tutorial
fp!Stack pointer manipulation
fp.Floating-point output
fp@Stack pointer manipulation
fp0Stack pointer manipulation
fpathSource Search Paths
fpickFloating point stack
freeHeap Allocation
free-closureClosures
free-mem-varHeap Allocation
frequently asked questionsForth-related information
frotFloating point stack
froundFloating Point
fs.Floating-point output
fsinFloating Point
fsincosFloating Point
fsinhFloating Point
fsqrtFloating Point
FSQRT, float<0floating-ambcond
fswapFloating point stack
ftanFloating Point
FTAN on an argument r1 where cos(r1) is zerofloating-ambcond
ftanhFloating Point
fthirdFloating point stack
ftruncFloating Point
ftuckFloating point stack
fully relocatable image filesFully Relocatable Image Files
functions, tutorialColon Definitions Tutorial
fvalueValues
fvariableVariables
fvarueVarues

G
gLocating source code definitions
gapwidget methods
gdb disassemblerCommon Disassembler
general control structures (case)General control structures with CASE
general filesGeneral files
getactor methods
get-block-fidBlocks
get-currentWord Lists
get-dirDirectories
get-orderWord Lists
get-recognizersDealing with existing Recognizers
getenvPassing Commands to the OS
gforthEnvironmental Queries
GFORTH – environment variableEnvironment variables
GFORTH – environment variablegforthmi
Gforth - leavingLeaving Gforth
gforth engineDirect or Indirect Threaded?
Gforth environmentGforth Environment
Gforth extensionsStandard vs Extensions
Gforth filesGforth Files
Gforth localsGforth locals
Gforth performancePerformance
Gforth stabilityStability Goals
gforth-ditcgforthmi
gforth-fast and backtracesError messages
gforth-fast engineDirect or Indirect Threaded?
gforth-fast, difference from gforthError messages
gforth-itc engineDirect or Indirect Threaded?
gforth.elEmacs and Gforth
gforth.el, installationInstalling gforth.el
gforth.fi, relocatabilityFully Relocatable Image Files
GFORTHD – environment variableEnvironment variables
GFORTHD – environment variablegforthmi
GFORTHHIST – environment variableEnvironment variables
gforthmigforthmi
GFORTHPATH – environment variableEnvironment variables
GFORTHSYSTEMPREFIX – environment variableEnvironment variables
ggLocating uses of a word
giving a name to a library interfaceDefining library interfaces
glossary notation formatNotation
GNU C for the enginePortability
goals of the Gforth projectGoals

H
hwidget methods
h.Simple numeric output
haltBasic multi-tasking
header fieldsHeader fields
header methodsHeader methods
header spaceWord Lists
heap allocationHeap Allocation
heap-newObjects Glossary
heap-new discussionCreating objects
heap-new usageBasic Objects Usage
helpHelp on Gforth
helpHelp on Gforth
hereDictionary allocation
hexNumber Conversion
hex.Simple numeric output
hgluewidget methods
hglue@widget methods
hideactor methods
highlighting Forth code in EmacsHilighting
hilighting Forth code in EmacsHilighting
history fileCommand-line editing
hmcopy,Threading Words
holdFormatted numeric output
holdsFormatted numeric output
hooks in the text interpreterText Interpreter Hooks
how:Class Declaration
hybrid direct/indirect threaded codeDirect or Indirect Threaded?

I
iCounted Loops
i'Counted Loops
I/O - blocksBlocks
I/O - file-handlingFiles
I/O - keyboard and displayOther I/O
I/O - see inputLine input and conversion
I/O exception in block transferblock-ambcond
id.Name token
IDE (integrated development environment)Locating source code definitions
IFArbitrary control structures
IF control structureSelection
if, tutorialConditional execution Tutorial
iferrorException Handling
image fileImage Files
image file backgroundImage File Background
image file initialization sequenceModifying the Startup Sequence
image file invocationRunning Image Files
image file loaderImage File Background
image file, data-relocatableData-Relocatable Image Files
image file, executableRunning Image Files
image file, fully relocatableFully Relocatable Image Files
image file, non-relocatableNon-Relocatable Image Files
image file, stack and dictionary sizesStack and Dictionary Sizes
image file, turnkey applicationsModifying the Startup Sequence
image licenseImage Licensing Issues
immediateInterpretation and Compilation Semantics
immediate wordsHow does that work?
immediate wordsInterpretation and Compilation Semantics
immediate, tutorialInterpretation and Compilation Semantics and Immediacy Tutorial
immediate?Header methods
implementationObjects Glossary
implementation of localsLocals implementation
implementation of structuresStructure Implementation
implementation usageObject Interfaces
implementation-defined options, block wordsblock-idef
implementation-defined options, core wordscore-idef
implementation-defined options, exception wordsexception-idef
implementation-defined options, facility wordsfacility-idef
implementation-defined options, file wordsfile-idef
implementation-defined options, floating-point wordsfloating-idef
implementation-defined options, locals wordslocals-idef
implementation-defined options, memory-allocation wordsmemory-idef
implementation-defined options, programming-tools wordsprogramming-idef
implementation-defined options, search-order wordssearch-idef
in-lining of constantsConstants
includeForth source files
include search pathSearch Paths
include-fileForth source files
INCLUDE-FILE, file-id is invalidfile-ambcond
INCLUDE-FILE, I/O exception reading or closing file-idfile-ambcond
include-localei18n and l10n
include, placement in filesEmacs Tags
includedForth source files
included-localei18n and l10n
INCLUDED, I/O exception reading or closing file-idfile-ambcond
INCLUDED, named file cannot be openedfile-ambcond
included?Forth source files
including filesForth source files
including files, stack effectForth source files
indentation of Forth code in EmacsAuto-Indentation
indirect threaded inner interpreterThreading
infile-executeRedirection
infile-idRedirection
infinityFloating Point
info-colorTerminal output
inheritance(継承)Object-Oriented Terminology
init-asmAssembler Definitions
init-bufferHeap Allocation
init-objectObjects Glossary
init-object discussionCreating objects
initialization of localsGforth locals
initialization sequence of image fileModifying the Startup Sequence
initiateBasic multi-tasking
inline:Colon Definitions
inner interpreter implementationThreading
inner interpreter optimizationScheduling
inner interpreter, direct threadedThreading
inner interpreter, indirect threadedThreading
input bufferThe Text Interpreter
input format for body-relative addressesLiterals in source code
input format for characters/code pointsLiterals in source code
input format for double-cell numbersLiterals in source code
input format for environment variablesLiterals in source code
input format for execution tokensLiterals in source code
input format for floating-point numbersLiterals in source code
input format for name tokensLiterals in source code
input format for single-cell numbersLiterals in source code
input format for stringsLiterals in source code
input from pipesGforth in pipes
input line size, maximumfile-idef
input line terminatorcore-idef
Input RedirectionRedirection
input sourcesInput Sources
input streamThe Input Stream
input-colorTerminal output
input, linewise from terminalLine input and conversion
input, single-keySingle-key input
insertMemory Blocks
inst-valueObjects Glossary
inst-value usageMethod conveniences
inst-value visibility(可視性)Classes and Scoping
inst-varObjects Glossary
inst-var implementationObjects Implementation
inst-var usageMethod conveniences
inst-var visibility(可視性)Classes and Scoping
instruction pointerThreading
insufficient data stack or return stack spacecore-ambcond
insufficient space for loop control parameterscore-ambcond
insufficient space in the dictionarycore-ambcond
INT-[I]Interpreter Directives
integer types, rangescore-idef
integrated development environmentLocating source code definitions
interfaceObjects Glossary
interface implementationObjects Implementation
interface to C functionsC Interface
interface usageObject Interfaces
interfaces for objectsObject Interfaces
interpretThe Text Interpreter
interpret stateThe Text Interpreter
Interpret/Compile statesInterpret/Compile states
interpret/compile:Combined words
interpretation semanticsHow does that work?
interpretation semanticsInterpretation and Compilation Semantics
interpretation semantics tutorialInterpretation and Compilation Semantics and Immediacy Tutorial
interpreter - outerThe Text Interpreter
interpreter directivesInterpreter Directives
Interpreting a compile-only wordcore-ambcond
Interpreting a compile-only word, for a locallocals-ambcond
interpreting a word with undefined interpretation semanticscore-ambcond
invalid block numberblock-ambcond
Invalid memory addresscore-ambcond
Invalid memory address, stack overflowcore-ambcond
Invalid name argument, TOcore-ambcond
Invalid name argument, TOlocals-ambcond
invertBitwise operations
invoking a selectorObject-Oriented Terminology
invoking GforthInvoking Gforth
invoking image filesRunning Image Files
ior type descriptionNotation
ior values and meaningfile-idef
ior values and meaningmemory-idef
ISDeferred Words
items on the stack after interpretationStack depth changes
iterate over arrayCounted Loops

J
jCounted Loops
JOINRegular Expressions

K
kCounted Loops
k-alt-maskSingle-key input
k-backspaceSingle-key input
k-ctrl-maskSingle-key input
k-deleteSingle-key input
k-downSingle-key input
k-endSingle-key input
k-enterSingle-key input
k-eofSingle-key input
k-f1Single-key input
k-f10Single-key input
k-f11Single-key input
k-f12Single-key input
k-f2Single-key input
k-f3Single-key input
k-f4Single-key input
k-f5Single-key input
k-f6Single-key input
k-f7Single-key input
k-f8Single-key input
k-f9Single-key input
k-homeSingle-key input
k-insertSingle-key input
k-leftSingle-key input
k-muteSingle-key input
k-nextSingle-key input
k-pauseSingle-key input
k-priorSingle-key input
k-rightSingle-key input
k-selSingle-key input
k-shift-maskSingle-key input
k-tabSingle-key input
k-upSingle-key input
k-voldownSingle-key input
k-volupSingle-key input
k-winchSingle-key input
kern*.fi, relocatabilityFully Relocatable Image Files
kerningwidget methods
keySingle-key input
key-fileGeneral files
key-iorSingle-key input
key?Single-key input
key?-fileGeneral files
keyboard events, encoding in EKEYfacility-idef
killBasic multi-tasking
kill-taskBasic multi-tasking
Kuehling, DavidEmacs and Gforth

L
lLocating source code definitions
l,Dictionary allocation
l!Special Memory Accesses
L"i18n and l10n
l@Special Memory Accesses
l>sSpecial Memory Accesses
labels as valuesThreading
lalignAddress arithmetic
lalignedAddress arithmetic
LANG – environment variableEnvironment variables
Languagei18n and l10n
last word was headerlesscore-ambcond
lastfitwidget methods
late bindingClass Binding
latestName token
latestntName token
latestxtAnonymous Definitions
lbeSpecial Memory Accesses
LC_ALL – environment variableEnvironment variables
LC_CTYPE – environment variableEnvironment variables
LEAVECounted Loops
leaving definitions, tutorialLeaving definitions or loops Tutorial
leaving GforthLeaving Gforth
leaving loops, tutorialLeaving definitions or loops Tutorial
leftactor methods
length of a line affected by \block-idef
lfield:Forth200x Structures
lib-errorLow-Level C Interface Words
lib-symLow-Level C Interface Words
Libraries in C interfaceDeclaring OS-level libraries
library interface namesDefining library interfaces
licenseHelp on Gforth
license for imagesImage Licensing Issues
lifetime of localsHow long do locals live?
light-modeTerminal output
line input from terminalLine input and conversion
line terminator on inputcore-idef
line-end-hookText Interpreter Hooks
listBlocks
LIST display formatblock-idef
list-sizeLocals implementation
LiteralLiterals
literal tutorialLiteral Tutorial
LiteralsLiterals
Literals (in source code)Literals in source code
literals for characters and stringsString and character literals
little-endianSpecial Memory Accesses
llLocating uses of a word
lleSpecial Memory Accesses
loadBlocks
load-covCode Coverage
loader for image filesImage File Background
loading files at startupInvoking Gforth
loading Forth code, tutorialUsing files for Forth code Tutorial
local in interpretation statelocals-ambcond
local variables, tutorialLocal Variables Tutorial
locale and case-sensitivitycore-idef
locale-csvi18n and l10n
locale-csv-outi18n and l10n
locale-filei18n and l10n
locale!i18n and l10n
locale@i18n and l10n
localsLocals
locals and return stackReturn stack
locals flavoursGforth locals
locals implementationLocals implementation
locals information on the control-flow stackLocals implementation
locals initializationGforth locals
locals lifetimeHow long do locals live?
locals programming styleLocals programming style
locals stackStack Manipulation
locals stackLocals implementation
locals typesGforth locals
locals visibilityWhere are locals visible by name?
locals words, ambiguous conditionslocals-ambcond
locals words, implementation-defined optionslocals-idef
locals words, system documentationThe optional Locals word set
locals, default typeGforth locals
locals, Gforth styleGforth locals
locals, maximum number in a definitionlocals-idef
locals, Standard Forth styleStandard Forth locals
locateLocating source code definitions
lockSemaphores
log2Bitwise operations
long longPortability
LOOPCounted Loops
loop control parameters not availablecore-ambcond
loops without countSimple Loops
loops, countedCounted Loops
loops, counted, tutorialCounted loops Tutorial
loops, endlessSimple Loops
loops, indefinite, tutorialGeneral Loops Tutorial
lp!Stack pointer manipulation
lp!Locals implementation
lp@Stack pointer manipulation
lp@Locals implementation
lp0Stack pointer manipulation
lrolBitwise operations
lrorBitwise operations
lshiftBitwise operations
LSHIFT, large shift countscore-ambcond
LU"i18n and l10n

M
m:Objects Glossary
m: usageMethod conveniences
m*Mixed precision
m*/Integer division
m+Mixed precision
macrosCompiling words
MacrosMacros
macros-wordlistSubstitute
macros, advanced tutorialAdvanced macros Tutorial
magenta-inputTerminal output
make-latestMaking a word current
map-vocsWord Lists
mapping block ranges to filesfile-idef
markerForgetting words
maxSingle precision
MAX-CHAREnvironmental Queries
MAX-DEnvironmental Queries
max-floatEnvironmental Queries
MAX-NEnvironmental Queries
MAX-UEnvironmental Queries
MAX-UDEnvironmental Queries
MAX-XCHAREnvironmental Queries
maxalignDictionary allocation
maxalignedAddress arithmetic
maxdepth-.sExamining data
maximum depth of file input nestingfile-idef
maximum number of locals in a definitionlocals-idef
maximum number of word lists in search ordersearch-idef
maximum size of a counted stringcore-idef
maximum size of a definition name, in characterscore-idef
maximum size of a parsed stringcore-idef
maximum size of input linefile-idef
maximum string length for ENVIRONMENT?, in characterscore-idef
mem-doCounted Loops
mem,Dictionary allocation
mem+doCounted Loops
memory access wordsMemory Access
memory access/allocation tutorialMemory Tutorial
memory alignment tutorialAlignment Tutorial
memory block wordsMemory Blocks
memory overcommit for dictionary and stacksInvoking Gforth
memory wordsMemory
memory-allocation word setHeap Allocation
memory-allocation words, implementation-defined optionsmemory-idef
memory-allocation words, system documentationThe optional Memory-Allocation word set
message sendObject-Oriented Terminology
metacompilercross.fs
metacompilerCross Compiler
method conveniencesMethod conveniences
method mapObjects Implementation
method selectorObject-Oriented Terminology
method usageBasic OOF Usage
methodsObjects Glossary
methods...end-methodsDividing classes
minSingle precision
mini-oofMini-OOF
mini-oof exampleMini-OOF Example
mini-oof usageBasic Mini-OOF Usage
mini-oof.fs, differences to other modelsComparison with other object models
minimum search ordersearch-idef
miscellaneous wordsMiscellaneous Words
mixed precision arithmetic wordsMixed precision
mkdir-parentsDirectories
modInteger division
modfInteger division
modf-stage2mTwo-stage integer division
modifying >INHow does that work?
Modifying a word defined earlierMaking a word current
modifying the contents of the input buffer or a string literalcore-ambcond
modsInteger division
modulusInteger division
most recent definition does not have a name (IMMEDIATE)core-ambcond
motivation for object-oriented programmingWhy object-oriented programming?
moveMemory Blocks
msKeeping track of Time
MS, repeatability to be expectedfacility-idef
Multiple exits from beginBEGIN loops with multiple exits
multitaskerMultitasker
Must now be used inside C-LIBRARY, see C interface docMigrating the C interface from earlier Gforth
muxBitwise operations
mwordsWord Lists

N
nLocating source code definitions
n, stack item typeNotation
n/aUser-defined TO and DEFER@
n>rReturn stack
nameThe Input Stream
name dictionaryIntroducing the Text Interpreter
name field addressName token
name lookup, case-sensitivitycore-idef
name not defined by VALUE or (LOCAL) used by TOlocals-ambcond
name not defined by VALUE used by TOcore-ambcond
name not foundcore-ambcond
name not found (', POSTPONE, ['], [COMPILE])core-ambcond
name tokenName token
name, maximum lengthcore-idef
name>compileName token
name>interpretName token
name>linkName token
name>stringName token
name$widget methods
names for defined wordsSupplying names
NaNFloating Point
native@i18n and l10n
needsForth source files
negateSingle precision
negative increment for counted loopsCounted Loops
Neon modelComparison with other object models
nested colon definitionsQuotations
newBasic Mini-OOF Usage
new-color:widget methods
newlineString and character literals
newline character on inputcore-idef
newtaskBasic multi-tasking
newtask4Basic multi-tasking
NEXTCounted Loops
next-argOS command line arguments
next-caseArbitrary control structures
NEXT, direct threadedThreading
NEXT, indirect threadedThreading
nextnameSupplying names
NFAName token
nipData stack
nocov[Code Coverage
non-graphic characters and EMITcore-idef
non-relocatable image filesNon-Relocatable Image Files
nonameAnonymous Definitions
noname-fromCreating from a prototype
noopExecution token
notation of glossary entriesNotation
notfoundDealing with existing Recognizers
nothrowException Handling
nr>Return stack
nsKeeping track of Time
ntLocating exception source
nt (name token)Name token
NT Forth performancePerformance
nt input formatLiterals in source code
nt token input formatLiterals in source code
ntimeKeeping track of Time
number conversionNumber Conversion
number conversion - traps for the unwaryNumber Conversion
number of bits in one address unitcore-idef
number representation and arithmeticcore-idef
numeric comparison wordsNumeric comparison
numeric output - formattedFormatted numeric output
numeric output - simple/free-formatSimple numeric output
numeric output, FPFloating-point output
nwLocating uses of a word

O
o>Mini-OOF2
object allocation optionsCreating objects
object classThe Objects base class
object creationCreating objects
object interfacesObject Interfaces
object models, comparisonComparison with other object models
object-:The OOF base class
object-::The OOF base class
object-'The OOF base class
object-[]The OOF base class
object-asptrThe OOF base class
object-bindThe OOF base class
object-boundThe OOF base class
object-classThe OOF base class
object-class?The OOF base class
object-definitionsThe OOF base class
object-disposeThe OOF base class
object-endwithThe OOF base class
object-initThe OOF base class
object-isThe OOF base class
object-linkThe OOF base class
object-map discussionObjects Implementation
object-newThe OOF base class
object-new[]The OOF base class
object-oriented programmingObjects
object-oriented programmingOOF
object-oriented programming motivation(オブジェクト指向プログラミングの動機)Why object-oriented programming?
object-oriented programming styleObject-Oriented Programming Style
object-oriented terminology(オブジェクト指向用語)Object-Oriented Terminology
object-postponeThe OOF base class
object-ptrThe OOF base class
object-selfThe OOF base class
object-superThe OOF base class
object-withThe OOF base class
objectsObjects
objects, basic usageBasic Objects Usage
objects.fsObjects
objects.fsOOF
objects.fs GlossaryObjects Glossary
objects.fs implementationObjects Implementation
objects.fs propertiesProperties of the Objects model
ofArbitrary control structures
offBoolean Flags
onBoolean Flags
onceDebugging
OnlyWord Lists
oofOOF
oof.fsObjects
oof.fsOOF
oof.fs base classThe OOF base class
oof.fs propertiesProperties of the OOF model
oof.fs usageBasic OOF Usage
oof.fs, differences to other modelsComparison with other object models
open-blocksBlocks
open-dirDirectories
open-fileGeneral files
open-libLow-Level C Interface Words
open-path-fileGeneral Search Paths
open-pipePipes
operating system - passing commandsPassing Commands to the OS
operator’s terminal facilities availablecore-other
opt:User-defined compile-comma
options on the command lineInvoking Gforth
orBitwise operations
orderWord Lists
orig, control-flow stack itemArbitrary control structures
OS command line argumentsOS command line arguments
os-classEnvironmental Queries
os-typeEnvironmental Queries
other system documentation, block wordsblock-other
other system documentation, core wordscore-other
outMiscellaneous output
outer interpreterIntroducing the Text Interpreter
outer interpreterStacks and Postfix notation
outer interpreterThe Text Interpreter
outfile-executeRedirection
outfile-idRedirection
output in pipesGforth in pipes
Output RedirectionRedirection
output to terminalTerminal output
overData stack
overcommit memory for dictionary and stacksInvoking Gforth
overflow of the pictured numeric output stringcore-ambcond
overridesObjects Glossary
overrides usageBasic Objects Usage

P
padMemory Blocks
PAD sizecore-idef
PAD use by nonstandard wordscore-other
pageTerminal output
par-splitwidget methods
parameter stackStack Manipulation
parameters are not of the same type (DO, ?DO, WITHIN)core-ambcond
parent class bindingClass Binding
parent class(親クラス)Object-Oriented Terminology
parent-wwidget methods
parseThe Input Stream
parse areaThe Text Interpreter
parse-nameThe Input Stream
parse-wordThe Input Stream
parsed string overflowcore-ambcond
parsed string, maximum sizecore-idef
parsing wordsHow does that work?
parsing wordsHow does that work?
parsing wordsThe Text Interpreter
passBasic multi-tasking
patching threaded codeDynamic Superinstructions
path for includedSearch Paths
path+General Search Paths
path=General Search Paths
pauseBasic multi-tasking
pedigree of GforthOrigin
performExecution token
performance of some Forth interpretersPerformance
persistent form of dictionaryImage Files
PFE performancePerformance
piFloating Point
pickData stack
pictured numeric outputFormatted numeric output
pictured numeric output buffer, sizecore-idef
pictured numeric output string, overflowcore-ambcond
pipes, creating your ownPipes
pipes, Gforth as part ofGforth in pipes
postponeMacros
POSTPONE applied to [IF]programming-ambcond
POSTPONE or [COMPILE] applied to TOcore-ambcond
postpone tutorialPOSTPONE Tutorial
postpone,Compilation token
Pountain’s object-oriented modelComparison with other object models
pow2?Bitwise operations
precisionFloating-point output
precompiled Forth codeImage Files
prefix `Execution token
prepend-whereLocating uses of a word
preserveDeferred Words
previousWord Lists
previous, search order emptysearch-ambcond
primitive source formatAutomatic Generation
primitive-centric threaded codeDirect or Indirect Threaded?
primitives, assembly code listingProduced code
primitives, automatic generationAutomatic Generation
primitives, implementationPrimitives
primitives, keeping the TOS in a registerTOS Optimization
prims2x.fsAutomatic Generation
printObjects Glossary
printdebugdataDebugging
private discussionClasses and Scoping
procedures, tutorialColon Definitions Tutorial
process-optionModifying the Startup Sequence
program data space availablecore-other
programming style, arbitrary control structuresArbitrary control structures
programming style, localsLocals programming style
programming style, object-orientedObject-Oriented Programming Style
programming toolsProgramming Tools
programming-tools words, ambiguous conditionsprogramming-ambcond
programming-tools words, implementation-defined optionsprogramming-idef
programming-tools words, system documentationThe optional Programming-Tools word set
promptcore-idef
pronounciation of wordsNotation
protectedObjects Glossary
protected discussionClasses and Scoping
pthreadPthreads
ptrClass Declaration
publicObjects Glossary

Q
queryInput Sources
quitMiscellaneous Words
quotationsQuotations

R
r, stack item typeNotation
r@Return stack
r/oGeneral files
r/wGeneral files
r>Return stack
raisewidget methods
ranges for integer typescore-idef
rdropReturn stack
re-colorwidget methods
re-emoji-colorwidget methods
re-fade-colorwidget methods
re-text-colorwidget methods
re-text-emoji-fade-colorwidget methods
read-csvCSV Reader
read-dirDirectories
read-fileGeneral files
read-lineGeneral files
read-only data space regionscore-idef
reading from file positions not yet writtenfile-ambcond
rec-bodyDealing with existing Recognizers
rec-dtickDealing with existing Recognizers
rec-floatDealing with existing Recognizers
rec-moof2Mini-OOF2
rec-ntDealing with existing Recognizers
rec-numDealing with existing Recognizers
rec-stringDealing with existing Recognizers
rec-tickDealing with existing Recognizers
rec-toDealing with existing Recognizers
reciprocal of integerTwo-stage integer division
recognizeDealing with existing Recognizers
recognizer-sequence:Dealing with existing Recognizers
Recognizers normal usageDefault Recognizers
Recognizers, dealing withDealing with existing Recognizers
recongizersRecognizers
recordsStructures
records tutorialArrays and Records Tutorial
recover (old Gforth versions)Exception Handling
recurseCalls and returns
RECURSE appears after DOES>core-ambcond
recursion tutorialRecursion Tutorial
recursiveCalls and returns
recursive definitionsCalls and returns
RedirectionRedirection
refillThe Input Stream
regexpsRegular Expressions
relocating loaderImage File Background
relocation at load-timeImage File Background
relocation at run-timeImage File Background
remainderInteger division
rename-fileGeneral files
REPEATArbitrary control structures
repeatability to be expected from the execution of MSfacility-idef
replace-wordDebugging
replacesSubstitute
ReplicationDynamic Superinstructions
report the words used in your programStandard Report
reposition-fileGeneral files
REPOSITION-FILE, outside the file’s boundariesfile-ambcond
representFloating-point output
REPRESENT, results when float is out of rangefloating-idef
requireForth source files
require, placement in filesEmacs Tags
requiredForth source files
reserving data spaceDictionary allocation
resizeHeap Allocation
resize-fileGeneral files
resizedwidget methods
restartBasic multi-tasking
restoreException Handling
restore-inputInput Sources
RESTORE-INPUT, Argument type mismatchcore-ambcond
restrictInterpretation and Compilation Semantics
Result out of rangeInteger division
result out of rangecore-ambcond
Result out of range (on integer division)Integer division
return stackStack Manipulation
return stack and localsReturn stack
return stack dump with gforth-fastError messages
return stack manipulation wordsReturn stack
return stack space availablecore-other
return stack tutorialReturn Stack Tutorial
return stack underflowcore-ambcond
return-stack-cellsEnvironmental Queries
returning from a definitionCalls and returns
revealCreating from a prototype
rolBitwise operations
rollData stack
RootWord Lists
rorBitwise operations
rotData stack
rounding of floating-point numbersfloating-idef
rp!Stack pointer manipulation
rp@Stack pointer manipulation
rp0Stack pointer manipulation
rshiftBitwise operations
RSHIFT, large shift countscore-ambcond
run-time code generation, tutorialAdvanced macros Tutorial
running GforthInvoking Gforth
running image filesRunning Image Files
Rydqvist, GoranEmacs and Gforth

S
S"String and character literals
S", number of string buffersfile-idef
S", size of string bufferfile-idef
s//Regular Expressions
s\"String and character literals
s+String words
s>>Regular Expressions
s>dDouble precision
s>fFloating Point
s>number?Line input and conversion
s>unumber?Line input and conversion
safe/stringString words
save-bufferBlocks
save-buffersBlocks
save-covCode Coverage
save-inputInput Sources
save-memHeap Allocation
savesystemNon-Relocatable Image Files
savesystem during gforthmigforthmi
scanString words
scan-backString words
scopeWhere are locals visible by name?
scope of localsWhere are locals visible by name?
scoping and classesClasses and Scoping
scrBlocks
scrolledactor methods
sealWord Lists
searchString words
search order stackWord Lists
search order, maximum depthsearch-idef
search order, minimumsearch-idef
search order, tutorialWordlists and Search Order Tutorial
search path control, source filesSource Search Paths
search path control, source filesGeneral Search Paths
search path for filesSearch Paths
search-order words, ambiguous conditionssearch-ambcond
search-order words, implementation-defined optionssearch-idef
search-order words, system documentationThe optional Search-Order word set
search-wordlistWord Lists
seeExamining compiled code
see tutorialDecompilation Tutorial
see-codeExamining compiled code
see-code-rangeExamining compiled code
SEE, source and format of outputprogramming-idef
selectBoolean Flags
selection control structuresSelection
selector implementation, classObjects Implementation
selector invocation, restrictionsBasic Objects Usage
selector invocation, restrictionsBasic OOF Usage
selector usageBasic Objects Usage
selectors and stack effectsObject-Oriented Programming Style
selectors common to hardly-related classesObject Interfaces
semantics tutorialInterpretation and Compilation Semantics and Immediacy Tutorial
semantics, interpretation and compilationInterpretation and Compilation Semantics
semaphoreSemaphores
send-eventMessage queues
setactor methods
set->compHeader methods
set->intHeader methods
set-currentWord Lists
set-dirDirectories
set-does>CREATE..DOES> details
set-executeHeader methods
set-forth-recognizeDealing with existing Recognizers
set-name>linkHeader methods
set-name>stringHeader methods
set-optimizerUser-defined compile-comma
set-orderWord Lists
set-precisionFloating-point output
set-recognizersDealing with existing Recognizers
set-toUser-defined TO and DEFER@
sf_, stack item typeNotation
sf!Memory Access
sf@Memory Access
sf@ or sf! used with an address that is not single-float alignedfloating-ambcond
sfalignDictionary allocation
sfalignedAddress arithmetic
sffield:Forth200x Structures
sfloat/Address arithmetic
sfloat%Structure Glossary
sfloat+Address arithmetic
sfloatsAddress arithmetic
shPassing Commands to the OS
sh-getPassing Commands to the OS
Shared libraries in C interfaceDeclaring OS-level libraries
shell commandsPassing Commands to the OS
shift-argsOS command line arguments
short-whereLocating uses of a word
showactor methods
show-youactor methods
signFormatted numeric output
sign extensionSpecial Memory Accesses
silent exiting from GforthGforth in pipes
simple defining wordsCREATE
simple loopsSimple Loops
simple-fkey-stringSingle-key input
simple-seeExamining compiled code
simple-see-rangeExamining compiled code
single precision arithmetic wordsSingle precision
single-assignment style for localsLocals programming style
single-cell numbers, input formatLiterals in source code
single-key inputSingle-key input
singlestep DebuggerSinglestep Debugger
size of buffer at WORDcore-idef
size of the dictionary and the stacksInvoking Gforth
size of the keyboard terminal buffercore-idef
size of the pictured numeric output buffercore-idef
size of the scratch area returned by PADcore-idef
size parameters for command-line optionsInvoking Gforth
skipString words
SLiteralLiterals
slurp-fidGeneral files
slurp-fileGeneral files
sm/remInteger division
sourceThe Text Interpreter
source code for exceptionLocating exception source
source code of a wordLocating source code definitions
source location of error or debugging output in EmacsEmacs and Gforth
source-idInput Sources
SOURCE-ID, behaviour when BLK is non-zerofile-ambcond
sourcefilenameForth source files
sourceline#Forth source files
sp!Stack pointer manipulation
sp@Stack pointer manipulation
sp0Stack pointer manipulation
spaceMiscellaneous output
space delimiterscore-idef
spacesMiscellaneous output
spanLine input and conversion
spawnCilk
spawn1Cilk
spawn2Cilk
speed, startupStartup speed
splitwidget methods
stability of GforthStability Goals
stack depth changes during interpretationStack depth changes
stack effectNotation
Stack effect design, tutorialDesigning the stack effect Tutorial
stack effect of DOES>-partsUser-defined defining words using CREATE
stack effect of included filesForth source files
stack effects of selectorsObject-Oriented Programming Style
stack emptycore-ambcond
stack item typesNotation
stack manipulation tutorialStack Manipulation Tutorial
stack manipulation wordsStack Manipulation
stack manipulation words, floating-point stackFloating point stack
stack manipulation words, return stackReturn stack
stack manipulations words, data stackData stack
stack overflowcore-ambcond
stack pointer manipulation wordsStack pointer manipulation
stack size defaultStack and Dictionary Sizes
stack size, cache-friendlyStack and Dictionary Sizes
stack space availablecore-other
stack tutorialStack Tutorial
stack underflowcore-ambcond
stack-cellsEnvironmental Queries
stack-effect comments, tutorialStack-Effect Comments Tutorial
stacksizeBasic multi-tasking
stacksize4Basic multi-tasking
staged/-divisorTwo-stage integer division
staged/-sizeTwo-stage integer division
Standard conformance of GforthStandard conformance
starting Gforth tutorialStarting Gforth Tutorial
startup sequence for image fileModifying the Startup Sequence
Startup speedStartup speed
state - effect on the text interpreterHow does that work?
STATE valuescore-idef
state-smart words (are a bad idea)Combined words
staticClass Declaration
status-colorTerminal output
stderrGeneral files
stderr and pipesGforth in pipes
stdinGeneral files
stdoutGeneral files
stopBasic multi-tasking
stop-nsBasic multi-tasking
str<String words
str=String words
str=?Regular Expressions
String input formatLiterals in source code
string larger than pictured numeric output area (f., fe., fs.)floating-ambcond
string literalsString and character literals
string longer than a counted string returned by WORDcore-ambcond
string words with $$tring words
string-parseThe Input Stream
string-prefix?String words
string-suffix?String words
string,Counted string words
strings - see character stringsString representations
strings tutorialCharacters and Strings Tutorial
structStructure Glossary
struct usageStructure Usage
structs tutorialArrays and Records Tutorial
structure extensionStructure Usage
structure glossaryStructure Glossary
structure implementationStructure Implementation
structure naming conventionStructure Naming Convention
structure naming conventionStructure Naming Convention
structure of Forth programsForth is written in Forth
structure usageStructure Usage
structuresStructures
structures containing arraysStructure Usage
structures containing structuresStructure Usage
Structures in Forth200xForth200x Structures
structures using address arithmeticWhy explicit structure support?
sub-list?Locals implementation
substituteSubstitute
success-colorTerminal output
superclass bindingClass Binding
SuperinstructionsDynamic Superinstructions
swapData stack
symmetric divisionInteger division
SynonymAliases
synonymsAliases
syntax tutorialSyntax Tutorial
systemPassing Commands to the OS
system dictionary space required, in address unitscore-other
system documentationStandard conformance
system documentation, block wordsThe optional Block word set
system documentation, core wordsThe Core Words
system documentation, double wordsThe optional Double Number word set
system documentation, exception wordsThe optional Exception word set
system documentation, facility wordsThe optional Facility word set
system documentation, file wordsThe optional File-Access word set
system documentation, floating-point wordsThe optional Floating-Point word set
system documentation, locals wordsThe optional Locals word set
system documentation, memory-allocation wordsThe optional Memory-Allocation word set
system documentation, programming-tools wordsThe optional Programming-Tools word set
system documentation, search-order wordsThe optional Search-Order word set
system promptcore-idef

T
tableWord Lists
TAGS fileEmacs Tags
target compilercross.fs
target compilerCross Compiler
taskBasic multi-tasking
task-local dataTask-local data
terminal buffer, sizecore-idef
terminal input bufferThe Text Interpreter
terminal outputTerminal output
terminal sizeTerminal output
terminology for object-oriented programmingObject-Oriented Terminology
text interpreterIntroducing the Text Interpreter
text interpreterStacks and Postfix notation
text interpreterThe Text Interpreter
text interpreter - effect of stateHow does that work?
text interpreter - input sourcesThe Text Interpreter
text interpreter - input sourcesInput Sources
text-color:widget methods
text-emoji-color:widget methods
text-emoji-fade-color:widget methods
THENArbitrary control structures
thirdData stack
thisObjects Glossary
this and catchObjects Implementation
this implementationObjects Implementation
this usageMethod conveniences
ThisForth performancePerformance
threaded code implementationThreading
threading wordsThreading Words
threading-methodThreading Words
threading, direct or indirect?Direct or Indirect Threaded?
throwException Handling
THROW-codes used in the systemexception-idef
thruBlocks
tibThe Text Interpreter
tick (’)Execution token
TILE performancePerformance
time-related wordsKeeping track of Time
time&dateKeeping track of Time
TMP, TEMP - environment variableEnvironment variables
TOValues
TO on non-VALUEscore-ambcond
TO on non-VALUEs and non-localslocals-ambcond
to-method:User-defined TO and DEFER@
to-table:User-defined TO and DEFER@
to-thisObjects Glossary
tokens for wordsTokens for Words
TOS definitionStacks and Postfix notation
TOS optimization for primitivesTOS Optimization
touchdownactor methods
touchupactor methods
toupperCharacters
translate-dnumDealing with existing Recognizers
translate-method:Dealing with existing Recognizers
translate-ntDealing with existing Recognizers
translate-numDealing with existing Recognizers
translate-stateDealing with existing Recognizers
translate:Dealing with existing Recognizers
traverse-wordlistName token
trigonometric operationsFloating Point
trueBoolean Flags
truncation of floating-point numbersfloating-idef
tryException Handling
try-recognizeDealing with existing Recognizers
ttLocating exception source
tuckData stack
turnkey image filesModifying the Startup Sequence
TutorialTutorial
typeDisplaying characters and strings
types of localsGforth locals
types of stack itemsNotation
types tutorialTypes Tutorial
typewhiteDisplaying characters and strings

U
u-[doCounted Loops
U-DOCounted Loops
u, stack item typeNotation
u.Simple numeric output
u.rSimple numeric output
u*/Integer division
u*/modInteger division
u/Integer division
u/-stage1mTwo-stage integer division
u/-stage2mTwo-stage integer division
u/modInteger division
u/mod-stage2mTwo-stage integer division
U+DOCounted Loops
u<Numeric comparison
u<=Numeric comparison
u>Numeric comparison
u>=Numeric comparison
uallotTask-local data
ud, stack item typeNotation
ud.Simple numeric output
ud.rSimple numeric output
ud/modInteger division
UDeferTask-local data
ukeyedactor methods
um*Mixed precision
um/modInteger division
umaxSingle precision
uminSingle precision
umodInteger division
umod-stage2mTwo-stage integer division
unaligned memory accessSpecial Memory Accesses
uncolored-modeTerminal output
undefined wordcore-ambcond
undefined word, ', POSTPONE, ['], [COMPILE]core-ambcond
under+Single precision
unescapeSubstitute
unexpected end of the input buffercore-ambcond
unlockSemaphores
unloopCounted Loops
unmapped block numbersfile-ambcond
UNREACHABLEWhere are locals visible by name?
UNTILArbitrary control structures
UNTIL loopSimple Loops
unusedDictionary allocation
unused-wordsLocating uses of a word
unwind-protectException Handling
up@Task-local data
updateBlocks
UPDATE, no current block bufferblock-ambcond
updated?Blocks
upper and lower caseCase insensitivity
useBlocks
UserTask-local data
user input device, method of selectingcore-idef
user output device, method of selectingcore-idef
user spaceTask-local data
user variablesTask-local data
user-defined defining wordsUser-defined Defining Words
user'Task-local data
Uses of a wordLocating uses of a word
utimeKeeping track of Time
UValueTask-local data

V
v*Floating Point
ValueValues
value-flavoured localsGforth locals
valuesValues
varClass Declaration
varBasic Mini-OOF Usage
VariableVariables
variable-flavoured localsGforth locals
variablesVariables
variadic C functionsDeclaring C Functions
VarueVarues
varue-flavoured localsGforth locals
varuesVarues
versions, invoking other versions of GforthInvoking Gforth
vgluewidget methods
vglue@widget methods
view (called locate in Gforth)Locating source code definitions
viewing the documentation of a word in EmacsEmacs and Gforth
viewing the source of a word in EmacsEmacs Tags
virtual functionObject-Oriented Terminology
virtual function tableObjects Implementation
virtual machineEngine
virtual machine instructions, implementationPrimitives
visibility of localsWhere are locals visible by name?
vlistWord Lists
Vocabularies, detailed explanationVocabularies
VocabularyWord Lists
vocsWord Lists
vocstack empty, previoussearch-ambcond
vocstack full, alsosearch-ambcond
vp-bottomwidget methods
vp-leftwidget methods
vp-neededwidget methods
vp-reslidewidget methods
vp-rightwidget methods
vp-topwidget methods

W
wwidget methods
w-colorwidget methods
w,Dictionary allocation
w, stack item typeNotation
W:Locals definition words
w!Special Memory Accesses
w@Special Memory Accesses
w/oGeneral files
W^Locals definition words
w>sSpecial Memory Accesses
WA:Locals definition words
walignAddress arithmetic
walignedAddress arithmetic
warning-colorTerminal output
WARNING"Exception Handling
warningsException Handling
wbeSpecial Memory Accesses
wfield:Forth200x Structures
whereLocating uses of a word
where to go nextWhere to go next
wheregLocating uses of a word
WHILEArbitrary control structures
WHILE loopSimple Loops
widWord Lists
wid, stack item typeNotation
widgetMINOS2 object framework
Win32Forth performancePerformance
wior type descriptionNotation
wior values and meaningfile-idef
withinNumeric comparison
wleSpecial Memory Accesses
wordIntroducing the Text Interpreter
wordThe Input Stream
WORD buffer sizecore-idef
word glossary entry formatNotation
word list for defining localsLocals implementation
word listsWord Lists
word lists - exampleWord list example
word lists - why use them?Why use word lists?
word name too longcore-ambcond
WORD, string overflowcore-ambcond
wordlistWord Lists
wordlist-wordsWord Lists
wordlistsEnvironmental Queries
wordlists tutorialWordlists and Search Order Tutorial
wordsWords
wordsWord Lists
words used in your programStandard Report
words, forgettingForgetting words
wordsetNotation
wrap-xtDeferred Words
write-fileGeneral files
write-lineGeneral files
wrolBitwise operations
wrorBitwise operations
WTF??Debugging
wwLocating uses of a word

X
xwidget methods
x-sizeXchars and Unicode
x-widthXchars and Unicode
x,Dictionary allocation
x!Special Memory Accesses
x@Special Memory Accesses
x\string-Xchars and Unicode
x>sSpecial Memory Accesses
xalignAddress arithmetic
xalignedAddress arithmetic
xbeSpecial Memory Accesses
xc-sizeXchars and Unicode
xc-widthXchars and Unicode
xc,Xchars and Unicode
xc!+Xchars and Unicode
xc!+?Xchars and Unicode
xc@Xchars and Unicode
xc@+Xchars and Unicode
xc@+?Xchars and Unicode
xchar-Xchars and Unicode
XCHAR-ENCODINGEnvironmental Queries
XCHAR-MAXMEMEnvironmental Queries
xchar+Xchars and Unicode
xd,Dictionary allocation
xd!Special Memory Accesses
xd@Special Memory Accesses
xd>sSpecial Memory Accesses
xdbeSpecial Memory Accesses
xdleSpecial Memory Accesses
xemitDisplaying characters and strings
xfield:Forth200x Structures
xholdXchars and Unicode
xkeyXchars and Unicode
xkey?Single-key input
xleSpecial Memory Accesses
xorBitwise operations
xtIntroducing the Text Interpreter
xtExecution token
xt input formatLiterals in source code
XT tutorialExecution Tokens Tutorial
xt-newObjects Glossary
xt-seeExamining compiled code
xt-see-codeExamining compiled code
xt-simple-seeExamining compiled code
xt, stack item typeNotation
XT:Locals definition words
xt>nameName token
XTA:Locals definition words
xywhwidget methods
xywhdwidget methods

Y
ywidget methods

Z
zero-length string as a namecore-ambcond
Zsoter’s object-oriented modelComparison with other object models

インスタンス変数(instance variables)Object-Oriented Terminology

オブジェクト(object)Object-Oriented Terminology
オブジェクト(object)Objects Glossary
オブジェクト(object)Basic Mini-OOF Usage

クラス(class)Object-Oriented Terminology
クラス(class)Objects Glossary
クラス(class)Basic Mini-OOF Usage

セレクター(selector)Object-Oriented Terminology
セレクター(selector)Objects Glossary
セレクター呼び出し(selector invocation)Object-Oriented Terminology

メソッド(method)Object-Oriented Terminology
メソッド(method)Objects Glossary
メソッド(method)Class Declaration
メソッド(method)Basic Mini-OOF Usage

受信オブジェクト(receiving object)Object-Oriented Terminology

子クラス(child class)Object-Oriented Terminology


Footnotes

(1)

ただし、 1998 年に主要な商用 Forth ベンダーがネイティブ・コード・コンパイラーに切り替えたとき、 その基準は引き上げられてしまいました。

(2)

つまり、 ユーザーのホーム・ディレクトリに保存されます。

(3)

この表記法は、 後値記法(postfix notation) または RPN (逆ポーランド記法) と呼ばれます。

(4)

したがって、 ワード名には ) を使用しないことをお勧めします。

(5)

見つかったかどうかはわかりませんが、いまのところ、 見つからなかったと仮定します。 訳注: 12 というワードを定義することもでき、その場合はワード 12 の方が数値 12 より優先される

(6)

それは完全には真実ではありません。 キーボードの上矢印キーを押すと、 以前のコマンドにスクロールして戻り、 編集して再入力できます。

(7)

実際には、 いくつか微妙な違いがあります – The Text Interpreter

(8)

For example, /usr/local/share/gforth...

(9)

この一緒にした表記から、 浮動小数点数だけを分離するだけで、 簡単に分離された表記にできます。 例えば: ( n r1 ur2 -- r3 )( n u -- ) ( F: r1 r2 -- r3 ) になります

(10)

「ディクショナリー」(辞書)という用語は、 従来の辞書と同じように名前を検索するために使用される、 ワード・リストやヘッダーに組み込まれた検索データ構造を指すために使用されることがあります

(11)

正確には、 インタープリター機能(interpretation semantics)(see Interpretation and Compilation Semantics)を持っていません

(12)

ええ、まぁ、 移植可能な方法ではできません、 ぐらいな

(13)

next-case は、 他の case ワード群とは異なり、 名前にハイフン(-)が含まれています。 VFX Forth には値をドロップする nextcase があるので、これと区別するためです。

(14)

まあ、 多くの場合、 それは可能ではありますが、 標準的な移植可能な方法では変更できません。 Value を使用する方が安全です (続きを読んでください)まあ、多くの場合変更できますが、標準的な移植可能な方法では変更できません。 Value (続きを読んでください)

(15)

厳密に言えば、 compile,xt をコード領域内の何かに変換するために使用するメカニズムは実装に依存します。 スレッド実装は実行トークンを直接吐き出す場合がありますが、 別の実装はネイティブ・コード・シーケンスを吐き出す場合があります

(16)

このデータ領域への読み取りと書き込みは両方とも正当です。

(17)

研究課題: この例は ValueTO をあなた独自に実装するための出発点として使って見ましょう。 もし、 あなたが行き詰まった場合は、 '['] の振る舞いを調べてください。

(18)

標準の用語では、 「現在の定義に追加する」と言います

(19)

標準の用語でいうと、 デフォルトのインタープリター機能はその実行機能です。 デフォルトのコンパイル機能は、 その実行機能を現在の定義の実行機能に追加します

(20)

このトピックに関する議論の詳細は M. Anton Ertl, State-smartness—Why it is Evil and How to Exorcise it, EuroForth ’98. をご覧ください。

(21)

標準 Forth には、 インタープリター機能(interpretation semantics)が未定義のワード(例: r@)や、 定義された実行機能(execution semantics)が無いワード(例: s")や、 およびそのどちらも持たないワード(例: if)があります。 ただし、 インタープリター機能(interpretation semantics)と実行機能(execution semantics)の両方が定義されている場合、 それらは同一でであるため、 それらを同一のものとして扱います

(22)

これは、 ワードのコンパイル機能(compilation semantics)に依存します。 ワードにデフォルトのコンパイル機能(compilation semantics)がある場合、 xtcompile, を表します。 それ以外の場合(たとえば、 即実行ワードの場合)、 xtexecute を表します

(23)

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

(24)

このセクションは Introducing the Text Interpreter の拡大バージョンです

(25)

テキスト・インタープリターがキーボードからの入力を処理している時、 このメモリー領域はターミナル入力バッファー(terminal input buffer (TIB))と呼ばれ、 (既に廃止されていますが) ワード TIB および ワード #TIB によってアドレス指定されます

(26)

言い換えると、 テキスト・インタープリターは、 パース領域が空になるまで、 パース領域からの文字列をパースすることによって、 入力バッファーの内容を処理します

(27)

これがワードのパースの仕組みです

(28)

研究課題: 34 に置き換えたらどうなるでしょうか?

(29)

buffer の標準 Forth での定義は、 ディスク I/O を発生させないことを目的としています。 以前の block コマンドにより、 特定のブロックに関連付けられたデータがすでにブロック・バッファーに格納されている場合、 buffer はそのブロック・バッファーを返し、 ブロックの既存のコンテンツが利用可能になります。 それ以外の場合、 buffer は単にそのブロックに新しい空のブロック・バッファーを割り当てます

(30)

コンパイラー構築用語では「すべての場所はローカル変数の定義によって支配される」と言う

(31)

この機能は「拡張レコード」(extended records)とも呼ばれます。 これは、 オベロン・プログラミング言語が成した主な革新です。 言い換えれば、 この機能を Modula-2 に追加することで、 Wirth は新しい言語を作成し、 新しいコンパイラの記述等を行いました。 この機能を Forth に追加するには、 数行のコードが必要なだけでした。

(32)

さらに、 catch を呼び出すワードや、 objects.fs をロードする前に定義されているワードについては、 catch を再定義したように、 これらも再定義する必要があります: : catch this >r catch r> to-this ;

(33)

「メソッド・マップ」(method map)は著者自作用語です。 C++ 用語では、 仮想関数テーブル と言います。

(34)

この批評の長いバージョンは、Anton Ertl の On Standardizing Object-Oriented Forth Extensions (Forth Dimensions, May 1997) にあります。

(35)

あなたが C 言語コンパイラーの呼び出し規則を知っていれば、 通常は何らかの方法でそのような関数を呼び出すことはできますが、 その方法は通常、 プラットフォーム間で移植可能ではなく、 場合によっては C 言語コンパイラー間でさえ移植可能ではありません。

(36)

ただし、 著者の意見としては、 (実装がどうであれ)二重リンク・リストを使用する前によく考えるべきです。

(37)

Unix シェルは実際には 2 種類のファイルを認識します。 それは実行可能ファイルとデータ・ファイルです。 データ・ファイルは「インタープリター行」で指定されたインタープリターによって処理されます。 このインタープリター行は、 シーケンス #! で始まるデータ・ファイルの最初の行です。 インタープリター行で指定できる文字数には小さな制限(例: 32)がある場合があります。

(38)

データと浮動小数点スタックが別々に存在する場合でも、1 つのスタック表記法を使用します。 個別の表記は、 統一された表記から簡単に生成できます。