Forth(フォース)は、スタック指向のプログラミング言語およびそのプログラミング環境である。Forth はしばしば、かつての習慣に従ってすべて大文字で綴られることもあるが、頭字語ではない。

概要 Forth はスタック指向であり、逆ポーランド記法(RPN)と同様の後置記法による記述が一番の特徴である。その他の特徴としては、手続き型・命令型であり、言語としては全ての値は型としての区別なく扱われること(型システムが無いこと)、制御構造などもプログラム可能であること(リフレクション)といったものがある。

典型的な Forth の実装には、LISP におけるRead–eval–print loop(REPL)に対応する、入力されたワードを即座に実行する対話型のインタプリタモードと(これは、正規のオペレーティングシステムがないシステム向けのシェルにも適している)、後の実行のために一連のワードをコンパイルするモードのふたつのモードがある。後者にはコロン(:)というワードにより遷移しセミコロン(;)というワードで脱する。

初期の実装や移植性を目的とした実装にはスレッデッドコードを生成するものもあるが、近年の実装では他の言語のコンパイラのように最適化された目的コードを生成するものもある。

他の言語のシステムほどは人気はないが、商用においても Forthはいくつもの言語のベンダを引き止めるだけの十分なサポートを持っている。Forth は現在 Open Firmware のようなブートローダや宇宙開発NASA applications of Forth、組込みシステム、ロボット制御などに使われている。GNUプロジェクトによる実装であるGforthは活発にメンテナンスされている。

特徴 Forthの環境はコンパイラと対話形式のシェルが一体化している。実行時環境ともなる仮想マシンの中で、ユーザは「ワード」(words) と呼ばれるサブルーチンを対話的に定義し実行する。ソースコードとしてテスト、再定義、デバッグされることができるワードは、プログラム全体を再コンパイルしたり再起動することなく組み込まれる。変数、基本的な演算子など、すべての構文要素はプロシージャのように見える。たとえ特定のワードが最適化されても、サブルーチンの呼び出しを必要とするといけないので、これもまた依然としてサブルーチンとして有効である。言い換えれば、シェルは対話的に入力されたコマンドをそれが実行される前にマシンコードにコンパイルする(この振る舞いは一般的だが、必須ではない)。どのように結果のプログラムが格納されるかはForth 環境によってさまざまだが、理想的には手動でそのコードが再入力されたときとプログラムの実行は同じ影響を持つ。コンパイルされる関数はプログラムオブジェクトの特殊なクラスで対話的コマンドは厳密にインタープレットされる、C言語とUnixシェルの組み合わせとは対照的である。ほとんどのForth のユニークな性質はこの原理の結果である。対話性、スクリプティング、コンパイレーションがあることにより、ForthはBBC MicroやApple IIシリーズのようなリソースが限られたコンピュータでよく使われ、ファームウェアや小さなマイクロコントローラなどのアプリケーションで生き残っている。Cコンパイラがよりコンパクトで効率的なコードを生成しようとしている今まさにそのときでも、Forthの対話性における優位は保たれているのである。

スタック 再帰的なサブルーチンを持つすべてのプログラミング環境は制御フローのためにスタック (stack) を実装している。この構造は典型的にはローカル変数やサブルーチンの引数も格納する(C言語のようなシステムにおける call-by-value)。Forthにはしばしばローカル変数がないこともあるが、call-by-valueでもない。代わりに、中間的な値は第二のスタックにおいて保持される。ワードはスタックの最も上にある値を直接操作する。それゆえ、これは「パラメータ」または「データ」スタックと呼ばれたりもするが、ほとんどの場合は単に「スタック」である。それから、関数呼び出しスタックは「リンケージ」(lincage) もしくは「リターン」(return) スタックと呼ばれ、rstackとも略される。カーネルから提供された特殊な rstack 操作関数はそれがワード内で一時的なストレージとして使われることを可能にするが、その一方でこれは引数や操作データを渡すことに使うことはできない。Forthはスタックの概念をうまく利用しており、演算は逆ポーランド記法により記述される。このため構文解析が極めて単純となり、プログラムおよび処理系が小さくて済む。これは、機器組み込み用プログラムでは有利な特徴である。

