SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

JavaCCでスクリプト言語を作成する

JavaCCでスクリプト言語を作成する 第2回

文法定義の基本的な構文


  • X ポスト
  • このエントリーをはてなブックマークに追加

文字列定義

 それでは文法定義について見ていきましょう。文法定義は「文字列定義」と「生成規則の定義」に分かれます。まず最初に文字列定義を見てみます。JavaCCでは、トークンなど、次の4種類の文字列定義があります。

種類概要
TOKENトークンの定義
SKIP空白など、処理では無視される区切り文字
MORE続くトークンと一緒に処理される
SPECIAL_TOKENコメントなど文法とは独立して現れる

 文字列定義の基本的な形は次のようになります。

TOKEN{
    <トークン名 : パターン > | <トークン名 : パターン >
}

 パターンは簡易正規表現で表します。そのとき文字は「"」で囲んで表します。

 サンプルでは次のようにして「HELLO」「JAVACC」「JJTREE」という固定文字列のトークンを定義しています。

    <HELLO: "hello">
|   <JAVACC: "javacc">
|   <JJTREE: "jjtree">

 次の部分は、「NAME」というトークンをアルファベットの繰り返しとして定義しています。

|   <NAME: (["A"-"Z", "a"-"z"])+>

 「[」「]」で囲んだ文字リストでは、文字をカンマ区切りで列挙します。また「-」を使うと、文字の範囲を指定することができます。

 次のようにすると、「大文字か小文字のアルファベット1文字」を表します。

["A"-"Z", "a"-"z"]

 また、このとき「~」を付けると、文字リストに列挙していない文字を表します。特に「~[]」としたときには任意の1文字を表します。

 回数の指定には次のものが使えます。通常の正規表現とは違い、「(」「)」は省略できません。

記述回数
(~)?省略可能
(~)*0回以上の繰り返し
(~)+1回以上の繰り返し

 次のようにすると「アルファベット1文字以上の繰り返し」を表現できます。

(["A"-"Z", "a"-"z"])+

 TOKEN以外ではトークン名は必要ないので、パターンだけを記述します。

SKIP:
{
    " " | "\r" | "\t" | "\n"
}

 トークン名に#を付けると、トークン定義だけで使えるローカルなトークンになります。

|   <#UPPERCASE : ["A" - "Z"]>
|   <NAME: ( <UPPERCASE> | ["a"-"z"])+>

 また、種別の後に「[IGNORE_CASE]」を記述すると、大文字小文字を区別しなくなります。

TOKEN[IGNORE_CASE]:
{
    <HELLO: "hello">
}

 トークンの優先順位は、一番長くマッチするものが、先に定義された順に当てはまります。

生成規則定義

 生成規則は、次のようにメソッド定義のような形で定義します。

戻り値 生成規則名(引数):
{ ローカル変数定義 }
{
  生成規則
}

 生成規則には、トークン、生成規則、文字列の連結を記述します。

 基本的な使い方は次のようになります。

void JavaCC():
{}
{
    <JAVACC>
}

 また、回数や選択などは文字列定義とほぼ同じように使えます。

記号意味
~|~選択(いずれか)
(~)?省略可能
[~]省略可能
(~)*0回以上の繰り返し
(~)+1回以上の繰り返し

 Command生成規則では次のように、JavaCC生成規則か、JJTree生成規則か、Name生成規則のいずれかを生成するように定義しています。

void Command() #void :
{}
{
        JavaCC()
    |   JJTree()
    |   Name()
}

 ここで「#void」はノードクラスを生成しないための指定です。JJTreeを使うと、生成規則ごとに「AST生成規則名」という名前のノードクラスが生成されますが、「#void」を付けるとノードクラスを生成しません。ノードクラスの生成の制御に関しては次回に説明します。

 生成規則やトークンの後に「{」「}」で囲んでJavaコードを記述すると、それらの生成規則やトークンが処理された後で実行されるアクションとなります。ローカル変数を定義すると、アクションの中で利用できます。

 Name生成規則は次のようになっています。

void Name():
{ Token t;}
{
    t = <NAME> { jjtThis.nodeValue = t.image;}
}

 ここで、トークンを変数tに代入していますが、このようにすると、識別したトークンをアクションで利用することができます。

 アクション内で使っているjjtThisはそのノード自身をあらわすオブジェクトです。

 リテラルや識別子など、トークンの文字列を処理中で利用したい場合には、このようにしてトークンのimageフィールドを取得します。

 ここで使っているnodeValueフィールドは、BaseNodeクラスで定義しているString型のフィールドです。オプションにノードの基底クラスとして指定しているので、ここで利用できます。ノードの基底クラスを指定しない場合には、生成されたノードクラスを編集してフィールドを追加しておく必要があります。

 生成規則定義の戻り値は通常voidとしますが、戻り値の型を指定することもできます。このときの戻り値は、アクション中でreturn文を使って指定します。

 サンプルでは次のように、この文法定義での開始記号になる生成規則で自分自身のノードオブジェクトを返すようにしています。開始記号はHello生成規則なので、ノードオブジェクトのクラスはASTHelloです。

ASTHello Hello():
{}
{
    <HELLO> Command() { return jjtThis;}
}

 このように開始記号の生成規則では、自分自身のノードを返すようにするのが一つのパターンです。

構文の処理

 最後に処理の記述について説明します。

 構文解析は、パーサーオブジェクトから開始記号のメソッドを呼び出すことで始まります。今回はノードを返すように指定していたので、ノードのオブジェクトが返ってきます。

Node node = parser.Hello();

 ノードのオブジェクトは、SimpleNodeクラスを継承した「AST+生成規則名」という名前のクラスになります。また、SimpleNodeクラスはNodeインタフェースを実装しています。今回のオプションにNODE_EXTENDSを指定すると、SimpleNodeクラスは指定したクラスのサブクラスになります。

 これらのクラス・インタフェースは、PARSER_BEGINのあとに指定したパッケージに作成されます。

構文ノードのクラス図
構文ノードのクラス図

 VISITORオプションを指定している場合、jjtAcceptメソッドを呼び出すとそのノードに対応するvisitメソッドが呼び出されます。jjtAcceptメソッドの第2引数に渡したオブジェクトは、visitメソッドの第2引数として渡されます。ここでは特に指定する必要がないのでnullを渡しています。

node.jjtAccept(visitor, null);

 また、それぞれのノードの処理では、jjtGetChildメソッドで子ノードを取得してjjtAcceptメソッドを呼び出すことで再帰的に処理が行われていきます。

public Object visit(ASTHello node, Object data) {
    String word =
        node.jjtGetChild(0).jjtAccept(this, null).toString();
    System.out.println("Hello " + word +"!!");
    return null;
}

 visitメソッドでの戻り値は、そのままjjtAcceptの戻り値として引き継がれます。

public Object visit(ASTJavaCC node, Object data) {
    return "ステキJavaCC";
}

まとめ

 今回は、JavaCC+JJTreeでの構文定義について大まかに説明しました。JavaCCの「examples」フォルダにはJava文法の構文定義などサンプルがあるので、構文定義を実際に行うときの参考になると思います。

 次回は簡単な式言語を定義して、実用的な構文を定義していこうと思います。

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
JavaCCでスクリプト言語を作成する連載記事一覧

もっと読む

この記事の著者

きしだ なおき(キシダ ナオキ)

フリーのプログラマ。Javaでの業務アプリケーションからC++での小型端末などさまざまなプログラムを開発。また、入門者向けのJavaセミナーなども行う。そのセミナーで鍛えられたテキストが、著書である「創るJava」。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/384 2006/08/09 11:26

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング