プログラミング
LispによるForthの実装、そしてForthからLispへ
Lisp moving Forth moving Lisp (letoverlambda.com)
要約
この記事は、Lispのマクロ機能を用いてプログラミング言語Forthを実装する手法を解説しています。Forthの歴史、哲学、そしてLisp環境での実装における「レジスタ」や「辞書」といった概念を、Lispのコード例と共に説明し、両言語のメタプログラミングにおける類似性と違いを探求します。
全文翻訳
Let Over Lambda -- Lisp 50年の歴史 by Doug Hoyte LispがForthを動かし、ForthがLispを動かす 奇妙なデザイン この章は、これまで本書で見てきた多くのマクロ技術の集大成です。私たちが開発したマクロ抽象化を用いて、私のお気に入りのプログラミング言語の一つであるForthの実装を作成します。この実装はForthの重要なアイデアのほとんどを体現していますが、非常に異なっており、リスピーです。この章のコードにはいくつかの興味深い用途がありますが、その主な目的は、Lispの読者に対してForthのメタプログラミングの概念と基礎を教えること、そして本書の中心的なテーマであるマクロを用いた構文の二重性の作成と使用について議論するためのプラットフォームとなることです。
Lispを除けば、Forthほど豊かで魅力的な歴史を持つ言語は他にありません。そして、それを見つけられたことに感謝しています。その理由、そして他のすべてのために、この章は、私にForthとコンピュータプログラミングを紹介してくれた父Brian Hoyteに愛を込めて捧げられます。この章は、[THREADING-LISP]とHenry Bakerの研究[LINEAR-LISP][LINEAR-LISP-AND-FORTH]に部分的に触発されています。
Forthは、強力な政府、学術、または企業のスポンサーなしに作成および開発された最初のプログラミング言語でした。少なくとも、そのような言語で成功した最初のものです。大規模組織のニーズによって動機付けられるのではなく、ForthはChuck Mooreによって1968年頃に天文学、ハードウェア設計などの自身のコンピューティングニーズを解決するために独自に発明されました。それ以来、Forthは熱心な草の根ユーザーコミュニティによって配布、実装、改善されてきました[EVOLUTION-FORTH-HOPL2]。MIT(および後のDARPA)による初期のLispやCOMMON LISPの支援、IBMのFORTRAN、AT&TのUnix言語Cと比較してみてください。
これらのルーツと、コンピュータソフトウェアおよびハードウェアの役割に対する一般的な哲学の違いにより、Forthは異なります。Lisp以上に、Forthは奇妙に見えます。しかし、Lispのように、Forthが奇妙に見えるのには理由があります。それはスタイル以上のものを考慮して設計されたからです。Forthは意図的に奇妙であり、この設計はマクロに関連しています。
今日、Forthは一般的に、リソースが厳しく制限されているいわゆる組み込みプラットフォームで見られます。言語全体が、これまで作成されたほぼすべてのプログラム可能なコンピュータシステムに実装できることは、Forthの設計の証です。Forthは、実装と実験を可能な限り容易にするように設計されています。実際、Forthクローンを作成することは非常に些細なことなので、プログラミング言語の設計に関心のあるプログラマーにとって、Forthスタイルのスタックベース言語を1つか2つ発明することは、ほぼ通過儀礼です。Forthにルーツを持ち、興味深い貢献をしたスタックベース言語には、PostScriptやJoyがあります。
しばしば、重要なForthの実装上の決定は、Forthが実装されているコンピュータの正確なリソースに基づいています。Forthプログラマーは、実際のレジスタにマッピングされるか、メモリ位置にマッピングされるか、あるいはまったく異なる方法で実装される必要がある一連の抽象レジスタを考案しました。しかし、無限の可能性とほとんど制限のない環境であるLispでForthを実装している場合、私たちはどうすればよいでしょうか?単にForthの抽象レジスタをLispコードに任意にマッピングするのではなく、一歩引いて考えてみます。ChuckがLispマシンを持っていたら、Forthはどう見えるでしょうか?任意の機械(実機または仮想)の能力に合わせてForthを調整するのではなく、Lisp上で実装されたときにシンプルさと能力のために最適化された、最小限のForth抽象レジスタのセットを探求します。
しかし実際には、最適な抽象概念のセットを探すことは、Chuckが何十もの異なるアーキテクチャ上の何十もの異なるForth実装の経験を使ってForthを作成したときに彼が行ったことです。だからこそForthは素晴らしいのです。Lispのように、Forthは言語設計の空間における高い局所的極大値を表しており、Lispのように、Forthはプログラミング言語や標準のセットというよりも、構築材料であり、何が機能し何が機能しないかに関する知恵のコレクションです。
FORTH-REGISTERS
(defvar forth-registers '(pstack rstack pc dict compiling dtable))
forth-registers変数は、私たちのForthマシン用の抽象レジスタを表すシンボルのリストです。もちろん、Lispはレジスタやフィクスナムの terms で考えるのではなく、変数やシンボルで考えます。変数の名前のリストだけでForth環境の開発を開始するのは奇妙に思えるかもしれませんが、これは実際にはForthシステムを実装する際の最初のステップです。Forthを作成することは、Lispに次いで、美しさと巧妙さにおいてのみ超えられる独創的なブートストラッププロセスです。このプロセスの控えめな説明を以下に示します。
Forthの特徴的な機能の1つは、プログラムがサブルーチンにパラメータを渡すために使用するスタックデータ構造、およびこれらのサブルーチン全体での実行パスを追跡するために使用するスタックデータ構造への直接アクセスです。Forthは、ほとんどのプログラミング言語とは異なり、スタックデータ構造のこれらの2つの用途を、操作できる2つのスタックに分離しているため、特に興味深い1。ほとんどの言語では、スタックを直接操作することはまったく許可されていません。
典型的なC実装では、関数呼び出しのパラメータとそのいわゆるリターンアドレスは、すべての関数呼び出しに対して1つの可変サイズのスタックフレームに格納されます。Forthでは、これらはパラメータスタックとリターンスタックという2つの異なるスタックであり、抽象レジスタpstackとrstackとして表されます。COMMON LISPのpushおよびpopマクロを使用します。これは、これらのスタックがほとんどのForthで使用される配列データ構造の代わりにconsセルリンクリストで実装されていることを意味します。
抽象レジスタpcはプログラムカウンタの略で、現在実行中のコードへのポインタです。Forthコードとは何か、そしてそれにどうポインタを指せるかは、抽象レジスタcompilingとdtableと同様に、すぐに説明されます。
FORTH-WORD
(defstruct forth-word name prev immediate thread)
Forthのもう1つの構成要素は、辞書の概念です。Forth辞書は、Lisp関数に似たForthワードの単方向リンクリストです2。言い換えれば、それらは関数ではなく、プロシージャです。
ワードはLisp構造体で表されます。構造体は、ベクトルとして実装されることが多い効率的なスロットベースのデータ構造です。nameスロットは、辞書でワードをルックアップするために使用されるシンボル用です。Forth辞書はアルファベット順ではなく、時系列順に格納されていることに注意してください。新しいワードを追加するときは、辞書の末尾に追加するため、辞書をトラバースすると、最後に定義されたワードが最初に検査されます。辞書の最後の要素は、常に抽象レジスタdictに格納されます。辞書をトラバースするには、dictから開始し、ワード構造体のprevポインタをたどります。これは、以前に定義されたワードを指すか、最後のワードの場合はnilを指します3。はい、Lispプログラマーはconsセル以外のリンクリストの代替手段を使用することもあります。
FORTH-LOOKUP
(defun forth-lookup (w last))
(if last (if (eql (forth-word-name last) w) last (forth-lookup w (forth-word-prev last))))
w(ルックアップするワード)とlast(検索する辞書)が与えられると、forth-lookupは、ワードwが辞書で見つかったかどうかによって、Forthワード構造体またはnilを返します。Lispとは異なり、Forthではワードを数値やその他の非シンボルで名前付けできるため、比較関数eqではなくeqlが使用されます。
forth-wordのimmediateスロットは、ワードがimmediateであるかどうかを示すフラグです。Immediacyは、すぐに詳細に調査するForthのメタプログラミング概念です。今のところ、Lispの対応物への大まかなアナロジーを次に示します。immediateワードは、実行時ではなくコンパイル時に実行されるForth関数であるという点で、Lispマクロに似ています。何?Lispだけがマクロを持つことになっている。COMMON LISPのマクロシステムは、最高のフォ