Shoeisha Technology Media

CodeZine(コードジン)

特集ページ一覧

JavaとSAXパーサでXMLベースのプログラミング言語を作る

字句解析や構文解析を省略した簡易言語の実装

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加
2005/05/16 00:00

ダウンロード ソースコード (55.3 KB)

プログラミング言語を作ってみたいと思ったことはありませんか? 字句解析と構文解析を省略するために、XMLベースにして、すぐに実行可能なプログラミング言語をJavaで実装してみます。

目次

はじめに

 プログラミング言語を作ってみたいと思ったことはありませんか? あるいは、どうして単なるテキストファイルに過ぎないソースファイルがプログラムとして実行できるのか、仕組みを知りたいと感じたりしたことはありませんか? 仕組みを知るには実際に作ってみるのが一番です。結局、どっちにしてもプログラミング言語を作ることになりますね。

 ところが仮にそう思ったとして、プログラミング言語についての本を手に取ると延々と字句解析(ソースファイル中の文字列を定数や演算子などの分解すること)について説明しているのでせっかく湧いた興味がみるみるうちに醒めてしまう、そんな経験はありませんか。もちろん、「本物」のプログラミング言語を作るにはそこでますます熱意が湧いてくる必要があるのでしょう。でも、単に興味があるだけならもっと簡単に、いきなりプログラミング言語そのものを作れたほうが良いじゃないですか。

 というわけで、字句解析と構文解析(字句解析の結果から文の構成を調べること)抜きでいきなりプログラミング言語をプログラムしてみましょう。これらの解析処理は人間がソースファイルを記述する時点で済んでいることにしてしまえば良いからです。具体的にはプログラミング言語のソースファイルをXMLで記述し、SAXパーサから要素や文字を読み込んだ時点で直接、構文解析木を作成します。その結果、すぐに実行可能なプログラミング言語が実装できます。

対象読者

 ここでは、実装言語としてJavaを利用します。

対象読者へのもうひとつの「はじめに」

 この記事のプログラムはJavaによる以下の実装技術の実例として構成しています。

XMLの利用

 SAXパーサを直接利用してプログラム固有のオブジェクト構造(ここではInterpreterパターンのインスタンス)を生成します。

各種デザインパターンの適用

 記事中で多少の解説を試みますが、Interpreterパターンは当然として、Strategyパターン、Factoy Methodパターン、Abstract Factoryパターン、Singletonパターン、Builderパターンなどの実装例としても参照できます。

必要な環境

 J2SE 1.4以上を想定します。サンプルはjavacに「-source 1.4」をオプション指定してコンパイルしたものを1.5.0上で実行して動作確認をしています。

その他に用意しておいたほうが良いもの

  • ant 1.6以上またはNetBeans4.0以上
  • ソースファイルが複数に分かれているためantのビルドスクリプトを用意してあります。一括してビルドするには、antそのものか、antスクリプトをプロジェクトで直接扱えるNetBeans4.0以上を利用するのが良いでしょう。
  • JUnit 3.8.1
  • ソースファイルの中にテストプログラムを用意してあります。実際にテストプログラムをビルドして動かすにはjunitが必要となります。

目標

実装範囲

 仮にもプログラミング言語なのですから、変数、代入、制御構文としてif―then―elsewhileを実装しましょう。また、Javaで開発するのですから、Javaのオブジェクトも利用できた方が良いですね。後はプログラミング言語自身のデバッグのためにもコンソール出力機能は用意しましょう。

 以上のことから、とりあえず、次のJavaのプログラムに相当する処理を実行可能なプログラミング言語の実装を目標とします。

実装の目標とするプログラム
public class Sample {
    public static void main(String[] args) {
        if (args.length == 0) {
            System.out.println("no args");
        } else {
            int i = 0;
            while (args.length > i) {
                System.out.println(args[i]);
                i++;
            }
            System.out.println("done");
        }
    }
}

言語デザイン

 どういう言語にするかデザインするのは本来一番おもしろい部分の1つですが、ここでは実装方法を示すことが目的ですので、できる限りにシンプルにします。また、細かいことは決めないで、とにかく実装範囲を楽に実装することを優先しましょう。とりあえず、ここでは次のXML(sample.xml)を実際のソースファイルとして想定します。

