Lexerとかの挙動

僭越ながら ASTは、タダじゃ作ってくれない。 - 設計と実装の狭間で。 を添削。

トーカナイズ

まず、↓の評価について。

charactors : (CHAR)+;
...
CHAR : ('0'..'9'|'a'..'z'|'A'..'Z') ;

これをやると、"hoge"っていう文字列に対して、"h", "o", "g", "e" って言うトークンを作った後に charactersという構文ルールで纏め上げてしまう。

ということで、

charactors : IDENT;
...
IDENT    : CHAR+;
fragment
CHAR	: ('0'..'9'|'a'..'z'|'A'..'Z') ;

こんなかんじ。こうすると、IDENTというlexerルールで"hoge"というトークンを作る。
fragmentはlexerルールの前につけると、トークンを作らずにマクロっぽい動きをするという指示子。

正規表現の罠

これだけで終わりにしてしまうと、なぜかIFとかが出現しなくなってしまう。
これは、Lexer特有の優先度付けのせいで、

  1. 最長一致 (文字数の多いトークンを優先)
  2. 早い者勝ち (文字数が同じならば、先に宣言したものを優先)

というなぞの2個目のルールがあるため。

なので、IDENTが

BEGIN : WS* 'BEGIN' WS*;
IF : WS* 'IF' WS*;
ELSE : WS* 'ELSE' WS*;
END : WS* 'END' WS*;

の、BEGINやらIFやらを食ってしまう。

これは単純に、IDENTをこれらより後ろで宣言すればいい。
つまり、

BEGIN	: WS* 'BEGIN' WS*;
IF	: WS* 'IF' WS*;
ELSE 	: WS* 'ELSE' WS*;
END	: WS* 'END' WS*;

IDENT    : CHAR+;

なかんじ。


WSをparserルール上に出現させない方法などは次回。