また、ルーチンは「ワード」単位で記述され、コンパイルされる。これらの「ワード」を集積して「ディクショナリ」を形成する。一般的なFORTH処理系におけるプログラミングは、インタプリタ上でのワード作成の積み重ねであり、対話的に行える。開発中でも部分的な処理を動かしてのテストがやりやすく、それらを適宜組み合わせてのテストも容易である。

ほとんどのワードはスタック上でのその効果の観点から定義される。典型的には、引数はワードが実行される前にスタックの一番上に置かれる。実行後は引数は消去され、何らかの返り値で置き換えられている。数学的な操作をするためには、これを逆ポーランド記法のルールに従う。スタックの使用法を図解した以下の例を参照すること。

保守 Forthは単純だが拡張性のある言語である。そのモジュール性と拡張性はCADシステムのような高度なプログラミングをも可能にする。しかしながら、拡張性は未熟なプログラマがわかりにくいコードを書くのも促すため、このことは Forthに「記述専用言語」との評判ももたらしている。大規模で複雑なプロジェクトでも成功裏に使われてきていて、有能でよく訓練されたプロフェッショナルによって開発されたアプリケーションが、何十年にもわたって発展していくハードウェアプラットフォーム上でも容易に保守されることを証明している。Forth は天文学分野や宇宙開発分野という得意分野も持っている。

その移植性、効率的なメモリ使用、短い開発期間および高速な実行スピードのため、Forthは今日でもいまだ多くの組み込みシステム(小さなコンピュータ化されたデバイス)で使われている。これは近代的なRISCプロセッサ上で効率的に実装されてきており、マシン語としてのForthの利用も生み出されてきている。他のForthの用途としてはApple、IBM、サン・マイクロシステムズ、OLPC XO-1に使われるOpen Firmwareブートロムが含まれる。また、FreeBSDのFICL-based first stage boot controllerもある。

歴史 Forthはチャールズ・ムーアの1958年から絶え間なく開発されていた個人的なプログラミングシステムから考案された。1968年、家具と絨毯を扱う企業に雇われた際、このソフトウェアをミニコン上でFORTRANを使って書き直したものがForthの原型である、という。Forth が他のプログラマに最初に公開されたのは1970年代で、アメリカ国立電波天文台 (NRAO) にいたアメリカの によって始められたものである。1971年にNRAOの制御用ソフト作成において、ムーアはForthを完成させた。このNRAOにいた彼らの仕事のあと、チャールズ・ムーアとElizabeth Ratherは FORTH, Inc. を1973年に設立し、その後の 10 年でさらに磨きをかけるとともにいくつものプラットフォームに Forth システムを移植した。

1968年の"[t]he file holding the interpreter was labeled FOURTH, for 4th (next) generation software — but the IBM 1130 operating system restricted file names to 5 characters."において Forth は命名された。ムーアは compile-link-go 第三世代言語の後継、または「第四世代」ハードウェアのためのソフトウェアとして Forth を見ており、用語として使われるようになっていた第四世代言語 (4GL) ではなかった。ムーアは、アセンブラ・FORTRAN・BASICに続く4番目の言語という意味で、このソフトウェアに「fourth」と名付けるつもりだったが、このミニコンで取り扱えるファイル名は最大5文字であったため「FORTH」という名になったという。

チャールズ・ムーアはたびたび仕事を渡り歩いていたため、初期の言語開発の困難は異なるコンピュータアーキテクチャへの移植の容易さであった。Forth システムはしばしば新しいハードウェアを育てるために使われていた。たとえば、Forthは1978年の新しい 8086チップ上の最初の常駐ソフトウェアで、MacFORTHは1984年の最初のMacintoshの最初の常駐開発システムであった。

