コンテンツにスキップ

Forth

出典: フリー教科書『ウィキブックス(Wikibooks)』

はじめに

[編集]

Forthはスタック指向のプログラミング言語であり、そのシンプルさと柔軟性から広く用いられています。 本書は、プログラマがForthを効果的に使いこなすための手引書として設計されています。例示や実践的なヒントが豊富に盛り込まれており、読者は理論を理解しながら実際のプロジェクトに応用できるでしょう。 本書を通じて、Forthの魅力と実力を十分に体感し、その利用価値を最大限に引き出すことができるでしょう。 本書では、ForthのCore Word Setに準拠した標準的なForthシステムを題材とします。

Forthプログラミングの基礎

[編集]

Forthはスタック指向のプログラミング言語であり、独自の特徴を持っています。以下では、Forthプログラミングの基礎を解説します。

Hello, world!

[編集]

Forthでは、.(ワードはデリミター )に囲まれた文字列を指定し表示します。

.( Hello, world!) cr

これにより、"Hello, world!" が表示されます。

スタック操作

[編集]

スタックは、Forthにおける中心的なデータ構造です。ほとんどのワードは、スタックから値を取り出したり、スタックに値を代入したりします。スタックは、後入れ先出し(LIFO; Last In First Out)のデータ構造で、Forthの演算はスタック上で行われます。

[編集]

以下は、Forthでの簡単な計算の例です:

3 4 + .
まず3をスタックに置き、4 + . を解釈します。
次に、4をスタックに置き、3 4 となり、+ . が残ります。
そして、+ はスタックのトップの2つの要素を加算して結果を戻し、スタックには7だけが残ります。
最後に、.(ドット)はスタックから7を取り除いてそれを表示します。
コード 3 4 + .
スタックトップ 3 4 7
3
出力 7


シャント・ヤード・アルゴリズム
シャント・ヤード・アルゴリズム(Shunting Yard Algorithm)は、中置記法の数式を逆ポーランド記法(RPN)に変換するアルゴリズムです。Edsger Dijkstraが1960年代に考案したこのアルゴリズムは、コンパイラや電卓のRPN変換に広く使われています。

アルゴリズムの手順は以下の通りです:

  1. 出力用の逆ポーランド記法列と、一時的なオペレータスタックを用意する
  2. 入力の中置記法式をトークン(数値、オペレータ、括弧)に分ける
  3. トークンを以下のルールで処理する:
    • 数値の場合、そのまま出力列に追加
    • 開き括弧の場合、オペレータスタックにプッシュ
    • 閉じ括弧の場合、開き括弧が出るまでオペレータスタックから出力列にポップ
    • 演算子の場合、オペレータスタックのトップの演算子の優先順位以上になるまでスタックからポップして出力列に追加し、その後プッシュ
  4. 最後にオペレータスタックを出力列に残り詰めにポップする

例えば、3 + 4 * 5は以下のようにRPN列3 4 5 * +に変換されます:

  1. 初期状態 (出力列=[],スタック=[])
  2. 3を処理 (出力列=[3],スタック=[])
  3. +を処理 (出力列=[3],スタック=[+])
  4. 4を処理 (出力列=[3,4],スタック=[+])
  5. *を処理 (出力列=[3,4],スタック=[*,+])
  6. 5を処理 (出力列=[3,4,5],スタック=[*,+])
  7. スタックの残りをポップ (出力列=[3,4,5,*,+])
このように中置記法式をスタックを用いて機械的に逆ポーランド記法に変換できます。シャント・ヤード・アルゴリズムの名前は、出力列と一時スタックを使ってトークンを"シャントする(移動させる)"様子に由来しています。

コメント

[編集]

Forthでは、コメントは括弧 ( ) で囲んで表現されます。例えば:

( これはコメントです )

また \ から行末までもコメントです

3 4 + \ 3 + 4 の結果をスタックトップに残す

コメントは、インタプリタの動作に影響を与えません。

スタックエフェクト

[編集]

Forthの各ワードは、スタック上で特定の操作を行います。ワードのドキュメントでは、それがスタックに対してどのような影響を与えるかを示す記述があります。例えば:

+ ( a b -- c )

この記述は、+ワードが2つの引数 ab を消費し、1つの結果 cをスタックに残すことを意味します。

Forthのワードとは?その定義と使用について

[編集]

Forthにおける「ワード」とは、基本的には単語や言葉を意味しますが、Forthプログラミングでは、より具体的には次の2つの意味を持ちます。

  1. 定義済みの単語
    ワードは、Forthの語彙において定義された単語や操作を指します。Forthでは、これらのワードはスタック操作や制御構造、算術演算などの基本的な機能を表します。例えば、+は2つの数値を取り出して足し合わせ、結果をスタックに戻すワードです。
  2. 新しい単語の定義
    ワードは、Forthプログラム内で新しい単語を定義するための仕組みとしても使われます。これは、既存のワードを組み合わせて、新しい機能を持つ単語を定義することを意味します。

ワードの定義と使用例

[編集]

Forthにおけるワードの定義と使用についてさらに詳しく説明します。

  1. ワードの定義 :
    ワードの定義は、通常、次のような形式で行われます:
    : ワード名  定義 ;
    
    ここで、: ワード名は新しいワードの定義を開始し、;は定義の終了を示します。定義部分には、既存のワードや操作を組み合わせて、新しい機能を実現するForthのコードが記述されます。
    例えば、SQUAREという新しいワードを定義してみましょう。
    : SQUARE ( n -- n*n ) DUP * ;
    
    ここでは、SQUAREというワードが定義されています。このワードは、スタックから1つの数値を取り出し、その数値を自乗して結果をスタックに戻します。
  2. ワードの使用 :
    定義したワードは、Forthプログラム内で直接使用することができます。
    5 SQUARE . cr  \ スタックから5を取り出して自乗し、結果を表示する
    
    上記の例では、5 SQUARE . cr というForthコードが実行されます。まず、5がスタックに積まれ、次にSQUAREワードが実行されて、5の自乗である25が計算されます。.はスタックのトップの値を表示しますので、結果として25が表示され、最後の cr 改行します。
  3. 組み込みワードとカスタムワード :
    Forthには組み込みのワード(+, -, *, /, dup, crなど)がありますが、ユーザーはこれに加えて独自のワードを定義することができます。カスタムワードを使うことで、プログラムをより効率的に構築できます。
  4. 再帰的なワード :
    Forthでは、ワードは自身の中で再帰的に呼び出すことができます。これにより、複雑な演算や制御構造を簡潔に表現することが可能です。

ワードの定義と使用は、Forthプログラムの中核的な部分を構成します。これにより、プログラムの再利用性、メンテナンス性、可読性が向上し、効率的なコーディングが実現されます。

基本的な制御構造

[編集]

Forthの制御構造は、他のプログラミング言語とは異なり「制御構造もワードの集合から定義する」という独自の特徴を持っています。Forthでは、基本的な制御構造として条件分岐とループが利用されます。

制御構造に関するワードは、コンパイル時(つまり : から ; までの間)でのみ使用でき会話モードでは使用できません。

条件分岐

[編集]

Forthでは、条件分岐は主に IF, ELSE, THEN のワードを組み合わせて実現されます。

: ABS ( n -- |n| ) 
  DUP 0< 
  IF 
    NEGATE 
  THEN
;

この例では、ABSというワードを定義しています。このワードは、スタックから数値を取り出し、その絶対値を計算して結果をスタックに戻します。DUP 0< はスタックトップの値が0より小さいかどうかをチェックし、IF から THEN の間に記述されたブロックが条件を満たす場合に実行されます。つまり、もし数値が負の場合は NEGATE が実行されます。

ループ

[編集]

Forthでは、ループは DO, LOOPBEGIN, WHILE, REPEAT のワードを使って実装されます。

: COUNTDOWN ( n -- )
 0 DO
   I .
 LOOP
;

上記の例では、0から与えられた数値(n)までカウントダウンするCOUNTDOWNというワードを定義しています。

その他の制御構造

[編集]

Forthには他にも、CASE, OF, ENDOF, ENDCASE を使った条件分岐や、IF-ELSE-ENDIF 構造、BEGIN-UNTILBEGIN-WHILE-REPEAT など、さまざまな制御構造が存在し、新たな制御構造をユーザーが定義することすら出来ます。

Forthの制御構造は、スタック指向の特性に基づいてシンプルかつ柔軟に設計されており、独自のプログラミングスタイルを持つことが特徴です。

スタック操作の基本命令

[編集]

Forthにはスタック上のデータを操作するための基本的な命令があります。

  • DUP: ( n -- n n ) スタックのトップの要素を複製する
  • DROP: ( n -- ) スタックのトップの要素を削除する
  • SWAP: ( n1 n2 -- n2 n1 ) スタックのトップとその下の要素を交換する
  • OVER: (n1 n2 -- n1 n2 n1 ) スタックのトップの2つ下の要素を複製してトップに置く
  • ROT: ( n1 n2 n3 — n2 n3 n1 ) スタックのトップの3つ下の要素をトップに回転する

基本的な演算

[編集]

Forthには、算術演算(+-*/など)、論理演算(ANDORNOT)、比較(=<>><など)の基本ワードが用意されています。これらのワードは、スタック上の値を取り、演算結果をスタックに代入します。

再帰的呼び出し

[編集]

Forthで再帰的呼び出しを行う場合、ワード recurse を使ってワード自身を表します。 再帰的なワードを定義する方法について説明します。

階乗

[編集]

再帰的な階乗(factorial)を定義します。

: factorial ( n -- result )
  dup 0= if             \ スタックのトップの値が0かどうかをチェック
    drop 1              \ もし0ならば、結果は1として処理を終了
  else
    dup 1 -             \ スタックのトップの値から1を引いた値をコピー
    recurse *           \ 再帰的に自身を呼び出して、その結果にスタックのトップの値を掛ける
  then 
;

