A1
Нет, возврат не требуется (пока). Но если вам нужен некоторый возврат, рекомендуется не использовать backtrack=true
сразу, а использовать предикат до правил, которые действительно требуют возврата. Используя backtrack=true
, вы включаете возврат по всем вашим правилам, в то время как, вероятно, только один или два требуют возврата. Но если ваш язык будет относительно небольшим, backtrack=true
проще, чем смешивать предикаты вручную, и, вероятно, не окажет большого влияния на производительность. Но если вы можете избежать их, сделайте это.
У вас есть пара правил синтаксического анализа, которые соответствуют пустым строкам, которые вызывают проблемы. Обычно лучше позволить правилам соответствовать чему-либо и сделать правило необязательным. Так что вместо:
foo : bar baz ;
bar : 'bar' ;
baz : 'baz' | /* epsilon */ ;
сделать
foo : bar baz? ;
bar : 'bar' ;
baz : 'baz' ;
вместо.
Кроме того, в случае зарезервированных ключевых слов, таких как true
, false
и т. Д., Не смешивайте их в правилах вашего синтаксического анализатора: всегда явно определяйте их в начале ваших правил лексера. Правила Lexer сопоставляются, начиная сверху вниз, поэтому их безопаснее всего определить (по крайней мере) перед правилами, подобными ID
, которые также могут соответствовать им. Я обычно ставлю их как первые правила лексера.
A2
Вы могли бы сделать это, передав параметры в правила вашего синтаксического анализатора, хотя это делает вашу грамматику (немного) менее читабельной.
Ваша грамматика с моими комментариями:
grammar test;
options {
output=AST;
}
tokens {
ARRAY_EXPR;
}
start : expression;
expression : andExp;
andExp : cmpExp (And^ cmpExp)*;
cmpExp : sumExp (LessThan^ sumExp)*;
sumExp : prodExp ((Plus | Minus)^ prodExp)*;
prodExp : unaryExp (Times^ unaryExp)*;
unaryExp : '-' primaryExp
| '!' primaryExp // negation is really a `unaryExp`
| primaryExp
;
primaryExp : INT primaryPrime[null]?
| 'true' primaryPrime[null]?
| 'false' primaryPrime[null]?
| 'this' primaryPrime[null]?
| (ID -> ID) (primaryPrime[new CommonTree($ID)] -> primaryPrime)?
| '('! expression ')'! primaryPrime[null]?
;
// removed the matching of 'epsilon'
primaryPrime [CommonTree parent]
: '[' expression ']' primaryPrime[null]? -> ^(ARRAY_EXPR {parent} expression primaryPrime?)
| '.' ID '(' exprList? ')' primaryPrime[null]?
| '.length' primaryPrime[null]?
| 'new' 'int' '[' expression ']' primaryPrime[null]?
| 'new' ID '(' ')' primaryPrime[null]?
;
// removed the matching of 'epsilon'
exprList : expression (',' expression)*;
// be sure to create explicit tokens for keywords!
True : 'true';
False : 'false';
This : 'this';
LessThan : '<';
Plus : '+';
Minus : '-';
Times : '*';
And : '&&';
Not : '!';
INT : '0' | ('1'..'9')('0'..'9')*;
ID : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*;
WS : ('\t' | ' ' | '\r' | '\n'| '\u000C') { $channel=HIDDEN; } ;
будет разбирать входные данные "array[2*3]"
на следующие значения AST:
Как вы можете видеть, запустив следующий тестовый класс:
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 = "array[2*3]";
testLexer lexer = new testLexer(new ANTLRStringStream(source));
CommonTokenStream tokens = new CommonTokenStream(lexer);
testParser parser = new testParser(tokens);
testParser.start_return returnValue = parser.start();
CommonTree tree = (CommonTree)returnValue.getTree();
DOTTreeGenerator gen = new DOTTreeGenerator();
StringTemplate st = gen.toDOT(tree);
System.out.println(st);
}
}