SHOEISHA iD

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

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

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

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

文法定義の基本的な構文


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

JavaCC+JJTreeを使ってスクリプト言語を作成する連載の第2回です。前回、オプション指定と生成クラスの定義について説明しましたが、肝心の文法定義は説明していませんでした。そこで今回は、基本的な文法定義の構文を説明します。

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

はじめに

 この連載ではJavaCC+JJTreeを使ってスクリプト言語を作成していきます。前回は、JJTreeのオプション指定と生成クラスの定義、構築方法について説明しましたが、肝心の文法定義は説明していませんでした。今回は、基本的な文法定義の構文を説明します。

環境

 前回と同じく、JavaCC 4.0、J2SE 5.0Antを用意しておいてください。また、前回のサンプルプログラムも必要です。

構文解析の用語

 ここから文法定義の書き方を説明しますが、その前に構文解析で出てくる用語をまとめておきます。

生成文法

 構文解析のプログラムを組むときには、与えられた文がどのような構文になっているかという構造を解析していくわけですが、構文解析の元となる理論では「その文がどのような規則から生成されているか」という考え方をします。そのため、使われている用語に感覚的でないと思えるものがいくつかあります。

 この理論はもともと、人が話しをするときにどのように言葉を生成してるかというところから始まっていて、生成文法と呼ばれます。そのため、文法規則のことを生成規則と言います。

 ある文を解析した結果はツリー構造になるので、このツリーのことを構文木と言いますが、このツリーは、その文を生成することができる生成規則のツリーでもあるので、導出木とも言います。

 文法での最小の単位になる単語や記号などはトークンと言います。導出木として構文木をみたときに、トークンはそれ以上の規則を生成しないので終端記号と言い、生成規則は非終端記号と言います。また、非終端記号の中で、ツリーの頂点となるものを開始記号と言います。

生成文法の用語
生成文法の用語

BNF

 文法定義で標準的に使われる表記方法にはBNFがあります。BNFは次のような記述方法です。

Command ::= JavaCC | JJTree | Name

 通常の文章では、文法の定義には必ずと言っていいほどBNFが使われています。ただ、もともとのBNFでは正規表現の「*」や「+」に該当するような繰り返し表現がないため、EBNFなど繰り返し表現などを追加したものが使われることも多いようです。

 JavaCCでの構文定義はEBNFをJava構文に近いもので置き換えた書き方になっています。例えば先ほどのBNFの例はJavaCCでは次のように記述します。

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

 JavaCCに付属のJJDocを使うと、JavaCCの構文定義ファイル(.jjファイル)をBNF形式で出力することができます。

今回利用するファイル

 今回は、前回のサンプルを少し変更して「hello xxx」のような形に対応できるようにしてみます。利用するファイルは次の通りです。

ファイル/フォルダ内容
/CodeZine
 build.xml構築スクリプト(前回のものをそのまま利用)
 build.properties環境設定(前回のものをそのまま利用)
 /src
  /codezine
   /hello
    Hello.java処理プログラム(変更)
    HelloParser.jjt文法定義ファイル(変更)
    BaseNode.javaノードの基底クラス(新たに作成)
    /parser
 /build
  /classes

サンプルプログラム

 このファイルは、前回にはなかったもので、生成されるノードの基底クラスになります。

codezine/hello/BaseNode.java
package codezine.hello;

public class BaseNode {
    public String nodeValue;
}

 文法定義ファイルでは、オプションにノードの基底クラスの指定を記述して、TOKENNAMEというトークンを登録、またNameという生成規則を定義しています。

codezine/hello/HelloParser.jjt
//オプション定義
options{
    STATIC=false;
    MULTI=true;
    VISITOR=true;
    NODE_EXTENDS="codezine.hello.BaseNode";
}

//パーサークラスの定義
PARSER_BEGIN(HelloParser)
package codezine.hello.parser;
public class HelloParser{

}
PARSER_END(HelloParser)

//トークンの定義
SKIP:
{
    " " | "\r" | "\t" | "\n"
}
TOKEN:
{
        <HELLO: "hello">
    |   <JAVACC: "javacc">
    |   <JJTREE: "jjtree">
    |   <NAME: (["A"-"Z", "a"-"z"])+>
}

//文法の定義
ASTHello Hello():
{}
{
    <HELLO> Command() { return jjtThis;}
}

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

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

void JJTree():
{}
{
    <JJTREE>
}

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

 処理プログラムには、Nameノードの処理をするメソッドを追加しています。

codezine/hello/Hello.java
package codezine.hello;
import codezine.hello.parser.*;

public class Hello implements HelloParserVisitor{
    public static void main(String[] args){
        try {
            HelloParser parser = new HelloParser(System.in);
            Node node = parser.Hello();
            Hello visitor = new Hello();
            node.jjtAccept(visitor, null);
        } catch(TokenMgrError ex){
            System.out.println("字句解析エラー:" + ex.getMessage());
        } catch (ParseException ex) {
            System.out.println("構文解析エラー:" + ex.getMessage());
        }
    }

    public Object visit(SimpleNode node, Object data) {
        //ここには来ない
        return null;
    }

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

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

    public Object visit(ASTJJTree node, Object data) {
        return "イカスJJTree";
    }

    public Object visit(ASTName node, Object data) {
        String name = node.nodeValue;
        return "よくわからん" + name;
    }
}

 ビルドスクリプトと構築方法は前回と同じです。

 実行すると、「hello」に続けて「JavaCC」「JJTree」以外を入力した際に、「こんにちは よくわからんxxx」と表示されるようになります。

実行例
C:\java\product\CodeZine\build\classes>java codezine.hello.Hello
hello JavaScript
こんにちは よくわからんJavaScript!!

会員登録無料すると、続きをお読みいただけます

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

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

メールバックナンバー

次のページ
文字列定義

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

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

もっと読む

この記事の著者

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

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

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

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

この記事をシェア

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

おすすめ

アクセスランキング

アクセスランキング

イベント

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

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

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

メールバックナンバー

アクセスランキング

アクセスランキング