ANTLRの実際にやってみる。
つれづれなるままにANTLRをやる。

もう、インストール済みなので、

立ち上げると,[Grammar Name:]を聞かれる。

あ、何するかきめてなかった。

なにか目標をたてよう。

そうだ、
入力:
y ==  x
 y ==  x

出力
strcmp(x,y)
 strcmp(x,y)

これていいや、文法名はLesson01とかでいいかな、

とおもったけど、ファイル名に数字があると駄目っぽい。とりあえずGGGとかいう、てきとーな名前にした。
動作確認したいだけなので、名前はどーでもいいのである。

Lexical Itemsでチェックボックスを入れることができる。
Identifierは、たぶん、識別子だから、イコールとか使えるようになるのかな?
変数は、Characterかな?、不要なスペースもキャンセルしたいから、White spaceにもチェックを入れておけばいいのかな。

とりあえず、仮説をたててやってみる。
とりあえず、仮説をたててやってみると、いいかもね。
すると、デフォルトでコードがいくつか作られている。

 ID  :	('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*
    ;
 
 WS  :   ( ' '
        | '\t'
        | '\r'
        | '\n'
        ) {$channel=HIDDEN;}
     ; 
 
 CHAR:  '\'' ( ESC_SEQ | ~('\''|'\\') ) '\''
    ;
 
 fragment
 HEX_DIGIT : ('0'..'9'|'a'..'f'|'A'..'F') ;
 
 fragment
 ESC_SEQ
    :   '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
    |   UNICODE_ESC
    |   OCTAL_ESC
    ;
 
 fragment
 OCTAL_ESC
    :   '\\' ('0'..'3') ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7')
    ;
 
 fragment
 UNICODE_ESC
    :   '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
    ;

たしかに、初めて利用する場合、このように、よくある選択肢を選ぶようになっているのは、ありがたい。

とりあえず、デバックボタンを押してみる。

ありゃ、エラー

 [10:46:05] java.lang.NullPointerException
	at org.antlr.xjlib.foundation.XJUtils.concatPath(XJUtils.java:43)
	at org.antlr.xjlib.foundation.XJUtils.concatPath(XJUtils.java:54)

対策がネットに載っていた、コンパイラーが通っていないかららしい。
You can fix this by going to File > Preferences > Compiler and entering the path for the executable.

って書いてあったが、Macの場合は[ANTLRWorks]-[環境設定]-[Preferrences]
でたどれた。

コンソールにて

 which java

で、どこにインストールされているか調べてみた。

 /usr/bin/java

ってでてきた。
それを入れれば、動いたけれども、なんだかうまく動かない。

 antlr-3.2.jar
をクラスパスに指定したりしてみたりしたら動いた。

とにかく、IDの書き方はいままで、

 ID      :	'a'..'z'+;

しかしらんかったのだけど
  ID  :	('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*
     ;
でもいけるということがわかった。

空白文字の書き方も
 WS	:	(' '|'\n'|'\r')+  {$channel=HIDDEN;};
しか知らなかったけど
 WS  :   ( ' '
        | '\t'
        | '\r'
        | '\n'
        ) {$channel=HIDDEN;}
     ; 

と書けることもわかった。こっちの方が見やすい。

1改善づつできることを、地道に増やしていくのが、近道かもね。

IDって、複数回マッチした場合どうなるんだろう?

もいちど、antlrの本家に立ち戻って、Getting Startを見てみる。

コメントをつかってLexerとPaserを区分けしている。

うむ、見やすい、採用。ということで、自分のコードは下記のようになった。

 grammar GGG;
 /*------------------------------------------------------------------
 * PARSER RULES
 *------------------------------------------------------------------*/
 r	:	'call' ID '==' ';' {System.out.println("invoke "+$ID.text);};
 
 /*------------------------------------------------------------------
 * LEXER RULES
 *------------------------------------------------------------------*/
 ID  :	('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*;
 WS  :   ( ' '
        | '\t'
        | '\r'
        | '\n'
        ) {$channel=HIDDEN;}     ;

パーサ部分を下記のようにして実行してみた。
 /*------------------------------------------------------------------
 * PARSER RULES
 *------------------------------------------------------------------*/
 r	:	ID '==' expr ';' {System.out.println("strcmp("+$ID.text+",");};
 expr    :	 ID{System.out.println($ID.text+")");};

結果
 call hoge==fuga;
という入力に対して
 fuga)
 strcmp(hoge,
という出力が得られた。

あと一歩だ、トークンを解析した結果の文字列を保存できれば、なんとかなるかもしれない。

この問題を解決しそうなチュートリアルをさがすと、下記のURLのチュートリアルが一番近そうだ。

http://www.antlr.org/wiki/display/ANTLR3/Simple+tree-based+interpeter

javaのコードを入れることが出来るようだ。

インポート文を入れれるし、メソッドも普通に書ける。

とりあえず、動作確認できたコードは下記のとおりである。

 @header {
    import java.util.Map;
    import java.util.HashMap;
    import java.math.BigInteger;
 }
 
 @members {
    public String getString(String name) {
        return name;
    }
 }
 
 
 /*------------------------------------------------------------------
 * PARSER RULES
 *------------------------------------------------------------------*/
 r	:	ID '==' expr ';' {System.out.println("strcmp("+$ID.text+","+$expr.value+")");};
 expr returns [String value]  :	 ID{$value = getString($ID.text);};
 
 /*------------------------------------------------------------------
 * LEXER RULES
 *------------------------------------------------------------------*/
 ID  :	('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*;
 WS  :   ( ' '
        | '\t'
        | '\r'
        | '\n'
        ) {$channel=HIDDEN;}     ; 

末端で解析した結果は、上位のノードに結果を返すようにしている点が、改良点だ。

あと、実験的にJavaのメソッドで解析した値を、定義したメソッドを介して得られる仕組みをとった。

これは、ちゃんと動作した。新たに一歩前進したといえるだろう。

独学は、確実に動作するサンプルをもとに、小さな課題を設定してクリアしていくことが近道なのだ。

トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS