知的にエキサイティングな行為はいろいろあります。例えば,パズルを解くこと,囲碁や将棋をたしなむこと,プログラミングなどがそれにあたります。

 なかでも,プログラミングにはパズルや囲碁・将棋にない大きな特徴があります。それは,プログラミングに使用する言語処理系を,プログラミングによって生み出すことができという特徴です。パズルを解くことでパズルが生み出されたり,将棋を指すことで将棋のコマが生まれるようなことはあり得ません。しかし,プログラミングの世界ではそのようなことがあり得るのです。それを体験できることが,ほかと異なるプログラミングならではの面白さと言えます。

 「いや,理屈はそうであっても,難しくて僕らに作れるわけがない…」,そう思う方々もいるでしょう。もちろん,大企業や有名な団体が開発し,配布しているような有名言語処理系(例えば米MicrosoftのVisual StudioやGNUプロジェクトのGNU Compiler Collectionなど)と同じ水準のものは,そう簡単には作れません。それらは,トップ・クラスの才能が結集し,切磋琢磨しながら開発しているからです。彼らと張り合うには,才能と運と頼れる仲間が必要です。

 しかし,思い起こしてみてください。プログラミングはそれ自体が楽しいことです。言語処理系のプログラミングを楽しむことが目的であるなら,何も有名処理系と同じ水準を目指す必要などないのです。必要なのは,「自分だけのオリジナルの処理系」を作り出すことであって,作り出したものの水準を張り合うことではありません。

 有名処理系と張り合おうなどと思いさえしなければ,言語処理系は驚くほど小さく作ることができます。小さければ過剰に複雑になることもなく,中身はさほど難しくなりません。それを作るために,才能も運も頼れる仲間も,いずれも必須ではありません。

768バイトから作れる「小さい」処理系

 では,処理系はどれくらい小さく作れるのでしょうか? 筆者が知っている最も小さなプログラム言語の処理系は,1970年代に生まれた「VTL(Very Tiny Language)」というものです。ソースコードを編集するエディタなどを含め,インタプリタの実行時のサイズはたったの768バイトのサイズでした。念のために補足しますが,KもMも付かない768バイトです。これを解説した「768バイトで何ができるか」という書籍があったくらいですから,間違いありません。

 とはいえ,VTLは記号を用いた表記を行うプログラム言語で,VTLで書かれたソースコードは難解です。例えば,条件判断を“if”というキーワードではなく「セミコロン記号(;)への代入」で表記するなど,ちょっと試してみるには取っつきにくいものです。VTLの小ささは,その取っつきにくさを代償として手に入れたものなので仕方ありません。

 VTLよりもう少し大きな処理系として,「Tiny BASIC」と呼ばれるカテゴリのものがあります。これはエディタを含め,インタプリタの実行時のサイズが2Kバイト程度のものを言います。Tiny BASICは,条件判断にはIF,繰り返しにはFORといったキーワードを使うなど,VTLに比べて親しみやすさが格段に上がります。

 2Kバイト程度のプログラムなら,仕様さえわかっていれば試行錯誤しているうちに出来上がってしまうでしょう*1。無駄な部分を注意深く取り除かないと2Kバイトより大きくなってしまうかもしれません。しかし,2Kバイトより多少大きくても,メモリー容量の大きな今どきのパソコンでは問題ではありません。このようなものを作るためには,才能も運も頼れる仲間も必要ありません。必要なのは,やる気と,それを楽しいと思う感性だけです。