この定義の内容を説明します。

  • ワード factorial は、( n -- result ) のスタックエフェクトを持ちます。n は階乗を計算する整数です。
  • dup 0=: スタックのトップの値が0かどうかをチェックします。
    • もし0ならば、drop 1 が実行されて、スタックのトップの値(n)が削除され、結果として 1 がスタックにプッシュされます。
  • else: もしスタックのトップの値が0でない場合は、次の操作が実行されます。
    • dup 1 -: スタックのトップの値から 1 を引いた値をスタックにプッシュします。これは n - 1 の計算です。
    • recurse *: 自身の factorial を再帰的に呼び出して、結果に対してスタックのトップの値(n)を掛けます。これにより、n! = n * (n-1)! の計算が行われます。
  • then: if ブロックの終了を示します。

例えば、5 factorial . を実行すると、5 の階乗(5!)が計算されます。

5 factorial .

この例では、5! の計算手順は以下の通りです:

  • 5 がスタックにプッシュされる。
  • factorial が呼び出される。
    • dup 0=: スタックのトップの値が0でないので、次の処理が実行される。
    • dup 1 -: 5 - 1 が計算されて 4 がスタックにプッシュされる。
    • recurse *: 4 の階乗 (4!) が再帰的に計算される。
      • dup 0=: スタックのトップの値が0でないので、次の処理が実行される。
      • dup 1 -: 4 - 1 が計算されて 3 がスタックにプッシュされる。
      • recurse *: 3 の階乗 (3!) が再帰的に計算される。
        • ...
          • 最終的に、1 の階乗 (1!) が計算されて 1 がスタックにプッシュされる。
        • 5 *: 54! の結果を掛けて 120 がスタックにプッシュされる。
      • 5 *: 5120 の結果を掛けて 120 がスタックにプッシュされる。
  • . (dot) コンパイラワ

最大公約数(GCD)

[編集]

再帰的にユークリッドのアルゴリズムを使用して最大公約数(Greatest Common Divisor, GCD)を計算します。

: gcd ( u1 u2 -- gcd )
  dup 0= if           \ スタックのトップの値が0かどうかをチェック
    drop              \ スタックのトップの値が0ならば削除して終了
  else
    swap over mod recurse  \ スタックのトップと2番目の値を入れ替えて、modを計算し再帰的に呼び出す
  then 
;

このプログラムの動作をステップごとに説明します:

  1. dup 0=: スタックのトップの値(u1)が0かどうかをチェックします。
    • もし0ならば、drop が実行されて、スタックのトップの値(u1)が削除されます。この場合は再帰の終了条件となります。
  2. else: もしスタックのトップの値(u1)が0でない場合は、次の操作が実行されます。
  3. swap over mod recurse:
    • swap: スタックのトップと2番目の値を入れ替えます。これにより、u1u2 の値が入れ替わります。
    • over: スタックの2番目の値(入れ替え前の u1)をスタックのトップにコピーします。
    • mod: スタックのトップの2つの値を取り出して、u1 % u2u1u2 で割った余り)を計算し、その結果をスタックにプッシュします。
    • recurse: 自身の gcd を再帰的に呼び出します。これにより、新しい u1u2 の値がスタックに積まれ、再帰的に最大公約数が計算されます。
  4. then: if ブロックの終了を示します。

例えば、12 18 gcd . を実行すると、以下のように動作します:

  • 1218 がスタックにプッシュされます。
  • gcd が呼び出されます。
    • dup 0=: 18 は0ではないので、次の処理に進みます。
    • swap over mod recurse: 1812 が入れ替わり、18 % 12 が計算されます(余りは 6)。
    • gcd が再帰的に呼び出され、新しい値 126 がスタックにプッシュされます。
      • dup 0=: 6 は0ではないので、次の処理に進みます。
      • swap over mod recurse: 612 が入れ替わり、6 % 12 が計算されます(余りは 6)。
      • gcd が再帰的に呼び出され、新しい値 66 がスタックにプッシュされます。
        • dup 0=: 6 は0ではないので、次の処理に進みます。
        • swap over mod recurse: 66 が入れ替わり、6 % 6 が計算されます(余りは 0)。
        • 0=: 0 が0なので、drop が実行されて、スタックのトップの値 0 が削除されます。
  • 最終的に、スタックのトップには 6 が残ります。これが 1218 の最大公約数です。

したがって、12 18 gcd . を実行すると、6 が表示されます。

変数の宣言・代入と参照

[編集]

Forthにおける VARIABLE は、変数を定義するためのワードです。Forthでは、変数はメモリ内のアドレスに名前を関連付ける方法で定義されます。

以下は、VARIABLE を使用して変数を定義する方法とその使い方の例です。

VARIABLE の使用方法

[編集]

VARIABLE は以下のように使用します:

VARIABLE my-var       \ my-var という名前の変数を定義する

この行は、名前が my-var の変数を定義します。この変数はメモリ内の特定のアドレスに関連付けられます。

変数の使用方法

[編集]

定義された変数を使用するには、@(fetch)や !(store)などのワードを使います。これらのワードを使って変数の値を取得したり設定したりできます。

- @ は、変数の値をスタックにプッシュします。 - ! は、スタックのトップの値を変数に代入します。

例えば、次のように変数に値を設定して取得します:

123 my-var !     \ 変数 my-var に値 123 を代入する
my-var @ .       \ 変数 my-var の値を表示する

この例では、変数 my-var123 を代入し、その後 @ を使って変数の値を取得して表示しています。

実際の例

[編集]

以下は、VARIABLE を使って変数を定義し、値を設定して取得する具体的な例です:

VARIABLE my-var      \ my-var という名前の変数を定義する

123 my-var !         \ 変数 my-var に値 123 を代入する

my-var @ .           \ 変数 my-var の値を表示する

この例では、変数 my-var を定義し、123 を代入してその値を表示しています。! で変数に値を設定し、@ で変数の値を取得しています。

変数はプログラムでデータを保持する際に便利です。Forthでは、変数の値は直接メモリ上に代入されるため、効率的なデータ管理が可能です。

定数の定義と参照

[編集]

Forthの CONSTANT は、定数を定義するためのワードです。定数は変更されることのない値であり、プログラムの中で何度も使われる値を簡潔に表現するために使用されます。

CONSTANT の使用方法

[編集]

CONSTANT は以下のように使用します:

123 CONSTANT my-const       \ my-const という名前の定数を定義する

この行は、名前が my-const の定数を定義し、その値を 123 に設定します。定数はプログラム中で使用される値を一元管理し、可読性を向上させます。

定数の使用方法

[編集]

定義された定数は、その名前を使ってプログラム中で値を参照することができます。

my-const .     \ 定数 my-const の値を表示する

この例では、定数 my-const の値が表示されます。

実際の例

[編集]

以下は、CONSTANT を使って定数を定義し、その定数を使用する具体的な例です:

123 CONSTANT my-const       \ my-const という名前の定数を定義する

my-const .                  \ 定数 my-const の値を表示する

この例では、定数 my-const123 に設定し、その後定数の値を表示しています。

定数はプログラム中で共通の値を使い回す際に便利です。Forthの定数は一度定義されると値が変更されることはなく、プログラム全体で一貫性のある値を提供します。

基数

[編集]

Forthの BASE は、数値の基数(n進数のn)を設定するための変数です。Forthでは、数値の表現において基数を変更することができます。通常、Forthはデフォルトで10進数を使用しますが、BASE を変更することで他の基数(2進数、16進数など)で数値を表現することが可能です。

BASE の使用方法

[編集]

BASE は以下のように使用します:

16 BASE !      \ 基数を16進数に設定する

この行は、BASE の値を 16 に設定しています。これにより、その後の数値の表示や入力が16進数で行われるようになります。

基数の変更と数値の表現

[編集]

BASE を変更することで、その後の数値リテラルの表現が変わります。例えば、BASE を16進数に設定した場合、数値リテラルは16進数として解釈されます。

16 BASE !      \ 基数を16進数に設定する

FF .           \ 16進数の255を表示する

この例では、BASE を16進数に設定してから、FF を表示すると、16進数の FF (10進数の 255)が表示されます。

デフォルトの基数と再設定

[編集]

Forthのデフォルトの基数は10進数です。基数を変更した後は、必要に応じて再度基数を10進数に戻すことができます。

DECIMAL BASE !   \ 基数を10進数に戻す

この行は、BASE を10進数に再設定しています。これにより、数値リテラルは再び10進数として解釈されるようになります。

よくある間違い:

10 BASE !   \ 基数を16進数から10進数に戻す???
として基数を10に戻そうとするミスがあります。
コードの 10 は16進数として解釈されるので基数は16のままです。

注意点

[編集]

BASE を変更すると、数値の解釈や表示が変わるため、基数の変更後には適切な基数に戻すことが重要です。また、BASE の値は変数として扱われるため、必要に応じて保存や復元することができます。

基数を変更することで、特定の基数で数値を扱うプログラムを簡単に記述することができます。

イミディエイトワード

[編集]

イミディエイトワード(immediate word)は、Forthプログラミング言語において特定の動作を持つワードの一種です。イミディエイトワードは、通常のワードとは異なり、コンパイル時ではなく実行時に直ちに処理されます。