Forth, IncのmicroFORTHは1976年に始まった8080、6800、Z80マイクロプロセッサ向けに開発された。MicroFORTHは 後に1978年の6502のような他のアーキテクチャ向けのForthシステムを生成するのにホビーストたちにも使われた。広い普及は最終的に言語の標準化を誘導することとなった。共通の慣習は事実上の標準である FORTH-79 および FORTH-83 にそれぞれ1979年、1983年に成文化された。これらの標準は1994年に ANSIによって統合され、通常これは ANS Forth(ANSI INCITS 215-1994 (R2001)、ISO/IEC 15145:1997(E)のベースとなった)と呼ぶ。

Forthは1980年代にはとてもよく使われるようになったが、これは小さくかつ移植性が高いとして当時の小さなマイクロコンピュータにとてもよく適していたからである。すくなくともひとつのホームコンピュータ、イギリスのJupiter ACEは、そのROM常駐オペレーティングシステムにForthを持っていた。キヤノン・キャットもまた、そのシステムプログラミングのためにForthを使っていた。さらにRockwellも常駐Forthカーネルを持つR65F11とR65F12のシングルチップマイクロコンピュータを製造していた。

標準規格 Forth-77 Standard Forth-78 Standard Forth-79 Standard Forth-83 Standard Forth-94 Standard ISO/IEC 15145:1997(E) - Information technology - Programming languages - Forth (First edition: 1997-04-15)

プログラマの観点

Forthの後置記法は、データスタック(オペランドスタック、以下単にスタックと呼ぶ)を意識的に操作しなければならないというForthの特徴と密接に結びついている。すなわち、オペランドはそのままスタックに積まれ、後から現れる演算子などはスタックからそれを取り出して演算し、結果をスタックに積む、といったように動作する。多くの言語と違い、バッカス・ナウア記法で構文が定義されているわけでもなく、伝統的にはコンパイラは、スレッデッドコードと呼ばれるシンプルな構造の目的コードを直接生成するだけである。また、文法の修正のような、多くの言語では実装の内部に手を入れる変更が必要なことが、Forthではそのようなワードを定義することで可能である(これは、Lispのマクロに少し似ている)。なお、データスタックの他にリターンスタックがあり、そちらはワードの呼出しの後で手続きを再開するアドレス等が積まれるスタックである(CPUのいわゆるスタックに似ている)。

たとえば、25 * 10 + 50 は、次のように入力して計算する(「⏎」は、改行の入力。「ok」はForth処理系が結果の出力の後に付けるプロンプト)。

25 10 * 50 + . ⏎ 300 ok

まず最初に数値 25 と 10 がこの順でスタックに積まれる。

ワード * がスタックの一番上にあるふたつの数を乗算し、その積に置き換える。

それから数値 50 をスタックに積む。

ワード + がこれに先ほどの積を加算する。最後に、. コマンドがスタックのトップを取り出して、それを、ユーザの端末に結果として出力する。

4 5 + . これは、スタックに4を積み、さらに5を積み、スタック上の2つの値を取り出して加算、その結果をスタックに戻し、スタックの値を表示する操作を示している。

上記のように入力してエンター(⏎:リターン)を打鍵すると、画面上には下記のように結果が表示される。 4 5 + . ⏎ 9 ok

Forth の構造化機能でさえもスタックベースである。たとえば、