携帯ゲーム機がルーツの超小型BASIC風インタプリタ

 このような処理系で使われる小さな玩具のようなプログラム言語をトイ言語(toy language)と呼ぶことがあります。この記事では,その一例として非常に小規模なBASIC風インタプリタ「ワンべぇBaby」のソースコードを解説します。いくらトイ言語とはいえ,ゼロから試行錯誤で処理系を組み上げるのも無駄が多く面倒です。トイ言語のインタプリタの作成には基本パターンや定石のようなものがあります。この記事では,それを紹介しつつ,実はプログラム言語の処理系はこんなに簡単に作れてしまうということを示したいと思います。

 ワンべぇBabyのルーツは,バンダイの携帯用ゲーム機「WonderSwan(ワンダースワン)」で使用されることを前提に筆者が開発したインタプリタ「ワンべぇ」にあります。これは,WonderSwanで実行する自作ソフトを開発するためのキット「WonderWitch」で開発したもので,開発環境一式がWonderSwanの内部で動くという特徴があります。2001年のWonderWitchプログラミング・コンテスト「WWGP2001」でテクニカル賞を受賞しました。

 どうしてわざわざ携帯用ゲーム機用の言語処理系を取り上げるのか不思議に思う方もいるかもしれません。その理由は,君も携帯用ゲーム機のプログラミングを楽しもう…,ということではありません。WonderSwanのような携帯用ゲーム機には,今日のパソコンにはない大きな特徴があるからです。それはメモリーの容量が小さく,CPUの速度も遅いため,プログラムのサイズや実行時の効率をよく考えて作る必要があるという点です。

 最近のパソコンは,メモリー容量が極めて大きくなったために,厳しく制限されたコンパクトなプログラムを作る必要性が薄れています。言語処理系もその例外ではありません。気分次第で機能を次から次へと追加していくこともできてしまいます。しかし,初めて試すのがそのように肥大化した言語処理系では,題材としてあまり適切ではありません。そのような処理系には本質と関係のないコードが多く含まれ,理解の妨げになるからです。ぎりぎりまでシンプルに切りつめられ,本質と関係ないコードが極力排除された言語処理系はどこにあるのかと探してみたら,それは容量の制限が厳しい携帯用ゲーム機にあったわけです。

 この記事では携帯用ゲーム機に固有のコードはすべて取り除き,オリジナルのワンべぇよりさらにシンプルなソースコードを実現しました。実行環境も,Windowsに限定しました。これが,今回解説する「ワンべぇBaby」です。

 ただし,処理系を作った後で,その処理系を使って動作するプログラムが1本もないのは寂しいので,ワンべぇで記述されたSTAR TREK風ゲーム*2「STAR WITCH ~ゴブリン帝国の野望~」を実行するために最低限必要な機能は残しています(図1)。

図1●STAR WITCH ~ゴブリン帝国の野望~。ワンべぇBabyで動作するSTAR TREK風のウォー・ゲーム
図1●STAR WITCH ~ゴブリン帝国の野望~。ワンべぇBabyで動作するSTAR TREK風のウォー・ゲーム

 ワンべぇBabyの特徴の一つは,機能を簡単に追加できることです。この記事を読んで,プログラム言語処理系を作ることに興味を持ったとしても,ゼロから自分の言語を作ることはまだ容易ではないでしょう。しかし,既存の処理系に自分で考えた新しい機能を追加することで,プログラム言語処理系を楽しむ第1歩とすることができます。

 ワンべぇBabyのBabyは赤ん坊という意味ですが,つまり読者の皆さんが独自の機能を付け加えることで成長していく出発点という意味を込めた名前となっています。

“print 1+2*3”の動作を追跡する

 伝統的なBASICの特徴は,1行のコマンドを入力すると,即座にそれが実行される「ダイレクト・モード」の存在にあります。これは,ステートメントの動作が実際にどうなるか試したりするのに有益ということに加えて,ソースコード上に記述できるステートメントと直接入力して実行できるコマンドが同じであるため,覚えやすく使いやすいというメリットももたらします。

 さて,ワンべぇBabyもこのような伝統を正当に継承しています。例えば,ワンべぇBabyを開いた後,すぐにprint 1+2*3と入力してEnterキーを押すと,専用テキスト画面に7という数字が出力されます(図2)。

図2●print 1+2*3の実行結果
図2●print 1+2*3の実行結果

 printは専用テキスト画面に出力する文,1+2*3はprint文の出力対象となる計算式です。ワンべぇBabyは常識的な演算子の優先順位を持っているので,2*3(=6)が先に計算され,その後で1と6が足し合わされ,7という結果を表示します。

 この動作は,1行だけで構成されるプログラムを入力した後,即座に実行したと見なすことができます。つまり,一種の使い捨てプログラミングです。

 そこで,この結果が出るまでの動作の流れをソースコード上で追いかけてみましょう。こんな簡単な事例…というなかれ。実は,これを通じて,言語処理系の主要な部分をほとんど見て回ることができるのです。

 ソースコードを読む前に,ワンべぇBabyのソースコードのおおまかな構造を見ておきましょう。ワンべぇBabyは,Visual Studio .NET 2003を使用し,C言語で記述されています。ワンべぇBabyのソースコードは,おおまかに言って以下の四つのファイルからなっています。これらはすべて本誌のWebサイトに公開されています。

サイトの更新により、プログラムの場所を移動しました。こちらからダウンロードしてください

wonbe.c

 ワンべぇBabyの本体です。

keywords.h

 中間言語のコード番号とそれに対応する名前を関連付けるマクロ定義を行っています。独立して参照,編集されるケースが多いので,別ファイルになっています。

win32text.c

 Win32 APIに対応するコードが含まれています。

win32text.h

 win32text.cにに対応するヘッダー・ファイルです。

 今回の記事では,win32text.cとwin32text.hについては触れません。これらは言語処理系の話題とは関係がないからです。ただし,この二つのファイルの内容を入れ替えることで,Windows以外のOSで稼働させることも難しくない,ということは覚えておいても良いでしょう。言語処理系を,自分のひいきのOS上で動作するように移植することも楽しみ方の一つです。