以下は、イミディエイトワードの特性について詳しく説明します:

  1. 実行時処理:
    • イミディエイトワードは、定義された時点ではコンパイルされず、実行時に直ちに解釈および実行されます。
    • 通常のワード(コンパイルワード)は、単なるコンパイル時にスタックに動作を積むだけであり、後で実行されますが、イミディエイトワードは定義された時点ですぐに実行されます。
  2. 定義方法:
    • イミディエイトワードは、IMMEDIATE を使用して定義されます。
    • これにより、そのワードが定義された直後に実行されるようにマークされます。
  3. 使用例:
    • 例えば、."(ダブルクォートピリオド)はイミディエイトワードです。これは、コンパイル時ではなく実行時に文字列を表示します。
    • : greeting ." Hello, world!" ; という定義では、. "Hello, world!" はコンパイル時に実行されず、greeting` を実行したときに実行されます。
  4. 動的な挙動:
    • イミディエイトワードは、通常のワードとは異なり、実行時の環境やスタックの状態に応じて動的に振る舞います。
    • これにより、実行時に動的なテキストや操作を生成するために使用することができます。

イミディエイトワードは、Forthの柔軟性と強力な動的な特性を提供します。コンパイル時に制御構造や動作を決定するだけでなく、実行時に動的な挙動を持つことができます。

Forthの文法
Forthのプログラムは、空白文字で区切られた「ワード」の並びで構成されます。ワードには、数値リテラル、制御構造、演算子、そして他のワードを呼び出す構文などがあります。Forthには高水準の構文がなく、基本的な構文要素は非常にシンプルです。

例えば、2 3 + と入力すると、2と3がスタックにプッシュされ、最後の+がスタック上の2つの数値を加算します。この結果である5がスタックに残ります。つまり、Forthではプログラムの実行とデータ操作が、ワードの解釈とスタック操作によって行われるのです。

Forthは、基本的に行指向の言語です。プログラムは複数行に分割できますが、改行の前後で文が継続する場合は、スペースを入れる必要があります。このため、適切なインデントを使ってプログラムの構造をわかりやすくすることが重要です。

Core Word SetとOptionalなWord Sets

[編集]

ForthのWord Setsは、言語の機能をカテゴリ別に整理したものであり、それぞれのセットは特定の目的や機能に関連する語彙を提供します。以下は一般的なForthのWord Setsの一覧です。

  1. Core Word Set:
    Forthの基本的な動作やスタック操作、制御構造、変数定義などの基本的な語彙を提供します。ほとんどのForthシステムでサポートされる基本セットです。
  2. Block Word Set:
    ブロック操作やファイルからの読み込み・書き込みなど、ブロック機能に関連する語彙を提供します。
  3. Double Number Word Set:
    倍精度整数の操作に関連する語彙を提供します。
  4. Exception Word Set:
    例外処理やエラー処理に関連する語彙を提供します。
  5. Facility Word Set:
    便利なユーティリティや汎用の機能に関連する語彙を提供します。
  6. File Word Set:
    ファイル操作に関連する語彙を提供します。
  7. Float Word Set:
    浮動小数点数操作に関連する語彙を提供します。
  8. Locals Word Set:
    ローカル変数やスタックフレーム管理に関連する語彙を提供します。
  9. Memory Word Set:
    メモリ操作やメモリアロケーションに関連する語彙を提供します。
  10. Tools Word Set:
    開発やデバッグに役立つツールに関連する語彙を提供します。
  11. Search Word Set:
    データの検索やソートに関連する語彙を提供します。
  12. String Word Set:
    文字列操作やフォーマットに関連する語彙を提供します。
  13. Extended Character Word Set:
    拡張文字やUnicode文字に関連する語彙を提供します。

これらのWord Setsは、特定の用途や環境に応じてForthの機能を拡張するために使用されます。各Word Setは、Forthの実装によってサポートされる範囲が異なることがあります。

環境問い合わせとObsolateな環境問い合わせ

[編集]

環境問い合わせ(Environmental queries)は、Forthプログラムの実行環境に関する情報を取得するための仕組みです。例えば、文字列の最大サイズやスタックの最大容量などの情報を取得できます。これに対して、Obsolescentな環境問い合わせ(Obsolescent Environmental Queries)は、Forthの特定の機能やWord Setが古くなりつつあることを示すためのものです。たとえば、Forth 94の特定のWord Setが存在するかどうかを確認することができます。しかし、このようなObsolescentな環境問い合わせは将来のForthの標準仕様から削除される可能性があり、新しいプログラムでの使用は推奨されません。

以下は、個々の環境問い合わせについての解説を表形式で示したものです。

文字列 定数? 解説
/COUNTED-STRING n yes 最大カウント文字列サイズ(文字数)
/HOLD n yes 数値の出力文字列バッファのサイズ(文字数)
/PAD n yes PADが指すスクラッチ領域のサイズ(文字数)
ADDRESS-UNIT-BITS n yes アドレスユニットのサイズ(ビット数)
FLOORED flag yes フロア除算がデフォルトかどうか(trueならばデフォルト)
MAX-CHAR u yes 実装定義文字セット内の最大文字値
MAX-D d yes 利用可能な最大符号付き倍精度整数値
MAX-N n yes 利用可能な最大符号付き整数値
MAX-U u yes 利用可能な最大符号なし整数値
MAX-UD ud yes 利用可能な最大符号なし倍精度整数値
RETURN-STACK-CELLS n yes リターンスタックの最大サイズ(セル数)
STACK-CELLS n yes データスタックの最大サイズ(セル数)
これらの環境問い合わせは、Forthの実装や実行環境に関する情報を取得するために使用されます。例えば、文字列の最大サイズやスタックの容量を知ることで、プログラムの設計や最適化に役立ちます。

以下は、Obsolescentな環境問い合わせ(Obsolescent Environmental Queries)についての表です。

文字列 定数? 解説
CORE flag no Forth 94の完全なコアワードセットが存在するかどうか(5.1.1で定義されたサブセットではなく)
CORE-EXT flag no Forth 94のコア拡張ワードセットが存在するかどうか
BLOCK flag no Forth 94ブロックワードセットの存在
BLOCK-EXT flag no Forth 94ブロック拡張ワードセットの存在
DOUBLE flag no Forth 94ダブル数ワードセットの存在
DOUBLE-EXT flag no Forth 94ダブル数拡張ワードセットの存在
EXCEPTION flag no Forth 94例外ワードセットの存在
EXCEPTION-EXT flag no Forth 94例外拡張ワードセットの存在
FACILITY flag no Forth 94ファシリティワードセットの存在
FACILITY-EXT flag no Forth 94ファシリティ拡張ワードセットの存在
FILE flag no Forth 94ファイルワードセットの存在
FILE-EXT flag no Forth 94ファイル拡張ワードセットの存在
FLOATING flag no Forth 94浮動小数点数ワードセットの存在
FLOATING-EXT flag no Forth 94浮動小数点数拡張ワードセットの存在
LOCALS flag no Forth 94ローカルワードセットの存在
LOCALS-EXT flag no Forth 94ローカル拡張ワードセットの存在
MEMORY-ALLOC flag no Forth 94メモリ割り当てワードセットの存在
MEMORY-ALLOC-EXT flag no Forth 94メモリ割り当て拡張ワードセットの存在
TOOLS flag no Forth 94プログラミングツールワードセットの存在
TOOLS-EXT flag no Forth 94プログラミングツール拡張ワードセットの存在
SEARCH-ORDER flag no Forth 94検索順ワードセットの存在
SEARCH-ORDER-EXT flag no Forth 94検索順拡張ワードセットの存在
STRING flag no Forth 94文字列ワードセットの存在
STRING-EXT flag no Forth 94文字列拡張ワードセットの存在
これらの環境問い合わせは、Forth 94の特定のWord Setが現在の実装に存在するかどうかを確認するために使用されます。ただし、これらの環境問い合わせは古くなりつつあり、将来のForthの標準からは削除される可能性があります。新しいプログラムの開発ではこれらの環境問い合わせの使用は推奨されません。

データ型

[編集]

Forthには明示的なデータ型宣言がないため、データ型は主に暗黙的に扱われます。Forthでは、データ型はデータの解釈や使用方法に依存します。以下では、Forthでよく使用されるデータ型について説明します。

文字(Character)

[編集]

Forthの文字型は、通常は1バイトの範囲で扱われ、数値としても解釈されます。Forthの形式的な仕様には符号付きと符号なしの区別はありませんが、実際の使用では文字を短い正の整数として扱う傾向があります。文字型の格納単位は通常、0から255までの符号なし数値を保持できるように設計されています。文字型は整数型の一部であり、8ビット以上のビット幅であれば、C!がセルの下位ビットを書き換えることができます。また、文字型は32から126の範囲でASCII文字として扱われますが、その範囲外では実装に依存します。国際文字やアクセント記号を含む文字は、128から255の範囲で標準化される可能性があります。

Forthの標準プログラムは、KEYを介して32から126の範囲の任意の文字を受け取ることを前提としています。また、制御文字は定義名に使用できませんが、これはForthのシステムに依存します。文字はC!C@などのワードを使ってメモリやスタックに転送され、その他の演算子も文字型に適用可能です。ただし、実装によっては文字の高位ビットに表示情報がキャッシュされている場合があるため、適切な処理が必要です。

単一セル型

[編集]

単一セルスタックエントリ(single-cell stack entry)は、Forthの基本データ型であり、他のすべてのデータ型は1つ以上の単一セルスタックエントリによって実際に表されます。

格納と取り出し
単一セルデータは、!によってスタックからメモリに転送され、@によってメモリからスタックに転送されます。両方向の転送ではすべてのビットが転送され、どの種類の型チェックも行われません。また、標準システムは、!または@が使用するメモリアドレスが正しく整列されているか、転送されるデータに適合するサイズであるかを確認しません。
スタック上での操作
単一セルデータを、スタックに移動させたり取り出したりするための重要なワードの選択は以下の通りです:
! 
@ 
>R 
?DUP 
DROP 
DUP 
OVER 
PICK 
R> 
R@ 
ROLL 
ROT 
SWAP
比較演算子
以下の比較演算子は、1つ以上の単一セルに対して普遍的に有効です:
=   ( x1 x2 -- flag ) \ flag は、x1がビット毎にx2とすべて同じ場合にのみtrue
<>  ( x1 x2 -- flag ) \ flag は、x1がビット毎にx2と1ビットでも異なればtrue
0=  ( x -- flag )     \ flag は、xのすべてのビットが0ならばtrue
0<>  ( x -- flag )    \ flag は、xのいずれかのビットが1ならばtrue

フラグ

[編集]

偽のフラグは、すべてのビットがクリアされた単一セルデータであり、真のフラグはすべてのビットがセットされた単一セルデータです。Forthのフラグをテストするワードは、任意のヌル以外のビットパターンを真として受け入れますが、正規化されたフラグの概念も存在します。結果が真または偽以外のビットマスクを生成する操作が行われる場合、推奨される方法は、その結果をForthワードの0<>を使用して正規化されたフラグに変換することです。

単一セルアイテムを移動、取得、保存するためのワードに加えて、データスタック上に存在する1つまたは複数のフラグデータに対する操作として、次のワードが有効です:

AND 
OR 
XOR 
INVERT

整数

[編集]

単一セルデータは、標準プログラムによって符号付き整数として扱うことができます。これらのデータの移動や保存は、単一セルデータと同様に行われます。上記で指定された単一セルデータに普遍的に適用可能な演算子に加えて、次の数学的および比較演算子が単一セルの符号付き整数に対して有効です:

* 
*/ 
*/MOD 
/MOD 
MOD 
+ 
+! 
- 
/ 
1+ 
1- 
ABS 
MAX 
MIN 
NEGATE 
0< 
0> 
< 
>

同じビット数であれば、符号付き整数で表現可能な絶対値の2倍の数を表すことができます。

単一セルデータは、標準プログラムによって符号なし整数として扱うこともできます。これらのデータの移動や保存は、単一セルデータと同様に行われます。さらに、次の数学的および比較演算子が単一セルの符号なし整数に対して有効です:

UM* 
UM/MOD 
+ 
+! 
- 
1+ 
1- 
* 
U< 
U>

アドレス

[編集]

アドレスは、単一セルの符号なし数値としてユニークに表現され、スタックに移動させたり、スタックから取り出したりする際にはそれと同様に扱うことができます。逆に、各符号なし数値は一意のアドレスを表し(これは必ずしもアクセス可能なメモリのアドレスではありません)、アドレス算術と符号なし数値の対応する操作との間には一対一の関係が強制されます。さまざまなアドレス演算子が特に提供されています:

CHAR+ 
CHARS 
CELL+ 
CELLS

また、浮動小数点数のワードセットが存在する場合は、次のような操作も可能です:

FLOAT+ 
FLOATS 
SFLOAT+ 
SFLOATS 
DFLOAT+ 
DFLOATS

カウントされた文字列

[編集]

Forth 94では、スタック上での文字列の表現として「c-addr u」表現の一貫した使用に向かいました。代替の「カウントされた文字列のアドレス」スタック表現の使用は推奨されません。伝統的なForthワードWORDFINDは、歴史的な理由から「カウントされた文字列のアドレス」表現を使用し続けます。既存のプログラムの移植支援として追加された新しいワードC"も、カウントされた文字列の表現を使用します。

カウントされた文字列は、メモリに文字列を保存する方法として便利です。この使用法は非推奨ではありませんが、スタック上にそのような文字列への参照が表示される場合は、「c-addr u」表現を使用することが好ましいです。

実行トークン

[編集]

実行トークン(Execution tokens; xt)と定義との関連は静的です。一度定義されると、検索順序の変更などで変更されることはありません。ただし、一意ではない場合もあります。たとえば、次のフレーズ

' 1+

および

' CHAR+

は同じ値を返す可能性があります。

エラー結果

[編集]

iorという用語は、元々入出力操作の結果を記述するために定義されました。これは他の操作も含むように拡張されました。

セルペア型

[編集]
記憶と取り出し
セルペアを取得および格納するための2つの演算子が提供されています:
2@ 
2!
スタック上での操作
さらに、これらの演算子は、セルペアをスタックから移動し、スタックに移動させるために使用できます:
2>R 
2DROP 
2DUP 
2OVER 
2R> 
2SWAP 
2ROT
比較
次の比較操作は、セルペアに対して普遍的に有効です:
D= 
D0=

ダブルセル整数

[編集]

ダブルセル整数を符号付きとして扱う場合、次の比較および数学的演算が有効です:

D+ 
D- 
D< 
D0< 
DABS 
DMAX 
DMIN 
DNEGATE 
M*/ 
M+

ダブルセル整数を符号なしとして扱う場合、次の比較および数学的演算が有効です:

D+ 
D- 
UM/MOD 
DU<

データ型の制約と振る舞い

[編集]

Forthでは、データ型に関する制約やチェックはコンパイラやランタイムによっては行われません。プログラマはデータを正しく扱う責任があります。特にプログラムの移植性を考慮する場合は、適切なデータ型の使用が重要です。

データ型はForthの柔軟性とパフォーマンスの両方をサポートするため、プログラマに柔軟性と責任を与えます。それぞれのアプリケーションや環境に応じて、適切なデータ型を選択することが重要です。

64ビットプロセッサとForth
64ビットプロセッサをベースとしたForthプログラミング環境は、その特性や利点に注目されることがあります。以下に、64ビットプロセッサとForthの組み合わせに関する考察を示します。
メモリアドレスの広がり
64ビットアーキテクチャでは、アドレス空間が広くなります。これにより、大規模なメモリの管理やアクセスが可能になります。Forthはこの広いアドレス空間を活用し、柔軟で効率的なメモリ管理を提供します。
データサイズと整数型
64ビットプロセッサでは、アドレスの幅が広がります。標準では単一のセルにアドレスが保持できることを要求しているので、セルの幅も広くなります。1セルが8バイトとなる64ビットForth環境では、倍精度浮動小数点数と同じ幅を持つことになります。
計算の高速化
64ビットアーキテクチャは、大規模なデータ処理や計算を効率的に行うことができます。Forthはシンプルで直感的なスタックベースの計算モデルを持つため、64ビットプロセッサの並列処理能力や高速性を活かして効果的なプログラミングが可能です。
ハードウェアとの統合
64ビットプロセッサを基盤としたForthは、ハードウェアとの密接な統合が期待されます。低レベルの制御やハードウェア資源へのアクセスが柔軟に行えるため、組み込みシステムや制御系アプリケーションに適しています。
64ビットプロセッサとForthの組み合わせは、高度な計算能力とプログラムの柔軟性を活かした開発環境を提供します。特に組み込みシステムや計算機制御の分野で、効果的なプログラミングとシステム開発が可能です。

倍精度整数

[編集]

ForthのDouble Number word setとは、32ビット以上のシステムで64ビット長の倍精度整数を扱うためのワードセットです。

主なワードは以下のとおりです:

  • 2CONSTANT - 64ビット定数を定義
  • 2LITERAL - 64ビットリテラルデータをスタックに押す
  • 2VARIABLE - 64ビット変数を定義
  • D+ - 64ビット加算
  • D- - 64ビット減算
  • D* - 64ビット乗算
  • D/ - 64ビット除算
  • D. - 64ビット数値を出力
  • D>S - 64ビット数値を32ビットに変換
  • DU< - 64ビット数値の無符号比較

このワードセットを使うことで、32ビットを超える大きな整数値を扱えるようになります。例えば:

2VARIABLE X  123456789012345 X 2!  \ Xに123456789012345を代入
X 2@  D.     \ 123456789012345を出力

Double Number wordsetは、16ビットForth環境では一般的に使われていましたが、32ビット以上の環境が普及するにつれ、その必要性は低下してきました。そのため、Forth-2012標準ではこのワードセットはオプションとされています。

ただし、64ビット整数が必要な場合や、32ビットの範囲を超える大きな整数値を扱う必要がある場合には、Double Number wordsetが役立ちます。Forth処理系が対応しているかどうかは実装による違いがあります。

浮動小数点数

[編集]

Forthの浮動小数点数についての情報は、Forth-2012仕様におけるオプションの浮動小数点数ワードセットに関連しています。以下にその内容の要点をまとめます。

浮動小数点数の基本
  • 浮動小数点数は、DECIMALBASEで入力する必要があります。任意の基数での浮動小数点数の入力は許可されていません。
  • 標準システムで解釈されるすべての浮動小数点数には、指数インジケーター "E" を含める必要があります。
浮動小数点数の例
1e 3.14e 1.2e3 1e9
浮動小数点数のフォーマットと範囲
  • 浮動小数点数の有効桁数や指数のフォーマット、範囲は、Forth-2012では実装に依存します。IEEE浮動小数点数フォーマットが一般的に使用されています。
浮動小数点数のワードセット
  • Floating-Point Extensionsワードセットには、DF@SF@DF!SF!などのワードが含まれています。これらは、倍精度および単精度のIEEE浮動小数点フォーマットの数値をメモリに格納するためのものです。
浮動小数点数の入力
  • 浮動小数点数をASCII形式で入力するためには、>FLOATワードが使用されます。これは、標準Forthシステムや他の広く使用されるプログラミング環境からデータを受け入れるために広範な構文を許容します。
実装の注意点
  • カスタムの浮動小数点数データ構造を定義する際には、アドレスの整列(alignment)に注意する必要があります。CREATEが適切な浮動小数点数の整列を行わない場合は、コンパイル時および実行時に適切な整列を指定する必要があります。

ローカル変数

[編集]

ForthのOPTIONAL Locals word set(ローカルワードセット)は、Forthのプログラミングでローカル変数を定義するための機能を提供します。以下は、このワードセットに関する情報の要約です。

ローカル変数の定義方法
  • (LOCAL) ワードや LOCALS| ワードを使用して、ローカル変数を定義します。
    : my-swap ( x y -- y x )
     locals| x y |
     x y
    ;
    
    1 2 my-swap . . . cr  \ 1 2
    
  • { ... } または {: ... :} の表記を使用して、ローカル変数を囲みます。: ... : の形式は、他のシステムとの名前の衝突を避けるための妥協形式です。
ローカル変数の種類
  • ローカル変数は、初期化されたものと初期化されていないものに分けられます。初期化されたローカル変数はデータスタックから実行時に初期化されます。
定義の終了
  • ローカル変数の定義は -- もしくは :} で終了します。これにより、ローカル変数の定義中に完全なスタックコメントを記述することが可能です。
文法と慣習
  • ローカル変数の引数と値の区切りには | を使用しますが、他のシステムでは { ... } の表記が競合する場合があります。
  • 実装によっては、別の区切り文字(例: \, ¦)を使用する場合がありますが、移植性を確保するためには | の使用が推奨されます。
予約事項
  • ローカル変数の名前には、特定の文字(:[^)で終わる名前は予約されています。

Forthのローカル変数機能は、実装によって異なる場合がありますが、一般的な慣習として、(LOCAL){: ... :} の表記が広く使用されています。移植可能なプログラムを書く場合は、このような慣習に準拠することが重要です。

ファイル操作

[編集]

Forthのファイル操作に関連するオプションのワードセットである「File-Access」についての情報を要約します。

ファイル操作の機能

[編集]
  1. CREATE-FILE (CREATE-FILE)
    • ファイルを作成するためのワードです。
    • 例: S" TEST.FTH" R/W CREATE-FILE ABORT" CREATE-FILE FAILED" ;
  2. OPEN-FILE (OPEN-FILE)
    • ファイルをオープンするためのワードです。
    • 例: S" TEST.FTH" R/W OPEN-FILE ABORT" OPEN-FILE FAILED" ... ;
  3. READ-FILE (READ-FILE)
    • ファイルからデータを読み込むためのワードです。
    • 典型的なシーケンシャルなファイル処理アルゴリズム:
    BEGIN
        ... READ-FILE THROW
    ?DUP WHILE
        ...
    REPEAT
    
  4. READ-LINE (READ-LINE)
    • ファイルから行単位でデータを読み込むためのワードです。
    • 典型的な行指向のシーケンシャルなファイル処理アルゴリズム:
    BEGIN
        ... READ-LINE THROW
    WHILE
        ...
    REPEAT DROP
    
  5. INCLUDE (INCLUDE)
    • 指定されたファイルを読み込んで実行するためのワードです。
    • 例: INCLUDE filename
  6. REQUIRE (REQUIRE)
    • 指定されたファイルを読み込んで実行するためのワードです。
    • 例: REQUIRE filename
  7. REQUIRED (REQUIRED)
    • 指定されたファイルを読み込んで実行するためのワードです。
    • 例: S" filename" REQUIRED

ファイル操作の注意事項

[編集]
  • ファイルを再利用する際は、前のファイルのブロックバッファをフラッシュして、誤った関連付けを避ける必要があります。
  • S"S\" で使用される一時バッファは、2つの連続する文字列を格納できるようにする必要があります。バッファが不足している場合は、最も古いバッファを上書きすることができます。

例外処理

[編集]

Forthの例外処理に関する情報を要約します。 Exception word set はオプショナルなので、実装していない処理系もありえます。

CATCH と THROW

[編集]
  • CATCH (CATCH): 例外ハンドリングの開始をマークします。CATCH ブロック内のコードが例外を THROW すると、CATCH は例外コードをキャッチして対応する処理を行います。
  • THROW (THROW): 例外を発生させ、対応する CATCH ブロックに制御を戻します。例外コードが与えられると、THROW は対応する CATCH のスタック状態を復元し、適切な処理を行います。

例外処理の重要性と利用法

[編集]
  • 例外処理の重要性: ABORT などの状況でプログラムの制御を維持するために例外ハンドラを使用します。
  • 例外処理の利用法: 例外処理は、ファイルのテキスト解釈中に発生した例外をキャッチしてファイルをクローズするなど、プログラムの安全な実行を保証します。

具体例

[編集]
: could-fail ( -- char )
   KEY DUP [CHAR] Q = IF 1 THROW THEN ;

: do-it ( a b -- c) 2DROP could-fail ;

: try-it ( --)
   1 2 ['] do-it CATCH IF
   ( x1 x2 ) 2DROP ." There was an exception" CR
   ELSE ." The character was " EMIT CR
   THEN ;

: retry-it ( -- )
   BEGIN 1 2 ['] do-it CATCH WHILE
   ( x1 x2) 2DROP ." Exception, keep trying" CR
   REPEAT ( char )
   ." The character was " EMIT CR ;
  • could-fail: キーボードからキーを読み取り、特定の条件で例外を発生させます。
  • do-it: 例外を発生させる可能性のある処理を行います。
  • try-it: do-it を実行し、例外が発生した場合は例外メッセージを表示します。
  • retry-it: do-it を実行し、例外が発生した場合はリトライし続けます。

実行トークン

[編集]

Forthにおける実行トークン(Execution Token; xt)は、ワード(Word)や定義体(Definition)の実行を指示するためのデータ構造です。実行トークンは、コードワードを指すための一意の識別子として機能します。以下は、Forthの実行トークンについての詳細です。

実行トークンの定義
実行トークンは、通常、ワードや定義体の実行を示すために使用される整数またはポインタです。これは通常、コンパイラやインタプリタ内部で使用され、特定のワードや定義体を表す一意の識別子として機能します。
実行トークンの使用
Forthのコードは、実行トークンを使用してワードや定義体を実行します。実行トークンは通常、スタックや辞書(Dictionary)に格納され、必要なときに取り出されて使用されます。
' (Tick) 記号
一般的なForthのプログラミングスタイルでは、実行トークンを取得するためにシングルクォート(')記号が使用されます。例えば、' DUPDUP ワードの実行トークンを取得します。
実行トークンと定義体の関連付け
実行トークンは、定義体やコードワードと関連付けられます。通常、辞書内のワードエントリは、そのワードの実行トークンを参照します。これにより、プログラムは実行トークンを使用して、辞書内のワードを特定して実行することができます。
ランタイムスタックとの関係
Forthのランタイムスタックは、実行トークンを使用してコードを実行します。コンパイルされたForthコードは、ランタイムスタック上で実行トークンを取り出して、対応するワードや定義体を実行します。
実行トークンの特徴
実行トークンは、Forthの柔軟性と効率性を向上させるために使用されます。コンパイラやインタプリタは、実行トークンを介してワードを効率的に実行し、辞書の管理を行います。
実行トークンの安全性
Forthでは、実行トークンは通常、ユーザーからは直接操作されず、コンパイラやランタイムシステム内部で使用されます。これにより、実行トークンを誤って操作することがなく、安全なプログラミング環境が提供されます。

Forthの実行トークンは、言語の内部実装の詳細を隠蔽し、プログラマに柔軟性と効率性を提供する重要な概念です。

制御フロースタック

[編集]

制御フロースタックは、Forthの制御構造の実装に使用されるメカニズムです。具体的には以下のような役割を果たします:

  1. 分岐命令の始点(origin)を記録する
    • IFAHEAD などの前方ジャンプ命令の始点を記録
  2. 分岐命令の終点(destination)を記録する
    • BEGINAGAIN などの後方ジャンプ命令の終点を記録
  3. 分岐命令の始点と終点を解決する
    • THENIFAHEAD の始点を解決
    • AGAINBEGIN の終点を解決

制御フロースタックは、Forthの制御構造を実現するための中間表現として機能しています。プログラマはこの制御フロースタックの管理を意識することなく、高レベルな制御構造を記述できます。

実装方法は任意ですが、たとえばデータスタック、連結リスト、特殊な配列など、必要な操作ができる形で実現されます。重要なのは、制御構造の始点と終点が適切に記録・解決されることです。

この制御フロースタックを基に、IF-THENBEGIN-UNTILBEGIN-WHILE-REPEAT などの基本的な制御構造が構築されます。さらに、CASEWHILEREPEATELSE などの拡張構造も、制御フロースタックの操作によって定義できます。

つまり、Forthの制御フロースタックは、高度な制御構造を柔軟に構築するための基盤となる重要な概念なのです。

コンパイル時ワード

[編集]

Forthには、コンパイル時に処理される特殊なワードがあります。これらをコンパイル時ワードと呼びます。

主なコンパイル時ワードには以下のようなものがあります:

  1. コンパイル時の定義操作
    • : (コロン) - 新しい定義を開始する(:自身は通常の逐次ワード)
    • ; (セミコロン) - 定義を終了しコンパイルモードを抜ける
    • CONSTANTVARIABLEDEFER - 定数、変数、遅延定義を作成する
  2. コンパイル時の制御
    • IFELSETHENBEGINUNTIL など - 条件分岐やループなどの制御構造を定義する
    • LITERAL - スタックトップの値をコンパイル時に埋め込む
  3. その他のコンパイル時操作
    • IMMEDIATE - 定義をすぐに実行されるように指定する
    • [COMPILE] - 次のワードをコンパイル時に実行する
    • POSTPONE - 次のワードの実行トークンをコンパイルする

これらのコンパイル時ワードは、Forthのプログラムの構造を定義したり、実行時の振る舞いを制御したりするために使われます。

通常のワードとは異なり、これらのコンパイル時ワードはコンパイル時に処理され、実行時には特殊な動作をするようになっています。これにより、Forthのプログラミングモデルを柔軟に拡張することができます。

辞書

[編集]

Forthの辞書に関する情報を要約します。

辞書と名前空間
  • 名前空間: Forthの辞書は名前空間を提供します。標準プログラムは非標準の定義で標準ワードを再定義することができますが、その結果は標準システムではなくなります。
  • 定義名: 標準プログラムが使用する外部要素が標準システムで提供されていない場合、実行環境に依存する可能性があります。
コード空間とデータ空間
  • データ空間: Forthシステムのデータ空間は不連続な領域に分かれています。複数の領域の配置については標準プログラムは仮定できません。
  • アドレスの整列: アクセス命令によって使用されるアドレスにはアライメント制約があります。例えば、16ビットのデータは偶数アドレスにのみ保存される必要があるなどの制約があります。
他の情報
  • 一貫したアドレス: データ領域の一貫性は、領域内でのアドレス演算を有効にします。
Contiguous regions(連続領域)

Forthではデータ空間が連続しているという考え方があります。つまり、データ領域は一つの連続したブロックとして扱われますが、実際にはそのデータ空間は複数の部分から構成されていることがあります。

一般的なForthシステムでは、データ空間は複数の領域に分かれており、これらの領域は連続性を持ちますが、その間に他の種類の領域が存在する場合もあります。このような設計は、Forthシステムのメモリ管理において重要な役割を果たします。

例えば、以下のようなForthのコードで連続領域を確保することができます:

CREATE MY-ARRAY 100 CELLS ALLOT

この例では、100つのセル(通常はセルは1つのワードに対応します)を連続して確保しています。これにより、MY-ARRAYは連続した100個のセルを格納する領域を持つことになります。このようにして連続領域を確保することで、アドレス算術を利用してデータにアクセスすることができます。

Forthの連続領域の管理は、CREATEALLOTDOES>などの単語を使用して行われます。これらの単語を組み合わせてデータ構造を定義し、連続領域を動的に割り当てることができます。

これらの情報は、標準プログラムのポータビリティと安全性を確保するために重要です。具体的な実装や制約については、詳細なFor仕様書を参照してください。

データ構造とメモリ管理

[編集]

Forthは組み込みシステムなどのリソース制約の厳しい用途を想定して設計されているため、メモリの効率的な利用が重視されています。この章では、Forthにおけるデータ構造とメモリ管理の方法を説明します。

コントロールフロースタック

[編集]

コントロールフロースタックとは、Forthのプログラムの制御フローを管理するために使用される仮想的なスタックです。

その主な役割は以下の通りです:

  1. 制御フロー命令の対応関係の管理
    • 制御フロー命令(IF, THEN, ELSE, BEGIN, WHILE, REPEAT など)の対応関係を管理します。
    • 開始と終了の命令が適切に対応していることを確認します。
  2. 制御フロー中のデータスタック操作の制約
    • 制御フロー中のデータスタックの使用方法に制約を課します。
    • 制御フロー命令の入れ子関係に応じて、適切なデータスタックの状態が維持されるようにします。

コントロールフロースタックは物理的に実装されていなくても良いと仕様で定められています。 代わりに、システムはコンパイル時や実行時にこのスタックの挙動を模倣する方法を採ることができます。

つまり、コントロールフロースタックはForthの制御構造の妥当性を保証する上で重要な概念であり、プログラムの正しい実行を支えているのです。

通常のプログラミングでコントロールフロースタックを強く意識することはありませんが、制御構造を自らワードとして定義しようとする場合、コントロールフロースタックの振る舞いに注意する必要があります。

リターンスタック

[編集]

Forthには、データスタックとは別に、リターンスタックと呼ばれる重要なスタックが存在します。リターンスタックの主な役割は以下の通りです:

  1. 関数呼び出しとリターン
    • 関数を呼び出すとき、その関数の呼び出し元の命令アドレスがリターンスタックにプッシュされます。
    • 関数から抜け出すときは、リターンスタックからアドレスがポップされ、その場所に制御が戻されます。
    • これによって、関数呼び出しの際の制御の移動を管理することができます。
  2. 一時的データ保管
    • リターンスタックは、データを一時的に保管する場所としても使えます。
    • データスタックからデータを>Rでリターンスタックにプッシュし、後でR>で取り出すことができます。
    • これにより、データスタックの状態を一時的に変更せずに、値を保持しておくことができます。

リターンスタックの使用には注意点があります:

  • ワード内やループ内では、>RR>の回数が一致している必要があります。
  • リターンスタックの値は、同じブロック内でのみ参照・取り出しできます。
  • ループの脱出時やDO-ループ内からは、リターンスタックの値を取り出せません。

つまり、リターンスタックは制御フローの管理と一時的なデータ保管に使われますが、その使い方には一定の制限があるため、注意深く扱う必要があります。

プログラムの複雑さが増すと、リターンスタックの状態を適切に管理することが難しくなるため、できるだけ単純な使い方に留めるのが賢明です。

>R ( x -- ) ( R: -- x )
データスタックから値をポップしリターンスタックにプッシュする
R> ( R: -- x ) ( x -- )
リターンスタックから値をポップしデータスタックにプッシュする
R@ ( -- x ) ( R: x -- x )
リターンスタックのスタックトップをデータスタックにコピーする
2>R ( x1 x2 -- ) ( R: -- x1 x2 )
2R> ( -- x1 x2 ) ( R: x1 x2 -- )
2R@ ( -- x1 x2 ) ( R: x1 x2 -- x1 x2 )

スタックエフェクトに R: という新しい記号が出てきましたが、これはリターンスタックへの操作をしまします。このためリターンスタックはRスタックとも呼ばれています。

変数とアドレス指定

[編集]

Forthには、グローバル変数やローカル変数の概念がありません。代わりに、値を格納するためのメモリ領域を確保し、そのアドレスを使ってアクセスします。VARIABLEワードを使うと、新しい変数領域が確保され、その開始アドレスがスタックに置かれます。

VARIABLE COUNTER  \ カウンター変数を定義
COUNTER @ .  \ 変数の値を表示(初期値は0)
1 COUNTER +! \ 変数にインクリメント

変数の値を取り出すには@(フェッチ)、格納するには!(ストア)を使います。+!のように算術演算と!を組み合わせたワードもあります。

Forthではアドレスを直接スタックに置いて操作することもできます。HEREはディクショナリの次の空きアドレスを取得し、ALLOTはディクショナリ領域を確保します。

データスペース(Data Space)

[編集]

Forthのデータスペースとは、メモリ内の連続した領域を指し、データやワードの定義などが格納される場所です。Forthでは、データスペースは主に ヒープ(heap)ディクショナリ(dictionary) の2つの領域に分かれています。

  • ヒープ: データスタックやワードの実行時に使用される一時的な領域です。ここでは、動的に割り当てられたデータや一時的な計算に使用されるデータが保持されます。
  • ディクショナリ: ワードの定義やその実装が格納される領域です。Forthのディクショナリはワードの名前とそれに対応する実行可能なコードへのポインタを保持します。

HERE ワード

[編集]

HERE はForthで使用される特定のワードであり、現在のデータスペース内の「ここ(here)」の位置を示します。具体的には、ディクショナリ内の次の空き領域の先頭アドレスを指します。

HERE を使用すると、新しいワードやデータをデータスペースに格納する際に位置を特定することができます。主にワードの定義やデータの動的な確保に使用されます。

例えば、新しい変数や配列を定義する場合、HERE の値を参照して適切なメモリ領域を割り当て、その後 HERE を更新します。

具体的な使用例を示します:

HERE .   \ 現在のHEREの値を表示(ディクショナリ内の次の空き領域のアドレス)
10 ALLOT  \ 10バイトの領域を確保
HERE .   \ 確保後のHEREの値を表示

この例では、まず HERE の値が表示され、次に 10 ALLOT によって10バイトの領域が確保されます。その後、再度 HERE の値が表示され、確保された領域の終端アドレスが示されます。

HERE を適切に使用することで、動的なメモリ管理やデータスペースの操作が行えます。

HEREを利用したデータスペースの活用例

[編集]
sieve.fr
: prime? ( n -- ? ) here + c@ 0= ;
: composite! ( n -- ) here + 1 swap c! ;

: sieve ( n -- )
  here over erase
  2
  begin
    2dup dup * >
  while
    dup prime? if
      2dup dup * do
        i composite!
      dup +loop
    then
    1+
  repeat
  drop
  ." Primes: " 2 do i prime? if i . then
  loop
  ;
  
100 sieve
/ Primes: 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97

このForthのコードは、エラトステネスの篩(Sieve of Eratosthenes)アルゴリズムを実装しています。このアルゴリズムは、与えられた数 n 以下のすべての素数を見つけるために使用されます。以下では、与えられたコードの各部分を詳しく説明します。

prime? ワード
: prime? ( n -- ? ) here + c@ 0= ;
( n -- ? )
スタックから整数 n を取り出し、その数が素数であるかどうかの真偽値 ? をスタックに積む。
  • here + c@: here は現在のデータスペースの末尾を示し、それに + を使ってワードのn番目のアドレスを取得する。そして c@ はそのアドレスの内容(1バイトの文字)を取得する。
  • 0=: 取得したバイトが0かどうかをチェックし、0ならば真(素数)を返す。
composite! ワード
: composite! ( n -- ) here + 1 swap c! ;
( n -- )
スタックから整数 n を取り出す。
  • here + 1 swap c!: here からnのアドレスにを1を1バイトで書き込む。
sieve ワード
: sieve ( n -- )
  here over erase
  2
  begin
    2dup dup * >
  while
    dup prime? if
      2dup dup * do
        i composite!
      dup +loop
    then
    1+
  repeat
  drop
  ." Primes: " 2 do i prime? if i . then
  loop
  ;
( n -- )
スタックから整数 n を取り出す。
  • here over erase: 現在のデータスペース(here から先)を n バイト分消去する。
  • 2 begin ... repeat: メインのループ。2から始めて、以下の条件に基づいて処理を繰り返す。
    • 2dup dup * >: 現在の値を2つコピーし、2つを掛けた値が n を超えるまで続ける。
    • dup prime? if ... then: 現在の値が素数かどうかをチェックし、素数ならば以下を実行。
      • 2dup dup * do ... dup +loop: 素数の場合、その数の倍数を篩い落とす。内側のループで、その数の倍数を全て composite! でマークする。
    • 1+: 現在の数値を1増やす。
  • drop: 最後の数値を削除する。
  • ." Primes: " 2 do i prime? if i . then loop: 素数を表示するループ。2から始まり、n まで続ける。各数値が素数ならば表示する。
実行
100 sieve
100以下の素数を見つけるために sieve ワードが実行されます。

CREATEを使った変数と配列の定義

[編集]

Forthの CREATE は、新しい変数や配列をディクショナリに定義するためのワードです。CREATE を使うことで、任意の名前で参照可能なデータ領域を確保し、その領域に対する操作を定義することができます。

以下に、CREATE ワードの基本的な構文と使用方法を示します。

構文

[編集]
CREATE name
  • name: 定義する変数や配列の名前を指定します。

使用方法

[編集]
  1. 変数の定義
    CREATE を使って変数を定義する場合、単に変数の初期値を指定します。
    CREATE my-var
    42 ,
    
    上記の例では、my-var という名前の変数が定義され、初期値として 42 が設定されています。, はスタックのトップの値を my-var の定義領域に格納するワードです。
  2. 配列の定義
    CREATE を使って配列を定義する場合、配列の要素数や初期値を指定します。
    CREATE my-array  5 ALLOT
    
    上記の例では、my-array という名前の配列が定義され、5つの要素分の領域が確保されます。各要素には初期値が与えられていないため、初期化が必要な場合は別途行います。

使用例

[編集]

このForthのコードは、CREATE を使って変数と配列を定義し、それらに値を格納・表示する例です。

変数の定義と使用
CREATE x   \ 変数 x を定義
100 x !    \ x に 100 を代入
x @ . CR   \ x の値を表示
  • CREATE x: x という名前の変数を定義します。CREATE はディクショナリに新しいワードを登録しますが、データスペース自体は確保しません。
  • 100 x !: x のデータフィールドに 100 を格納します。
  • x @ . CR: x のデータフィールドから値を読み取って表示します。CR は改行を出力します。
配列の定義と使用
CREATE MY-ARY 100 ALLOT  \ 100セルの配列を作成
42 MY-ARY 10 + !         \ 11番目の要素に42を代入
MY-ARY 10 + @ .          \ 11番目の要素の値を表示
  • CREATE MY-ARY 100 ALLOT: MY-ARY という名前の配列を定義し、100セル分のデータスペースを確保します。
  • 42 MY-ARY 10 + !: MY-ARY の11番目の要素(0から始まるインデックスなので、10を足します)に 42 を代入します。
  • MY-ARY 10 + @ .: MY-ARY の11番目の要素の値を読み取って表示します。

この例では、CREATE を使って変数 x と配列 MY-ARY を定義し、それぞれに値を設定しています。x は単一の値を格納する変数であり、MY-ARY は複数の要素を持つ配列です。! を使ってデータを代入し、@ を使ってデータを取得しています。

Forthの特性として、変数や配列の定義はディクショナリに登録されるため、名前に対して対応するデータ領域へのアクセスが可能となります。これにより、動的なデータ構造を効率的に管理することができます。

アライメント

[編集]

Forthの ALIGN ワードは、データ領域のアラインメントを行うためのワードです。アラインメントとは、データがメモリ上で配置される位置を調整することであり、特定のアドレス境界にデータを配置することを指します。

アラインメントの必要性は、プロセッサアーキテクチャに依存しますが、多くのアーキテクチャでは特定のデータ型(例:4バイト整数など)は特定の境界に配置される必要があります。アラインメントが正しく行われないと、メモリアクセスの効率が低下したり、アドレスエラーなどの例外が発生し処理を中断する可能性があります。

構文
ALIGN
動作
ALIGN ワードは、データスペースポインタ (HERE) をアラインメント条件に従って調整します。具体的には、通常は2バイトまたは4バイトの境界に HERE を調整します。これにより、次に確保されるデータ領域が適切な境界に配置されます。
使用例
HERE ALIGN
上記の例では、HERE の値をアラインメントされた位置に調整します。これにより、次に確保されるデータ領域が適切な境界に配置されます。
CREATE my-var  ALIGN 2 ALLOT
この例では、my-var という名前の変数を定義していますが、そのデータ領域が2バイト境界にアラインメントされて確保されます。これにより、my-var のデータ領域は適切な境界に配置され、メモリアクセスの効率が向上します。

ALIGN ワードは、特定のアーキテクチャで必要なメモリアラインメントを確保するために重要な役割を果たします。メモリアクセスの効率を最大化するために、適切なアラインメントが常に考慮されるべきです。

入出力と組み込みワード

[編集]

Forthには標準の入出力ワードがあり、コンソールやファイルからの入出力を行えます。また、様々な組み込みワードが用意されており、これらを活用することでForthの機能を拡張できます。

標準入出力

[編集]

Forthには、キーボードからの文字入力や、コンソールへの文字出力を行うためのワードが用意されています。

KEY    \ キーボードから1文字読み込む
EMIT   \ スタック上の値(文字コード)を出力する 
."    \ 文字列を直接出力する

KEYはキーボード入力を待ち、押された文字のASCIIコードをスタックに置きます。EMITは逆に、スタック上の値を文字コードとして解釈し、コンソールに出力します。." Hello"のように、." の後に文字列を続けると、その文字列がコンソール出力されます。

数値入出力には、?U.U.Rなどのワードが使えます。?は数値を入力受け付け、U.は符号なし数値を、U.Rは数値を指定した基数(2進数、16進数など)で出力します。

ファイルアクセス

[編集]

ファイル入出力には、様々なワードが用意されています。OPEN-FILEでファイルを開き、READ-FILEWRITE-FILEでデータの読み書きができます。

S" data.txt" R/O OPEN-FILE THROW VALUE HANDLE  \ ファイルを読み取りモードで開く
HANDLE READ-FILE ABORT" READ ERROR"  \ ファイルを読み込む
HANDLE CLOSE-FILE THROW  \ ファイルを閉じる

ファイル名は、S" で文字列をスタックに置いた後、R/O(読み取り専用)やW/O(書き込み専用)といったモードを指定してファイルを開きます。THROWはエラーがあれば例外を発生させます。

READ-FILEWRITE-FILEでは、バッファ領域のアドレスと読み書きするバイト数をスタックに置く必要があります。

組み込みワードの利用

[編集]

Forthには、数学関数や論理演算、メモリ操作など、様々な組み込みワードが用意されています。必要に応じてこれらのワードを活用することで、プログラムにさまざまな機能を付加できます。

2 3 MAX .  \ 最大値(3)を出力
S" HELLO" SWAP DROP  \ スタック操作サンプル
HEX 1F20 4 DUMP  \ メモリダンプ

MAXは2つの数値の最大値を、SWAPは2つの値の順序を入れ替え、DROPは最上位の値を捨てます。DUMPは指定したアドレスからのメモリ内容をダンプ出力します。

組み込みワードには、浮動小数点演算、文字列処理、マルチタスク、IPCなど、様々な分野のものがあります。また、必要に応じてワードを自作したり、外部ライブラリからリンクしたりできます。Forthはコンパクトなシステムながら、組み込みワードの充実により高い機能性を発揮します。

アプリケーション開発

[編集]

Forthは小規模な組み込みシステムから大規模なアプリケーションまで、幅広い用途に使用できる言語です。この章では、Forthにおけるアプリケーション開発の手法と、実例について解説します。

モジュール化とワード管理

[編集]

Forthはワードをベースとしたモジュール性の高いプログラミングができます。大きなアプリケーションを開発する際は、適切にモジュール分割してワードを設計・管理することが重要です。

Forthにはネームスペースの概念がありません。そのため、ワード名の重複を避けるためには、プレフィックスなどの命名規則を定めることが一般的です。たとえばSTRING-LENGTHのように、ワードの役割がわかるよう名前を付けます。

また、ワードをまとめてファイルに保存し、必要に応じて読み込む方法があります。Forthの多くの処理系は、ワードの定義をソースファイルに保存したり、メモリイメージを永続化したりできます。

MARKER MODULE>STRING
: STRING-LENGTH   \ 文字列の長さを求める
  ...
;

: STRING-REVERSE  \ 文字列を反転する
  ...  
;

STRING-REVERSESTRING-LENGTH  +MODULE

作成したワードはMODULE>NAMEでモジュール名を付け、+MODULEでモジュールを完成させることができます。他のソースからUSE MODULE>NAMEとすれば、そのモジュールのワードが使用可能になります。

大規模なシステムでは、このようなソースレベルの分割に加え、オーバーレイ、仮想メモリ、マルチタスク機能を活用することで、メモリの効率的な使用が可能です。

グラフィックス処理

[編集]

Forthでもグラフィック処理が可能ですが、組み込みの機能は限られています。ほとんどの処理はユーザ定義ワードとして実装する必要があります。

ビットマップ画像の表示や、ウィンドウ、マウスカーソルなどの基本的なGUIの作成はある程度可能です。高度な3Dグラフィックスなどを実現するには、外部ライブラリを利用する必要があります。

NEED FRAMEBUFFER  \ フレームバッファへのアクセスモジュール

: DRAW-LINE  ( x1 y1 x2 y2 -- )
   \ ブレザンハムのアルゴリズムでライン描画
   ...
;

: DRAW-RECT ( x y w h -- )
   2SWAP 2>R  2R>   \ (x y w h -- x y x+w y+h)
   DRAW-LINE  2>R 2R>   
   DRAW-LINE  2SWAP
   DRAW-LINE  2SWAP
   DRAW-LINE
;

FRAMEBUFFER-INIT  \ グラフィックスサブシステムの初期化
100 100 300 200 DRAW-RECT  \ 長方形を描画

上記では、フレームバッファの制御関数がロードされた前提で、DRAW-LINEを使ってDRAW-RECT(長方形描画)を実装しています。スタックを活用して座標値を受け渡し、基本的な幾何図形を描画しています。

Forthはグラフィックスパイプラインなどの高度な機能を組み込んでいませんが、スタックベースの処理により、低レベルのデバイスアクセスが可能です。組み込みシステムのディスプレイやLED制御など、グラフィックス関連の処理に適しています。

システムプログラミング

[編集]

Forthの本質的な特性として、システムプログラミングに適していることがあげられます。小規模でコンパクト、高速動作、低レベルなメモリアクセスが可能というメリットがあります。

OSカーネル、デバイスドライバ、BIOS、ブートローダなどの開発にForthは広く使われてきました。組み込みシステムでは、センサーやI/Oデバイスの制御に最適です。スタック指向のモデルにより、レジスタレベルの処理が直接かけるのも特徴です。

例えば、Forthを使ってブートローダを実装する場合には、次のようなプログラムになります。

HEX 
07C0  INPORT  DROP  \ DOSデータ領域クリア
0800 0  20 MOVE     \ リセットベクタ設定

KERNEL.IMG LOAD     \ カーネル読み込み 

A000  OUTPORT       \ VGAモードへ切り替え

1000  JMP           \ カーネル実行
...

\ BIOSブート用の簡単なForth読み込みコード
:KERNEL.IMG
 LOAD CORE SWAP CPU KERNEL ;

BIOSなどの低レベル機能を呼び出してブートローダを構築する処理が、Forthの文法でコンパクトに記述できる様子がわかります。

また、Forth自身が小規模で可搬性が高いため、Forth処理系そのものもForthで書かれた簡単なコアを、ホストOSやアーキテクチャに合わせてセルフホスト (移植) する手法が一般的です。これは、アプリケーションと実行環境を一体化して開発できる利点があります。

高度なトピック

[編集]

ここまでForthの基本的な構文と概念、データ構造、アプリケーション開発の実例などを解説してきました。この章では、Forthの高度な利用方法としてパフォーマンス最適化、マルチスレッド・マルチタスク、メタプログラミングについて取り上げます。

最適化とパフォーマンス

[編集]

Forthはコンパクトで高速な言語ですが、さらなる最適化の余地があります。ワードの定義を工夫したり、ネイティブコードコンパイラを使ったりすることで、パフォーマンスを向上できます。

ワード定義の最適化では、冗長な演算を排除したり、インラインコーディングの手法を用いたりします。例えば、次のようなワード定義があるとします。

: MAX2  ( n1 n2 -- max )  2DUP > IF SWAP DROP ELSE NIP THEN ;

このMAX2は一時的に2つの値を複製(2DUP)し、IF..ELSE..THENの条件分岐で最大値を選びます。しかし、この一時複製が無駄な演算となっています。次のように書くとコードがシンプルになります。

: MAX2  ( n1 n2 -- max )  OVER OVER > IF SWAP THEN ;

ワードの最適化には一般的なテクニックがあり、Forthのコンパイラ作者から提案されているものを参考にするとよいでしょう。

また、Forthにはネイティブコードコンパイラと呼ばれるもので、ワードの実行コードをネイティブマシン語に変換できるものがあります。Forthの対話的な実行環境は便利ですが、ネイティブコードの方が高速に動作します。こうした最適化手法を使い分けることで、Forthのパフォーマンスを飛躍的に向上できます。

マルチスレッドとマルチタスク

[編集]

Forthには並列実行の機構が組み込まれていないため、マルチスレッドやマルチタスク処理を実現するにはユーザ定義の実装が必要です。しかし、Forthの言語特性を活かせばきわめてコンパクトな並列処理ができます。

マルチスレッドでは、スタックを分離し、スレッド毎にスタックのコピーを持つ手法が一般的です。スレッドの切り替えはスタックのコンテキストスイッチで実現できます。また、Forthのデータストアクセス方式を応用すれば、IPC(プロセス間通信)の仕組みを作ることもできます。

: NEW-TASK  ( -- )
  HERE CURRENT-STACK >R  CREATE-STACK  SWAP
  [: DO-SOME-WORK ;]  ( compile: word behavior )
  R> SET-CURRENT
;

この例では、新しいタスクを作成するワードNEW-TASKを定義しています。CREATE-STACKでスタックを新しく割り当て、DO-SOME-WORKのようなワード定義をタスクとして登録します。実行時にはスケジューラによってこれらのタスクが実行されます。

マルチタスク機構を実装する例は多数あり、スレッド単位でデータを扱う方式、メッセージキューによる方式、それぞれ長所と短所があります。並列処理にはデッドロックなどの問題点もありますが、Forthではコンパクトでシンプルなコード記述が可能です。

メタプログラミング

[編集]

Forthにはプログラムを生成・変更するメタプログラミングの機能があり、コードを自動生成したり、プログラムを動的に変更したりできます。この機能は、Forthをドメイン固有言語(DSL)の開発などに適用できることを意味しています。

メタプログラミングには、コンパイル時に評価される構文[...]と実行時構文EVALがあります。どちらもプログラムをデータとして扱えるため、動的にプログラムを生成・変更できます。

: HILBERT-CURVE  ( n -- )
   0 ?DO
     [: 2DUP RECURSE >R >R
        1 HCURVE R> R> RECURSE
        3 HCURVE ;]
     POSTPONE LITERAL POSTPONE HCURVE
   LOOP
;

この例では、ヒルベルト曲線を描画するコードを動的に生成しています。POSTPONEはワードのコンパイルを遅延させ、[: ... ;]の構文はその間のテキストを実行可能なコードとしてコンパイルします。この手法により、 Hilbertカーブの次元(n)に応じて、その描画コードを生成しています。

Forthのメタプログラミング機能は、高度な用途に役立ちます。例えば、ユーザインターフェースビルダー、コードジェネレーター、プログラムシンセサイザなどでその力を発揮できます。Forthの柔軟性を最大限に活かせる側面といえるでしょう。

サンプルプログラムとプロジェクト

[編集]

Forthの理解を深めるには、実際にコードを書いて動作を確認することが大切です。この章では、様々なサンプルコードを紹介するとともに、実践的なForthプロジェクトの例を示します。

様々なサンプルコード

[編集]

以下はForthの基本的な機能を示すサンプルコード集です。

再帰によるフィボナッチ数列
: FIB ( n -- fib-n ) 
    DUP 0= IF DROP 1 EXIT THEN
    DUP 1 = IF    DROP 1 EXIT THEN  
    DUP 1- RECURSE
    SWAP 2 - RECURSE
    +
;
文字列操作
: UPPERCASE  ( addr n -- addr n )
    OVER + SWAP ?DO
        I C@ [CHAR] a [CHAR] z 1+ WITHIN 
        IF [CHAR] A [CHAR] a - + THEN
        I C!
    LOOP
;

: REVERSE-WORDS  ( addr n -- )
    [CHAR] \ SKIP SPACES  OVER >R
    BEGIN
        OVER C@ [CHAR] \ <> 
    WHILE
       [CHAR] \ PARSE  DUP >R REVERSE
       R> OVER C!  CHAR+
    REPEAT
    R> DROP 2DROP
;
ベクトル・行列計算
REQUIRE DOUBLE  \ 倍精度浮動小数点サポート

: VZEROS ( n -- addr )  \ n要素のゼロベクトルを生成
    CREATE  HERE  0 SWAP 8 * DUP ALLOT  FILL  
;

: MZEROS ( rows cols -- addr )  \ rows x cols の零行列を生成
    SWAP VZEROS  SWAP 0 ?DO  OVER VZEROS  COLS + LOOP  
;
簡易グラフィックス
VARIABLE FRAMEBUF  \ フレームバッファアドレス
: PIXEL  ( x y color -- )  \ 1ピクセル書き込み 
    FRAMEBUF @ >R  2 * R@ + 3 *  RP@ C!+ RP@ C!+  RDROP
;

: LINE  ( x1 y1 x2 y2 c -- )  \ ラインの描画
    >R  2SWAP R>  2>R  2R@ PIXEL  2R> PIXELLINE  
;

これらの例は、スタックを使った処理の流れ、ユーザ定義ワードの作成、再帰など、Forthのプログラミング手法の基本を示しています。コンパクトながらも表現力の高さがわかるでしょう。

実践的なプロジェクト例

[編集]

次に、もう少し大がかりなForthプロジェクトの例を紹介します。

Forth Chip-8 エミュレーター

Chip-8はレトロなゲームコンソールで、Forthでその命令セットをエミュレートするプロジェクトです。グラフィックス、入力処理、命令デコーダなどを実装します。

カーネルとブートローダ

ForthでOSカーネルの基本部分を実装し、ブートローダと合わせてシステムを構築するプロジェクト。メモリ管理、タスク管理、ファイルシステム、ドライバなどの機能が実装できます。

Webサーバー

ForthでHTTPリクエストを処理するWebサーバーを作成。ソケット通信、文字列処理、ファイル入出力などの機能が必要になります。追加してWebアプリケーションフレームワークなども作れます。

オープンソースForthプロセッサ

Forthを使ってCPU命令セットを記述し、ゲートレベルまでFPGAに実装するプロセッサの設計プロジェクト。ハードウェア記述言語との連携が重要です。

このようなプロジェクトを通して、Forthの実用性とシステムプログラミングでの可能性を体験できます。Forthという言語自体を使って、OSやハードウェアコンポーネントを実装できるのが最大の特徴です。いずれのプロジェクトも一般的な規模になると、モジュール分割やワード管理、ツール活用などの工夫が必要になります。

参考資料

[編集]

Forthについてより深く学ぶには、書籍や公開されているオンラインリソースを参照するのが有効な方法です。また、活発なForthコミュニティに参加することで、他の開発者からの助言を得られます。

関連書籍の紹介

[編集]

Forthの入門書から応用、さらには処理系の実装に至るまで、多くの優れた書籍が出版されています。

  • 「Forth入門」レオ・ブロディ (ISBN:978-4875930297 工学社)
    • Forth言語の基本から、アプリケーション開発までをカバーした長年の定番書。
  • 「Thinking Forth」Leo Brodie (Punchy Publishing) / Forth思考 ―問題解決のための言語と哲学― レオ・ブロディー著
    • Forth哲学に重きを置いた名著。Forthの考え方を深く解説。
  • 「Forth Programmer's Handbook」(ISBN:978-1419675492 Forth Inc.)
    • ANS Forthの規格書で、WordSetの詳細が書かれている。

これらの書籍を手に入れられれば、Forthの理解が格段に進むはずです。電子書籍としても出版されているものがあります。

オンラインリソース

[編集]
Wikipedia
Wikipedia
ウィキペディアForthの記事があります。

インターネット上にも無料で参照できるForth関連の豊富なリソースが存在します。

Webサイト、ソースコード、ニュースレターなど、さまざまな形でリソースが公開されているので、活用していきたいものです。

Forthコミュニティ

[編集]

長年にわたりForth言語を支える活発なコミュニティが存在し、開発者同士が支え合う環境があります。

  • Newsgroups: comp.lang.forth
    • ディスカッションの場となるForth専用のニュースグループ。
  • 地域 Forth グループ
    • ヨーロッパ、アメリカ、アジアなどの地域Forthグループ。

オンラインフォーラムやメーリングリスト、対面の勉強会など、様々な形でForth開発者同士が交流を深めています。初心者でもこうしたコミュニティに参加することで、サポートを受けられます。Forthの小さな世界ですが、地域を越えて開発が行われているのが特徴です。

Forthの参考文献やオンラインリソース、そしてコミュニティを上手く活用することで、この独特な言語の理解が一層深まるはずです。

脚註

[編集]