: FLOOR5 (n -- n') DUP 6 < IF DROP 5 ELSE 1 - THEN ;

このコードは 次のコマンドを使うことによって FLOOR5 が呼ばれる新しいワード(繰り返すが、「ワード」という用語はサブルーチンとして使われている)を定義する。DUPはスタックの数値を複製する。6がスタックの一番上に 6 を配置する。ワード < はスタックの一番上の二つの数(6 と DUP で複製された入力の値)を比較し、真偽値で置き換える。IFは真偽値をとり、その直後のコマンドを実行するか、ELSEまでスキップするかを選択する。DROPはスタックの上の値を放棄する。そして、THENは条件分岐の終端である。括弧に囲まれたテキストは、このワードが期待するスタックの数と値を返すかどうかを説明するコメントである。ワード FLOOR5 はC言語で書かれた次の関数に相当する。

int floor5(int v) { return v < 6 ? 5 : v - 1; }

この関数はより簡潔につぎのように書かれる。

: FLOOR5 (n -- n') 1- 5 MAX ; このワードは次のように実行できる。

1 FLOOR5 . ⏎ 5 ok 8 FLOOR5 . ⏎ 7 ok

最初にインタプリタは数値 1(もしくは 8)をスタックにプッシュし、それからこの数値を再びポップし結果をプッシュする FLOOR5 を呼び出す。最後に、「.」の呼び出しは値をポップし、ユーザの端末にそれを表示する。

機能 Forthには公式の文法仕様はない。代わりに、簡単なアルゴリズムによって定義される。インタプリタは、ユーザ入力デバイスからの入力行を読み取り、その後、空白を区切り文字としてワード (words) を構文解析する。一部のシステムは、追加の空白文字を認識する。インタプリタがワードを見つけると、そのワードをディクショナリ (dictionary) から検索する。ワードが見つかった場合、インタプリタはそのワードに関連付けられたコードを実行し、その後、入力ストリームの残りを解析する。ワードが見つからない場合、ワードは数値と見なされ、数値に変換し、スタックにプッシュしようとする。変換に成功した場合、インタプリタは入力ストリームの構文解析を続行する。それ以外の場合、検索と数値変換の両方が失敗した場合、インタプリタはワードをエラーメッセージと共に出力し、入力ストリームをフラッシュして新しいユーザ入力を待機する。

新しいワードの定義は、ワード :(コロン)で始まり、ワード ;(セミコロン)で終了する。たとえば、

: X DUP 1+ . . ;

とすると、ワード X がコンパイルされ、ディクショナリで見つけることができるようになる。コンソールから 10 X と入力して実行すると、11 10 と表示される。

Forthでは「+」や「.」などの演算子や出力機能などの全てがワードである。こういった組み込み済みのワードと、ユーザが後からコロン定義(コンパイラ)で追加したワードと、2つの間に本質的な差異はない。コンパイルしたワードはただちに環境に組み込まれ、インタプリタより単独で実行できるようになる。つまり、そのForth処理系を拡張するのである。このような点より、Forthは自己拡張性が高いと云われる。

プログラムの開発においては、処理毎に区切ってワードとして順次構築していくので、注意深く進めていけば自然ときれいに構造化されることになる。ワードは単独で実行できるため、部分に分けてのデバッグも容易である。また、それらのワードを使ってテスト用の処理(ワード)を気軽に作成して実行・テストできる。

ほとんどのForthシステムには、プロセッサの機能を使用してワードを記述するためのアセンブラが含まれている。Forthのアセンブラは、一般的に、命令のパラメータが命令の前に来る逆ポーランド構文を使用する。典型的な逆ポーランド構文のアセンブラは、オペランドをスタックに用意し、ニーモニックが命令全体をメモリにコピーする最後の手順としている。Forthのアセンブラは本質的にマクロアセンブラであり、Forthシステム内での役割に応じてレジスタのエイリアスを定義することが容易だ。例えば、データスタックポインタとして使用されるレジスタの場合、「dsp」というエイリアスを定義することができる。

オペレーティングシステムとファイル、マルチタスク 古典的な Forth システムでは伝統的にオペレーティングシステムもファイルシステムも使われない。コードはファイルに格納される代わりに、ソースコードは物理的なディスクアドレスに書かれたディスクブロックに格納される。ワード BLOCK はディスクスペースの1キロバイトサイズのブロックの数値からデータを格納しているバッファのアドレスへの変換に割り当てられ、Forth システムによって自動的に管理される。固定されたディスクブロック範囲にファイルが配置されるときには、システムのディスクアクセスが使われる実装もある。たいていはこれらはディスクブロックごとのレコードの整数をつかって、固定長バイナリレコードして実装される。高速な検索はキーデータ上のハッシュアクセスによって実現される。

マルチタスキングは、通常、協調的なラウンドロビン・スケジューリングが採用されるが、マルチタスキングのワードとサポートはANSI Forth標準ではカバーされていない。 ワード PAUSE は、次のタスクの配置や実行コンテキストのリストアための 現在のタスクの実行コンテキストの保存に使われる。どちらのタスクも自分自身のスタックやいくつかのコントロール変数のコピー、スクラッチエリアを持っている。タスクのスワップは単純で、効率的である。その結果、Forth マルチタスクは Intel 8051, Atmel AVR, and のような非常に単純なマイクロコントローラでさえ有効である。

その一方で、Microsoft WindowsやLinux、Unixのようなホストオペレーティングシステムのもとで実行され、ソースやデータのファイルのためにホストオペレーティングシステムのファイルシステムを利用する Forth システムもある。ANSI Forth 規格では I/O のために使われたワードについて書かれている。他の標準的でない機能はホスト OS やウィンドウシステムへのシステムコールを発行するためのメカニズムも含み、多くはオペレーティングシステムから提供されるスケジューリングを採用する拡張を提供する。典型的には、タスク作成、一時停止、解体、および優先順位の変更のために、スタンドアロンのForthの PAUSEワードとは大きくて異なったワードのセットを持っている。

セルフコンパイルとクロスコンパイル すべてのソースコードとともに十分な機能を有する Forth システムは自身をコンパイルすることができ、Forth プログラマはこのようなテクニックを普通メタ・コンピレーション (meta-compilation) と呼ぶ(ただし、この用語は普通の定義であるメタコンピレーションとは厳密には一致しない)。通常の方法はコンパイルされたビットをメモリに配置する一握りのワードの再定義である。コンパイラのワードはメモリ内のバッファエリアにリダイレクトされることができるフェッチとストアの、特別に命名されたバージョンを使う。このバッファエリアはコードバッファというより異なるアドレスから始まるメモリ領域へのシミュレートやアクセスをする。このコンパイラは対象のコンピュータのメモリとホストの(コンパイルする)コンピュータのメモリの両方にアクセスするワードを定義する。

フェッチやストア操作がコード空間に再定義されたあと、コンパイラやアセンブラなどはフェッチやストアの新たな定義を使って再コンパイルされる。これはコンパイラとインタプリタのすべてのコードの効果的な再利用である。それから、Forth システムのコードはコンパイルされるが、このバージョンはバッファに格納される。このメモリ内のバッファはディスクに書きこまれ、これをテストのために一時的にメモリにロードする方法が提供される。新たなバージョンがきちんと機能するようなら、これは以前のバージョンに上書きされる。

異なる環境のためのバリエーションが多数存在する。組み込みシステム向けには、代わりに他のコンピュータのためにコードが書かれることになるが、このテクニックはクロスコンピレーションとして知られ、シリアルポートや単独の TTL ビット越しでさえ、その上オリジナルのコンパイルするコンピュータのワード名やディクショナリの他の実行されない部分も維持する。このようなForthコンパイラのための最小の定義は バイト単位のフェッチやストアをするワードと、実行される Forth ワード を命令するワードである。しばしばもっとも多くの時間のかかるリモートのポートへの書き込みの部分は、フェッチやストア、実行を実装するための初期化プログラムの構築であるが、多くの現代的なマイクロプロセッサ(Motorola CPU32など)は、このタスクを排除する統合されたデバッグ機能を持っている。

言語の構造 Forthの基本的なデータ構造は、「ワード」を実行可能なコードや名前のつけられたデータ構造を対応させる「ディクショナリ」である。このディクショナリは、門番(通常は NULL ポインタ)が発見されるまで最も新しく定義されたワードから最も古いワードまで進むリンクを用いた連結リストのツリーとして、メモリに展開される。コンテキストスイッチは異なる葉で開始するためにリスト検索を引き起こす。首位のメインの幹への枝のマージは最終的にルートの門番へ戻ってくるので、連結リスト検索は継続する。そこはさまざまなディクショナリになることができる。メタコンピレーションのような稀なケースでは、ディクショナリは隔離されスタンドアロンである。この効果は名前空間のネストのそれに似ていて、コンテキストに依存するキーワードのオーバーロードが可能である。

定義されたワードは一般的にヘッドとボディからなり、ヘッドは名前フィールド (NF) とリンクフィールド (LF) からなり、ボディはコードフィールド (CF) とパラメータフィールド (PF) からなる。

ディクショナリのエントリのヘッドとボディは、隣接していないかもしれないので別々に扱われる。たとえば、Forth プログラムが新しいプラットフォームのために再コンパイルされたとき、ヘッドはコンパイルするコンピュータに残るかもしれないが、ボディは新しいプラットフォームに行ってしまっている。組み込みシステムのようないくつかの環境によっては、ヘッドは不必要にメモリを占有する。しかしながら、もしターゲット自身が対話的なForthをサポートすることを期待されるなら、クロスコンパイラによってはヘッドをターゲット内に配置するかもしれない。

ディクショナリのエントリ ディクショナリの厳密なフォーマットは規定されず、実装に依存する。しかしながら、いくつかのコンポーネントはほとんどいつも提示しており、しかし、厳密なサイズと順序は変わるかもしれない。記述された構造、ディクショナリエントリはこのように見えるかもしれない。

structure byte: flag 3bit flags + length of word's name char-array: name name's runtime length isn't known at compile time address: previous link field, backward ptr to previous word address: codeword ptr to the code to execute this word any-array: parameterfield unknown length of data, words, or opcodes end-structure forthword

この名前フィールドはワードの名前の長さ(典型的には32バイト)を与えるプリフィックスで開始し、何ビットかはフラグ用である。それからワードの名前の文字表現がプリフィックスのあとに続く。特定のForth実装に依存するが、アラインメントのためひとつ以上の NUL ('0') バイトがあるかもしれない。

リンクフィールドは以前に定義されたワードへのポインタを含む。このポインタは次に古い隣接するワードへの、相対的な変位かもしれないし、絶対的なアドレスかもしれない。

このコードフィールドポインタは コードを実行するワードのアドレスか、パラメータフィールド内のデータか、プロセッサ直接実行するであろうマシンコードの開始のいずれかになるだろう。ワードを定義するコロンでは、コードフィールドポインタはリターンスタック上の現在の Forth 命令ポインタ (instruction pointer, IP) を保存し、ワードを実行継続するための新たなアドレスを用いてIP をロードするワードを指し示す。これはプロセッサの call/return 命令が行っているのと同様である。

コンパイラの構造 コンパイラ自体は単一のプログラムではない。それはForthのワードで構成され、システムから見え、プログラマによって使用できる。これにより、プログラマは特別な目的のためにコンパイラのワードを変更できる。

名前フィールドの中にある「コンパイル時」フラグは、「コンパイル時」の振る舞いを持つワードに設定される。ほとんどの単純なワードは、コマンドラインに入力されるか、コードに埋め込まれるかにかかわらず、同じコードを実行する。これらをコンパイルするとき、コンパイラは単にコードを配置するか、ワードへのスレッドポインタを配置する。

コンパイル時のワードの典型的な例は、IFやWHILEなどの制御構造だ。Forthのほぼすべての制御構造およびほぼすべてのコンパイラは、コンパイル時のワードとして実装される。Ulrich HoffmannのpreForthで使用される条件付きリターンワード ?EXIT など、一部の稀な制御フローワードを除いて、Forthのほぼすべての制御フローワードは、プリミティブワードとその分岐アドレスとのさまざまな組み合わせをコンパイルするためにコンパイル中に実行される。たとえば、IFやWHILEなどのワード、およびそれに対応するワードは、BRANCH(無条件分岐)と(スタックから値をポップし、偽の場合に分岐する)を設定する。カウンティングループの制御フローワードも同様に機能するが、カウンタと一緒に動作するプリミティブワードの組み合わせを設定する。コンパイル中に、データスタックは、制御構造のバランス、入れ子構造、および分岐アドレスの後方パッチングをサポートするために使用される。たとえば、次のスニペット:

... DUP 6 < IF DROP 5 ELSE 1 - THEN ...