sample.xml
<?xml version="1.0" encoding="shift_jis"?>
<oreprog>
  <if>
    <condition type="eq">
      <call name="args" method="size"/><value type="int" value="0"/>
    </condition>
    <block>
      <print><value type="string" value="no args"/></print>
    </block>
    <block>
      <var type="int" name="i" value="0"/>
      <while>
        <condition type="gt">
          <call name="args" method="size"/><value name="i"/>
        </condition>
        <block>
          <print><call name="args" method="get"><value name="i"/>
          </call></print>
          <var type="int" name="i">
            <add><value name="i"/><value type="int" value="1"/></add>
          </var>
        </block>
      </while>
      <print><value type="string" value="done"/></print>
    </block>
  </if>
</oreprog>

 XMLの要素名を制御構文や演算子として対応させていることが、目標のプログラムと読み比べると理解できると思います。要素名と構文木のノードを直接対応させることで、XMLの読み込みと同時に構文木を生成できるようにしてあるわけですね。

 なお、本記事では以降、ここで作成するプログラミング言語をルート要素名から「oreprog(オレプログ)」と呼ぶことにします。

ファイル構成

 ダウンロードしたファイルはzipで圧縮してあります。展開すると「intp」というディレクトリを頂点としたディレクトリ階層ができます。すぐに実行できるようにコンパイル済みのクラスファイルも添付してあります。また、ソースファイルはすべてシフトJISでエンコードしています。

intpディレクトリ

build.xml

 antのビルドスクリプトです。

intp\src\com\example\progディレクトリ

OreProg.java

 プログラム本体です。ファイル数の抑制のために小さなクラスはstatic内部クラスとして実装しているので、本記事では主としてこのソースファイルについて解説します。

ValueExpression.java

 直定数(value要素に対応)処理クラスです。

 リフレクションを利用して文字列からオブジェクトを生成します。サポートしているのは、intlongString(xmlへの記述時はstringと小文字になります)、コンストラクタの引数に1つのStringを取るオブジェクトの生成です。

VariableExpression.java

 変数(var要素に対応)処理クラスです。ValueExpression.javaを継承しています。

CallExpression.java

 オブジェクトのメソッド呼び出し(call要素に対応)処理クラスです。

 リフレクションを利用して与えられたオブジェクトのメソッドを呼び出します。プログラムを短くするために、メソッドの検索については非常に限定された機能しか実装していません。

ConditionalExpression.java

 条件判断(condition要素に対応)処理クラスです。

intp\src\com\example\exprogディレクトリ

ExProg.java

 OreProg.javaを継承し、oreprogに独自の言語拡張を実装するサンプルプログラムです。

 元のOreProgに手を入れずに、新たな構文要素としてint型の定数を導入しています。

intp\testディレクトリ

sample.xml

 sample.xmlです。

test.xml

 筆者がテストに利用したxmlです。

extest.xmll

 ExProgのテストに利用したxmlです。

intp\test\com\example\prog\OreProgTest.java

 JUnitを利用したテストプログラムです。valuevarcallifconditionblockwhile要素をテストします。

intp\build\classes

 ビルド後のクラスファイルを格納してあります。antのデフォルトターゲットは、このディレクトリへクラスを生成します。

サンプルの実行

 コマンドラインで、zipを展開したディレクトリ(intp)に入り、

set CLASSPATH=[展開したディレクトリ名]\intp\build\classes;%CLASSPATH%
java com.example.prog.OreProg test/sample.xml [引数 ...]

 と入力することで実行できます。なお、CLASSPATH環境変数の設定はご利用の環境に合わせて調整してください。以下はMac OS Xでの実行例です。

$ cd /var/tmp/intp
$ export CLASSPATH=/var/tmp/intp/build/classes:$CLASSPATH
$ java com.example.prog.OreProg test/sample.xml
no args
$ java com.example.prog.OreProg test/sample.xml a b c
a
b
c
done

 引数で与えた文字列が順次プリントされ、最後に「done」と出力されることが確認できます。

 なお、J2SE 1.4.2で実行する場合(J2SE 1.4.2_08で確認)は以下のように-Dオプションを使用してSAXパーサの指定が必要となります。

$ java -Dorg.xml.sax.driver=org.apache.crimson.parser.XMLReaderImpl
    com.example.prog.OreProg test/sample.xml a b c

 指定しない場合、

Exception in thread "main" org.xml.sax.SAXException:
 System property org.xml.sax.driver not specified

 という例外となります。


  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • arton(アートン)

    専門は業界特化型のミドルウェアやフレームワークとそれを利用するアプリケーションの開発。需要に応じてメインフレームクラスから携帯端末までダウンサイジングしたりアップサイジングしたりしながらオブジェクトを連携させていくという変化に富んだ開発者人生を歩んでいる。著書に『Ruby③ オブジェクト指向とはじめ...

All contents copyright © 2005-2019 Shoeisha Co., Ltd. All rights reserved. ver.1.5