- 追加された行はこの色です。
- 削除された行はこの色です。
つれづれなるままにANTLRをやる。
もう、インストール済みなので、
立ち上げると,[Grammar Name:]を聞かれる。
あ、何するかきめてなかった。
なにか目標をたてよう。
そうだ、
入力:
y == x
出力
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のメソッドで解析した値を、定義したメソッドを介して得られる仕組みをとった。
これは、ちゃんと動作した。新たに一歩前進したといえるだろう。
独学は、確実に動作するサンプルをもとに、小さな課題を設定してクリアしていくことが近道なのだ。