Как я уже упоминал в комментариях: вы злоупотребляете правилами лексера неправильно. Посмотрите на правила лексера как на основные строительные блоки вашего языка. Так же, как вы бы описали воду в химии. Вы бы не описали бы воду так:
WATER
: 'HHO'
;
Т.е .: как единый элемент. Вода должна быть описана как 3 отдельных элемента:
water
: Hydrogen Hydrogen Oxygen
;
Hydrogen : 'H';
Oxygen : 'O';
, где Hydrogen
и Oxygen
- фундаментальные строительные блоки (правила лексера), а water
- соединение (правило синтаксического анализатора).
Хорошее практическое правило заключается в том, что если вы создаете правила лексера, которые состоят из нескольких других правил лексера, есть вероятность, что в вашей грамматике есть что-то подозрительное. Конечно, это не всегда так.
Допустим, вы хотите проанализировать следующий ввод:
for i 1 3
print(i)
end
if fun(y, z) == 0
print('foo')
end
Грамматика может выглядеть следующим образом:
grammar T;
options {
output=AST;
}
tokens {
BLOCK;
CALL;
PARAMS;
}
// parser rules
parse
: block EOF!
;
block
: stat* -> ^(BLOCK stat*)
;
stat
: for_stat
| if_stat
| func_call
;
for_stat
: FOR^ ID expr expr block END!
;
if_stat
: IF^ expr block END!
;
expr
: eq_expr
;
eq_expr
: atom (('==' | '!=')^ atom)*
;
atom
: func_call
| INT
| ID
| STR
;
func_call
: ID '(' params ')' -> ^(CALL ID params)
;
params
: (expr (',' expr)*)? -> ^(PARAMS expr*)
;
// lexer rules
FOR : 'for';
END : 'end';
IF : 'if';
ID : ('a'..'z' | 'A'..'Z')+;
INT : '0'..'9'+;
STR : '\'' ~('\'')* '\'';
SP : (' ' | '\t' | '\r' | '\n')+ {skip();};
И если вы сейчас запустите этот тестовый класс:
import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;
import org.antlr.stringtemplate.*;
public class Main {
public static void main(String[] args) throws Exception {
String src =
"for i 1 3 \n" +
" print(i) \n" +
"end \n" +
" \n" +
"if fun(y, z) == 0 \n" +
" print('foo') \n" +
"end \n";
TLexer lexer = new TLexer(new ANTLRStringStream(src));
TParser parser = new TParser(new CommonTokenStream(lexer));
CommonTree tree = (CommonTree)parser.parse().getTree();
DOTTreeGenerator gen = new DOTTreeGenerator();
StringTemplate st = gen.toDOT(tree);
System.out.println(st);
}
}
вы увидите вывод вывода на консоль, соответствующий следующему AST: