Выражения в переводчике CoCo в ANTLR
Я разбираю грамматику CoCo/R в утилите для автоматизации перевода CoCo -> ANTLR. Основная грамматика ANTLR:
rule '=' expression '.' ;
expression
: term ('|' term)*
-> ^( OR_EXPR term term* )
;
term
: (factor (factor)*)? ;
factor
: symbol
| '(' expression ')'
-> ^( GROUPED_EXPR expression )
| '[' expression']'
-> ^( OPTIONAL_EXPR expression)
| '{' expression '}'
-> ^( SEQUENCE_EXPR expression)
;
symbol
: IF_ACTION
| ID (ATTRIBUTES)?
| STRINGLITERAL
;
Моя проблема с такими конструкциями:
CS = { ExternAliasDirective }
{ UsingDirective }
EOF .
CS приводит к AST с узлом OR_EXPR, хотя нет '|' персонаж действительно появляется. Я уверен, что это связано с определением выражения, но я не вижу другого способа написания правил.
Я экспериментировал с этим, чтобы устранить неоднозначность.
// explicitly test for the presence of an '|' character
expression
@init { bool ored = false; }
: term {ored = (input.LT(1).Type == OR); } (OR term)*
-> {ored}? ^(OR_EXPR term term*)
-> ^(LIST term term*)
Это работает, но взлом укрепляет мое убеждение, что что-то фундаментальное не так.
Любые советы очень ценятся.
2 ответа
Ваше правило:
expression
: term ('|' term)*
-> ^( OR_EXPR term term* )
;
всегда вызывает правило перезаписи для создания дерева с корнем типа OR_EXPR
, Вы можете создать "правила перезаписи" следующим образом:
expression
: (term -> REWRITE_RULE_X) ('|' term -> ^(REWRITE_RULE_Y))*
;
И чтобы устранить неоднозначность в вашей грамматике, проще всего включить глобальный возврат, что можно сделать в options { ... }
раздел вашей грамматики.
Небольшая демонстрация:
grammar CocoR;
options {
output=AST;
backtrack=true;
}
tokens {
RULE;
GROUP;
SEQUENCE;
OPTIONAL;
OR;
ATOMS;
}
parse
: rule EOF -> rule
;
rule
: ID '=' expr* '.' -> ^(RULE ID expr*)
;
expr
: (a=atoms -> $a) ('|' b=atoms -> ^(OR $expr $b))*
;
atoms
: atom+ -> ^(ATOMS atom+)
;
atom
: ID
| '(' expr ')' -> ^(GROUP expr)
| '{' expr '}' -> ^(SEQUENCE expr)
| '[' expr ']' -> ^(OPTIONAL expr)
;
ID
: ('a'..'z' | 'A'..'Z') ('a'..'z' | 'A'..'Z' | '0'..'9')*
;
Space
: (' ' | '\t' | '\r' | '\n') {skip();}
;
с входом:
CS = { ExternAliasDirective }
{ UsingDirective }
EOF .
производит АСТ:
и вход:
foo = a | b ({c} | d [e f]) .
производит:
Класс для проверки этого:
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 source =
"CS = { ExternAliasDirective } \n" +
"{ UsingDirective } \n" +
"EOF . ";
*/
String source = "foo = a | b ({c} | d [e f]) .";
ANTLRStringStream in = new ANTLRStringStream(source);
CocoRLexer lexer = new CocoRLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
CocoRParser parser = new CocoRParser(tokens);
CocoRParser.parse_return returnValue = parser.parse();
CommonTree tree = (CommonTree)returnValue.getTree();
DOTTreeGenerator gen = new DOTTreeGenerator();
StringTemplate st = gen.toDOT(tree);
System.out.println(st);
}
}
и с выводом, который производит этот класс, я использовал следующий веб-сайт для создания AST-изображений: http://graph.gafol.net/
НТН
РЕДАКТИРОВАТЬ
Для учета epsilon (пустая строка) в вашем OR
выражения, вы можете попробовать что-то (быстро проверено!), как это:
expr
: (a=atoms -> $a) ( ( '|' b=atoms -> ^(OR $expr $b)
| '|' -> ^(OR $expr NOTHING)
)
)*
;
который разбирает источник:
foo = a | b | .
в следующее АСТ:
Производство для expression
прямо говорит, что он может только вернуть OR_EXPR
узел. Вы можете попробовать что-то вроде:
expression
:
term
|
term ('|' term)+
-> ^( OR_EXPR term term* )
;
Далее вы можете использовать:
term
: factor*;