JavaCCを使ってみた。
Ref: JavaCC Home
Ref: JavaCC Grammar Repository
準備
javacc-3.2.tar.gz をダウンロードし、展開する。できあがったディレクトリにあるbinディレクトリにパスを通す。
手順
1.文法を定義した jjファイルを作成 … hello.jj
2.JavaCCでparserを生成 … javacc Hello.jj
3.javacコマンドでコンパイル … javac Hello.java
4.実行 … java Hello
起動クラスを別に作成する場合は、2, 3の間に起動用クラス作成を行い、3では起動クラスをコンパイルする。
まずはとっても簡単なHelloクラスを作ってみる。これは、helloという文字列を受け付けて処理を行うプログラムにする。それ以外の文字列を入力するとエラーを発生する。hello.jjのコードは次のようになる。
** Hello
**/
PARSER_BEGIN(Hello)
public class Hello {
public static void main(String args[]) throws ParseException {
Hello parser = new Hello(System.in);
parser.Start();
}
}
PARSER_END(Hello)
SKIP :
{
” “|”\r”|”\n”
}
TOKEN :
{
< HELLO: “hello” >
}
void Start() :
{}
{
hello() { System.out.println(“…done”); }
}
void hello() :
{}
{
}
PARSER_BEGIN(Hello)からPARSER_END(Hello)のところにmainメソッドを持つHelloクラスの処理を記述する。ここでは、標準入力からデータを読み込むHelloクラスのオブジェクトを作っている。この記述により、次のようなクラスが生成される。
public class Hello implements HelloConstants {
public static void main(String args[]) throws ParseException {
Hello parser = new Hello(System.in);
parser.Start();
}
/* 以下略 */
}
SKIPには区切り文字となる文字を指定する。この区切り文字は読み込みが破棄される。ここでは、半角空白と復帰と行送りを指定している。|という記号を使って ” “か”\r”か”\n”のどれかと一致するものは区切り文字だと指定している。
TOKENではトークンとなる文字列に対してプログラム内で利用できるように名前をつけている。ここでは、”hello”という文字列を受け付けたいので、これに対してHELLOという名前をつけてプログラム中では文字列”hello”の代わりにHELLOを使うようにしている。
次の部分は、Helloクラスが持つメソッドを定義している。Startメソッドではhelloメソッドの処理をしてから、自分の処理(ここでは System.out.println(“…done”)の処理)を行うと記述されている。helloメソッドでは、<HELLO>というトークンであったら、指定された処理(ここではSystem.out.println(“Hello”)の処理)を行うと記述されている。
{}
{
hello() { System.out.println(“…done”); }
}
void hello() :
{}
{
<HELLO> { System.out.println(“Hello”); }
}
言葉で説明されるとわかりにくいが、どういうコードが生成されるかを見るとわかるだろう。 jj_consume_tokenメソッドは自動的に生成されるメソッドで、読み込んだ文字列がトークンHELLO(ここでは”hello”)と一致しているかをチェックするものだ。もし一致しなかったら、このメソッドがParseExceptionをthrowするので、System.out.println(“Hello”)という処理は実行されない。一致すれば、System.out.println(“Hello”)は実行される。
hello();
System.out.println(“…done”);
}
static final public void hello() throws ParseException {
jj_consume_token(HELLO);
System.out.println(“Hello”);
}
実行までの具体的な手順は次のようになる。プログラムを実行した後に、helloと入力すると文字列を受け付けてhello.jjで記述した処理が実行される。しかし、hello以外の文字列を入力すると、プログラムは文字列解析中に、受付できない文字列が入力されたこと認識して例外を発生する。これらのことを確認をしてみよう。
Java Compiler Compiler Version 3.2 (Parser Generator)
(type “javacc” with no arguments for help)
Reading from file hello.jj . . .
File “TokenMgrError.java” does not exist. Will create one.
File “ParseException.java” does not exist. Will create one.
File “Token.java” does not exist. Will create one.
File “SimpleCharStream.java” does not exist. Will create one.
Parser generated successfully.
> javac Hello.java
> java -cp . Hello
hello
Hello
…done
> java -cp . Hello
bye
Exception in thread “main” TokenMgrError: Lexical error at line 1, column 1. En
countered: “b” (98), after : “”
at HelloTokenManager.getNextToken(HelloTokenManager.java:225)
at Hello.jj_consume_token(Hello.java:109)
at Hello.hello(Hello.java:14)
at Hello.Start(Hello.java:9)
at Hello.main(Hello.java:5)
ここでは非常に簡単な例を示したが、パーザに馴染みのない人にはまだ何をしているのかよくわからないかもしれない。馴染みのある人は何となくJavaCCの使い方の感じがつかめたのではないだろうか。