forked from jasder/antlr
rm isGreedy from DecisionState, but allow ATN construction for lexer to be nongreedy. error if '.' in parser. rm unit tests for parser nongreedy
This commit is contained in:
parent
d3d5bebf9f
commit
e78ecd418a
|
@ -250,11 +250,9 @@ public abstract class ATNSimulator {
|
||||||
int ndecisions = toInt(data[p++]);
|
int ndecisions = toInt(data[p++]);
|
||||||
for (int i=1; i<=ndecisions; i++) {
|
for (int i=1; i<=ndecisions; i++) {
|
||||||
int s = toInt(data[p++]);
|
int s = toInt(data[p++]);
|
||||||
int isGreedy = toInt(data[p++]);
|
|
||||||
DecisionState decState = (DecisionState)atn.states.get(s);
|
DecisionState decState = (DecisionState)atn.states.get(s);
|
||||||
atn.decisionToState.add(decState);
|
atn.decisionToState.add(decState);
|
||||||
decState.decision = i-1;
|
decState.decision = i-1;
|
||||||
decState.isGreedy = isGreedy==1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
verifyATN(atn);
|
verifyATN(atn);
|
||||||
|
|
|
@ -31,6 +31,4 @@ package org.antlr.v4.runtime.atn;
|
||||||
|
|
||||||
public class DecisionState extends ATNState {
|
public class DecisionState extends ATNState {
|
||||||
public int decision = -1;
|
public int decision = -1;
|
||||||
|
|
||||||
public boolean isGreedy = true;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -384,13 +384,12 @@ public class ParserATNSimulator<Symbol extends Token> extends ATNSimulator {
|
||||||
", outerContext="+outerContext.toString(parser));
|
", outerContext="+outerContext.toString(parser));
|
||||||
}
|
}
|
||||||
DecisionState decState = atn.getDecisionState(dfa.decision);
|
DecisionState decState = atn.getDecisionState(dfa.decision);
|
||||||
boolean greedy = decState.isGreedy;
|
|
||||||
boolean loopsSimulateTailRecursion = SLL_loopsSimulateTailRecursion;
|
boolean loopsSimulateTailRecursion = SLL_loopsSimulateTailRecursion;
|
||||||
boolean fullCtx = false;
|
boolean fullCtx = false;
|
||||||
ATNConfigSet s0_closure =
|
ATNConfigSet s0_closure =
|
||||||
computeStartState(dfa.atnStartState,
|
computeStartState(dfa.atnStartState,
|
||||||
ParserRuleContext.EMPTY,
|
ParserRuleContext.EMPTY,
|
||||||
greedy, loopsSimulateTailRecursion,
|
loopsSimulateTailRecursion,
|
||||||
fullCtx);
|
fullCtx);
|
||||||
dfa.s0 = addDFAState(dfa, new DFAState(s0_closure));
|
dfa.s0 = addDFAState(dfa, new DFAState(s0_closure));
|
||||||
|
|
||||||
|
@ -428,7 +427,6 @@ public class ParserATNSimulator<Symbol extends Token> extends ATNSimulator {
|
||||||
DFAState s = s0;
|
DFAState s = s0;
|
||||||
|
|
||||||
DecisionState decState = atn.getDecisionState(dfa.decision);
|
DecisionState decState = atn.getDecisionState(dfa.decision);
|
||||||
boolean greedy = decState.isGreedy;
|
|
||||||
|
|
||||||
int t = input.LA(1);
|
int t = input.LA(1);
|
||||||
loop:
|
loop:
|
||||||
|
@ -440,14 +438,13 @@ public class ParserATNSimulator<Symbol extends Token> extends ATNSimulator {
|
||||||
boolean fullCtx = true;
|
boolean fullCtx = true;
|
||||||
ATNConfigSet s0_closure =
|
ATNConfigSet s0_closure =
|
||||||
computeStartState(dfa.atnStartState, outerContext,
|
computeStartState(dfa.atnStartState, outerContext,
|
||||||
greedy, loopsSimulateTailRecursion,
|
loopsSimulateTailRecursion,
|
||||||
fullCtx);
|
fullCtx);
|
||||||
retry_with_context_from_dfa++;
|
retry_with_context_from_dfa++;
|
||||||
int alt = execATNWithFullContext(dfa, s, s0_closure,
|
int alt = execATNWithFullContext(dfa, s, s0_closure,
|
||||||
input, startIndex,
|
input, startIndex,
|
||||||
outerContext,
|
outerContext,
|
||||||
ATN.INVALID_ALT_NUMBER,
|
ATN.INVALID_ALT_NUMBER);
|
||||||
greedy);
|
|
||||||
return alt;
|
return alt;
|
||||||
}
|
}
|
||||||
if ( s.isAcceptState ) {
|
if ( s.isAcceptState ) {
|
||||||
|
@ -598,13 +595,11 @@ public class ParserATNSimulator<Symbol extends Token> extends ATNSimulator {
|
||||||
int t = input.LA(1);
|
int t = input.LA(1);
|
||||||
|
|
||||||
DecisionState decState = atn.getDecisionState(dfa.decision);
|
DecisionState decState = atn.getDecisionState(dfa.decision);
|
||||||
boolean greedy = decState.isGreedy;
|
|
||||||
|
|
||||||
while (true) { // while more work
|
while (true) { // while more work
|
||||||
boolean loopsSimulateTailRecursion = SLL_loopsSimulateTailRecursion;
|
boolean loopsSimulateTailRecursion = SLL_loopsSimulateTailRecursion;
|
||||||
// System.out.println("REACH "+getLookaheadName(input));
|
// System.out.println("REACH "+getLookaheadName(input));
|
||||||
ATNConfigSet reach = computeReachSet(previous, t,
|
ATNConfigSet reach = computeReachSet(previous, t,
|
||||||
greedy,
|
|
||||||
loopsSimulateTailRecursion,
|
loopsSimulateTailRecursion,
|
||||||
false);
|
false);
|
||||||
if ( reach==null ) {
|
if ( reach==null ) {
|
||||||
|
@ -658,7 +653,6 @@ public class ParserATNSimulator<Symbol extends Token> extends ATNSimulator {
|
||||||
// boolean cont = needMoreLookaheadLL(reach);
|
// boolean cont = needMoreLookaheadLL(reach);
|
||||||
D.configs.conflictingAlts = getConflictingAlts(reach);
|
D.configs.conflictingAlts = getConflictingAlts(reach);
|
||||||
if ( D.configs.conflictingAlts!=null ) {
|
if ( D.configs.conflictingAlts!=null ) {
|
||||||
if ( greedy ) {
|
|
||||||
// CONFLICT, GREEDY (TYPICAL SITUATION)
|
// CONFLICT, GREEDY (TYPICAL SITUATION)
|
||||||
if ( outerContext == ParserRuleContext.EMPTY || // in grammar start rule
|
if ( outerContext == ParserRuleContext.EMPTY || // in grammar start rule
|
||||||
!D.configs.dipsIntoOuterContext || // didn't fall out of rule
|
!D.configs.dipsIntoOuterContext || // didn't fall out of rule
|
||||||
|
@ -686,14 +680,12 @@ public class ParserATNSimulator<Symbol extends Token> extends ATNSimulator {
|
||||||
ATNConfigSet s0_closure =
|
ATNConfigSet s0_closure =
|
||||||
computeStartState(dfa.atnStartState,
|
computeStartState(dfa.atnStartState,
|
||||||
outerContext,
|
outerContext,
|
||||||
greedy,
|
|
||||||
loopsSimulateTailRecursion,
|
loopsSimulateTailRecursion,
|
||||||
true);
|
true);
|
||||||
predictedAlt = execATNWithFullContext(dfa, D, s0_closure,
|
predictedAlt = execATNWithFullContext(dfa, D, s0_closure,
|
||||||
input, startIndex,
|
input, startIndex,
|
||||||
outerContext,
|
outerContext,
|
||||||
D.configs.conflictingAlts.getMinElement(),
|
D.configs.conflictingAlts.getMinElement());
|
||||||
greedy);
|
|
||||||
// not accept state: isCtxSensitive
|
// not accept state: isCtxSensitive
|
||||||
D.requiresFullContext = true; // always force DFA to ATN simulate
|
D.requiresFullContext = true; // always force DFA to ATN simulate
|
||||||
D.prediction = ATN.INVALID_ALT_NUMBER;
|
D.prediction = ATN.INVALID_ALT_NUMBER;
|
||||||
|
@ -701,34 +693,6 @@ public class ParserATNSimulator<Symbol extends Token> extends ATNSimulator {
|
||||||
return predictedAlt; // all done with preds, etc...
|
return predictedAlt; // all done with preds, etc...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
// CONFLICT, NONGREEDY (ATYPICAL SITUATION)
|
|
||||||
// upon ambiguity for nongreedy, default to exit branch to avoid inf loop
|
|
||||||
// this handles case where we find ambiguity that stops DFA construction
|
|
||||||
// before a config hits rule stop state. Was leaving prediction blank.
|
|
||||||
int exitAlt = 2;
|
|
||||||
// when ambig or ctx sens or nongreedy or .* loop hitting rule stop
|
|
||||||
D.isAcceptState = true;
|
|
||||||
D.prediction = predictedAlt = exitAlt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !greedy ) {
|
|
||||||
int exitAlt = 2;
|
|
||||||
if ( predictedAlt != ATN.INVALID_ALT_NUMBER && configWithAltAtStopState(reach, 1) ) {
|
|
||||||
if ( debug ) System.out.println("nongreedy loop but unique alt "+D.configs.uniqueAlt+" at "+reach);
|
|
||||||
// reaches end via .* means nothing after.
|
|
||||||
D.isAcceptState = true;
|
|
||||||
D.prediction = predictedAlt = exitAlt;
|
|
||||||
}
|
|
||||||
else {// if we reached end of rule via exit branch and decision nongreedy, we matched
|
|
||||||
if ( configWithAltAtStopState(reach, exitAlt) ) {
|
|
||||||
if ( debug ) System.out.println("nongreedy at stop state for exit branch");
|
|
||||||
D.isAcceptState = true;
|
|
||||||
D.prediction = predictedAlt = exitAlt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( D.isAcceptState && D.configs.hasSemanticContext ) {
|
if ( D.isAcceptState && D.configs.hasSemanticContext ) {
|
||||||
|
@ -789,15 +753,14 @@ public class ParserATNSimulator<Symbol extends Token> extends ATNSimulator {
|
||||||
@NotNull ATNConfigSet s0,
|
@NotNull ATNConfigSet s0,
|
||||||
@NotNull TokenStream input, int startIndex,
|
@NotNull TokenStream input, int startIndex,
|
||||||
ParserRuleContext<?> outerContext,
|
ParserRuleContext<?> outerContext,
|
||||||
int SLL_min_alt, // todo: is this in D as min ambig alts?
|
int SLL_min_alt) // todo: is this in D as min ambig alts?
|
||||||
boolean greedy)
|
|
||||||
{
|
{
|
||||||
// caller must have write lock on dfa
|
// caller must have write lock on dfa
|
||||||
retry_with_context++;
|
retry_with_context++;
|
||||||
reportAttemptingFullContext(dfa, s0, startIndex, input.index());
|
reportAttemptingFullContext(dfa, s0, startIndex, input.index());
|
||||||
|
|
||||||
if ( debug || debug_list_atn_decisions ) {
|
if ( debug || debug_list_atn_decisions ) {
|
||||||
System.out.println("execATNWithFullContext "+s0+", greedy="+greedy);
|
System.out.println("execATNWithFullContext "+s0);
|
||||||
}
|
}
|
||||||
boolean fullCtx = true;
|
boolean fullCtx = true;
|
||||||
ATNConfigSet reach = null;
|
ATNConfigSet reach = null;
|
||||||
|
@ -808,7 +771,7 @@ public class ParserATNSimulator<Symbol extends Token> extends ATNSimulator {
|
||||||
// System.out.println("LL REACH "+getLookaheadName(input)+
|
// System.out.println("LL REACH "+getLookaheadName(input)+
|
||||||
// " from configs.size="+previous.size()+
|
// " from configs.size="+previous.size()+
|
||||||
// " line "+input.LT(1).getLine()+":"+input.LT(1).getCharPositionInLine());
|
// " line "+input.LT(1).getLine()+":"+input.LT(1).getCharPositionInLine());
|
||||||
reach = computeReachSet(previous, t, greedy, true, fullCtx);
|
reach = computeReachSet(previous, t, true, fullCtx);
|
||||||
if ( reach==null ) {
|
if ( reach==null ) {
|
||||||
// if any configs in previous dipped into outer context, that
|
// if any configs in previous dipped into outer context, that
|
||||||
// means that input up to t actually finished entry rule
|
// means that input up to t actually finished entry rule
|
||||||
|
@ -854,7 +817,6 @@ public class ParserATNSimulator<Symbol extends Token> extends ATNSimulator {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ATNConfigSet computeReachSet(ATNConfigSet closure, int t,
|
protected ATNConfigSet computeReachSet(ATNConfigSet closure, int t,
|
||||||
boolean greedy,
|
|
||||||
boolean loopsSimulateTailRecursion,
|
boolean loopsSimulateTailRecursion,
|
||||||
boolean fullCtx)
|
boolean fullCtx)
|
||||||
{
|
{
|
||||||
|
@ -890,7 +852,7 @@ public class ParserATNSimulator<Symbol extends Token> extends ATNSimulator {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (ATNConfig c : intermediate) {
|
for (ATNConfig c : intermediate) {
|
||||||
closure(c, reach, closureBusy, false, greedy,
|
closure(c, reach, closureBusy, false,
|
||||||
loopsSimulateTailRecursion, fullCtx);
|
loopsSimulateTailRecursion, fullCtx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -902,7 +864,6 @@ public class ParserATNSimulator<Symbol extends Token> extends ATNSimulator {
|
||||||
@NotNull
|
@NotNull
|
||||||
public ATNConfigSet computeStartState(@NotNull ATNState p,
|
public ATNConfigSet computeStartState(@NotNull ATNState p,
|
||||||
@Nullable RuleContext ctx,
|
@Nullable RuleContext ctx,
|
||||||
boolean greedy,
|
|
||||||
boolean loopsSimulateTailRecursion,
|
boolean loopsSimulateTailRecursion,
|
||||||
boolean fullCtx)
|
boolean fullCtx)
|
||||||
{
|
{
|
||||||
|
@ -914,7 +875,7 @@ public class ParserATNSimulator<Symbol extends Token> extends ATNSimulator {
|
||||||
ATNState target = p.transition(i).target;
|
ATNState target = p.transition(i).target;
|
||||||
ATNConfig c = new ATNConfig(target, i+1, initialContext);
|
ATNConfig c = new ATNConfig(target, i+1, initialContext);
|
||||||
Set<ATNConfig> closureBusy = new HashSet<ATNConfig>();
|
Set<ATNConfig> closureBusy = new HashSet<ATNConfig>();
|
||||||
closure(c, configs, closureBusy, true, greedy,
|
closure(c, configs, closureBusy, true,
|
||||||
loopsSimulateTailRecursion, fullCtx);
|
loopsSimulateTailRecursion, fullCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1091,12 +1052,11 @@ public class ParserATNSimulator<Symbol extends Token> extends ATNSimulator {
|
||||||
@NotNull ATNConfigSet configs,
|
@NotNull ATNConfigSet configs,
|
||||||
@NotNull Set<ATNConfig> closureBusy,
|
@NotNull Set<ATNConfig> closureBusy,
|
||||||
boolean collectPredicates,
|
boolean collectPredicates,
|
||||||
boolean greedy,
|
|
||||||
boolean loopsSimulateTailRecursion,
|
boolean loopsSimulateTailRecursion,
|
||||||
boolean fullCtx)
|
boolean fullCtx)
|
||||||
{
|
{
|
||||||
final int initialDepth = 0;
|
final int initialDepth = 0;
|
||||||
closureCheckingStopStateAndLoopRecursion(config, configs, closureBusy, collectPredicates, greedy,
|
closureCheckingStopStateAndLoopRecursion(config, configs, closureBusy, collectPredicates,
|
||||||
loopsSimulateTailRecursion,
|
loopsSimulateTailRecursion,
|
||||||
fullCtx,
|
fullCtx,
|
||||||
initialDepth);
|
initialDepth);
|
||||||
|
@ -1106,7 +1066,6 @@ public class ParserATNSimulator<Symbol extends Token> extends ATNSimulator {
|
||||||
@NotNull ATNConfigSet configs,
|
@NotNull ATNConfigSet configs,
|
||||||
@NotNull Set<ATNConfig> closureBusy,
|
@NotNull Set<ATNConfig> closureBusy,
|
||||||
boolean collectPredicates,
|
boolean collectPredicates,
|
||||||
boolean greedy,
|
|
||||||
boolean loopsSimulateTailRecursion,
|
boolean loopsSimulateTailRecursion,
|
||||||
boolean fullCtx,
|
boolean fullCtx,
|
||||||
int depth)
|
int depth)
|
||||||
|
@ -1116,13 +1075,6 @@ public class ParserATNSimulator<Symbol extends Token> extends ATNSimulator {
|
||||||
if ( !closureBusy.add(config) ) return; // avoid infinite recursion
|
if ( !closureBusy.add(config) ) return; // avoid infinite recursion
|
||||||
|
|
||||||
if ( config.state instanceof RuleStopState ) {
|
if ( config.state instanceof RuleStopState ) {
|
||||||
if ( !greedy ) {
|
|
||||||
// don't see past end of a rule for any nongreedy decision
|
|
||||||
if ( debug ) System.out.println("NONGREEDY at stop state of "+
|
|
||||||
getRuleName(config.state.ruleIndex));
|
|
||||||
configs.add(config, mergeCache);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// We hit rule end. If we have context info, use it
|
// We hit rule end. If we have context info, use it
|
||||||
// run thru all possible stack tops in ctx
|
// run thru all possible stack tops in ctx
|
||||||
if ( config.context!=null && !config.context.isEmpty() ) {
|
if ( config.context!=null && !config.context.isEmpty() ) {
|
||||||
|
@ -1131,7 +1083,7 @@ public class ParserATNSimulator<Symbol extends Token> extends ATNSimulator {
|
||||||
// we have no context info, just chase follow links (if greedy)
|
// we have no context info, just chase follow links (if greedy)
|
||||||
if ( debug ) System.out.println("FALLING off rule "+
|
if ( debug ) System.out.println("FALLING off rule "+
|
||||||
getRuleName(config.state.ruleIndex));
|
getRuleName(config.state.ruleIndex));
|
||||||
closure_(config, configs, closureBusy, collectPredicates, greedy,
|
closure_(config, configs, closureBusy, collectPredicates,
|
||||||
loopsSimulateTailRecursion, fullCtx, depth);
|
loopsSimulateTailRecursion, fullCtx, depth);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1146,7 +1098,7 @@ public class ParserATNSimulator<Symbol extends Token> extends ATNSimulator {
|
||||||
// Make sure we track that we are now out of context.
|
// Make sure we track that we are now out of context.
|
||||||
c.reachesIntoOuterContext = config.reachesIntoOuterContext;
|
c.reachesIntoOuterContext = config.reachesIntoOuterContext;
|
||||||
assert depth > Integer.MIN_VALUE;
|
assert depth > Integer.MIN_VALUE;
|
||||||
closureCheckingStopStateAndLoopRecursion(c, configs, closureBusy, collectPredicates, greedy,
|
closureCheckingStopStateAndLoopRecursion(c, configs, closureBusy, collectPredicates,
|
||||||
loopsSimulateTailRecursion,
|
loopsSimulateTailRecursion,
|
||||||
fullCtx, depth - 1);
|
fullCtx, depth - 1);
|
||||||
}
|
}
|
||||||
|
@ -1179,7 +1131,7 @@ public class ParserATNSimulator<Symbol extends Token> extends ATNSimulator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
closure_(config, configs, closureBusy, collectPredicates, greedy,
|
closure_(config, configs, closureBusy, collectPredicates,
|
||||||
loopsSimulateTailRecursion, fullCtx, depth);
|
loopsSimulateTailRecursion, fullCtx, depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1188,7 +1140,6 @@ public class ParserATNSimulator<Symbol extends Token> extends ATNSimulator {
|
||||||
@NotNull ATNConfigSet configs,
|
@NotNull ATNConfigSet configs,
|
||||||
@NotNull Set<ATNConfig> closureBusy,
|
@NotNull Set<ATNConfig> closureBusy,
|
||||||
boolean collectPredicates,
|
boolean collectPredicates,
|
||||||
boolean greedy,
|
|
||||||
boolean loopsSimulateTailRecursion,
|
boolean loopsSimulateTailRecursion,
|
||||||
boolean fullCtx,
|
boolean fullCtx,
|
||||||
int depth)
|
int depth)
|
||||||
|
@ -1233,7 +1184,7 @@ public class ParserATNSimulator<Symbol extends Token> extends ATNSimulator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
closureCheckingStopStateAndLoopRecursion(c, configs, closureBusy, continueCollecting, greedy,
|
closureCheckingStopStateAndLoopRecursion(c, configs, closureBusy, continueCollecting,
|
||||||
loopsSimulateTailRecursion,
|
loopsSimulateTailRecursion,
|
||||||
fullCtx, newDepth);
|
fullCtx, newDepth);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
grammar T;
|
grammar T;
|
||||||
|
s : ID ;
|
||||||
tokens {}
|
ID : 'a'..'z'+ ;
|
||||||
s : INT+ ;
|
WS : (' '|'\n') {skip();} ;
|
||||||
|
|
||||||
ID : [a-z]+ ;
|
|
||||||
INT : [0-9]+ ;
|
|
||||||
WS : [ \t\n]+ -> skip ;
|
|
||||||
|
|
|
@ -226,7 +226,6 @@ public class ATNSerializer {
|
||||||
data.add(ndecisions);
|
data.add(ndecisions);
|
||||||
for (DecisionState decStartState : atn.decisionToState) {
|
for (DecisionState decStartState : atn.decisionToState) {
|
||||||
data.add(decStartState.stateNumber);
|
data.add(decStartState.stateNumber);
|
||||||
data.add(decStartState.isGreedy?1:0);
|
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
@ -300,8 +299,7 @@ public class ATNSerializer {
|
||||||
int ndecisions = ATNSimulator.toInt(data[p++]);
|
int ndecisions = ATNSimulator.toInt(data[p++]);
|
||||||
for (int i=1; i<=ndecisions; i++) {
|
for (int i=1; i<=ndecisions; i++) {
|
||||||
int s = ATNSimulator.toInt(data[p++]);
|
int s = ATNSimulator.toInt(data[p++]);
|
||||||
int isGreedy = ATNSimulator.toInt(data[p++]);
|
buf.append(i-1).append(":").append(s).append("\n");
|
||||||
buf.append(i-1).append(":").append(s).append(" ").append(isGreedy).append("\n");
|
|
||||||
}
|
}
|
||||||
return buf.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -454,8 +454,7 @@ public class ParserATNFactory implements ATNFactory {
|
||||||
epsilon(blkEnd, loop); // blk can see loop back
|
epsilon(blkEnd, loop); // blk can see loop back
|
||||||
|
|
||||||
BlockAST blkAST = (BlockAST)plusAST.getChild(0);
|
BlockAST blkAST = (BlockAST)plusAST.getChild(0);
|
||||||
loop.isGreedy = isGreedy(blkAST);
|
if ( !g.isLexer() || isGreedy(blkAST) ) {
|
||||||
if ( !g.isLexer() || loop.isGreedy ) {
|
|
||||||
epsilon(loop, blkStart); // loop back to start
|
epsilon(loop, blkStart); // loop back to start
|
||||||
epsilon(loop, end); // or exit
|
epsilon(loop, end); // or exit
|
||||||
}
|
}
|
||||||
|
@ -494,8 +493,7 @@ public class ParserATNFactory implements ATNFactory {
|
||||||
end.loopBackState = loop;
|
end.loopBackState = loop;
|
||||||
|
|
||||||
BlockAST blkAST = (BlockAST)starAST.getChild(0);
|
BlockAST blkAST = (BlockAST)starAST.getChild(0);
|
||||||
entry.isGreedy = isGreedy(blkAST);
|
if ( !g.isLexer() || isGreedy(blkAST) ) {
|
||||||
if ( !g.isLexer() || entry.isGreedy ) {
|
|
||||||
epsilon(entry, blkStart); // loop enter edge (alt 1)
|
epsilon(entry, blkStart); // loop enter edge (alt 1)
|
||||||
epsilon(entry, end); // bypass loop edge (alt 2)
|
epsilon(entry, end); // bypass loop edge (alt 2)
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,7 +137,18 @@ public class BasicSemanticChecks extends GrammarTreeVisitor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void modeDef(GrammarAST m, GrammarAST ID) {
|
public void modeDef(GrammarAST m, GrammarAST ID) {
|
||||||
checkMode(ID.token);
|
if ( !g.isLexer() ) {
|
||||||
|
g.tool.errMgr.grammarError(ErrorType.MODE_NOT_IN_LEXER, g.fileName,
|
||||||
|
ID.token, ID.token.getText(), g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void wildcardRef(GrammarAST ref) {
|
||||||
|
if ( !g.isLexer() ) {
|
||||||
|
g.tool.errMgr.grammarError(ErrorType.WILDCARD_IN_PARSER, g.fileName,
|
||||||
|
ref.getToken(), ref.getText(), g);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -258,13 +269,6 @@ public class BasicSemanticChecks extends GrammarTreeVisitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkMode(Token modeNameToken) {
|
|
||||||
if ( !g.isLexer() ) {
|
|
||||||
g.tool.errMgr.grammarError(ErrorType.MODE_NOT_IN_LEXER, g.fileName,
|
|
||||||
modeNameToken, modeNameToken.getText(), g);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void checkNumPrequels(List<GrammarAST> options,
|
void checkNumPrequels(List<GrammarAST> options,
|
||||||
List<GrammarAST> imports,
|
List<GrammarAST> imports,
|
||||||
List<GrammarAST> tokens)
|
List<GrammarAST> tokens)
|
||||||
|
@ -334,7 +338,14 @@ public class BasicSemanticChecks extends GrammarTreeVisitor {
|
||||||
{
|
{
|
||||||
boolean ok = true;
|
boolean ok = true;
|
||||||
if ( parent.getType()==ANTLRParser.BLOCK ) {
|
if ( parent.getType()==ANTLRParser.BLOCK ) {
|
||||||
if ( !Grammar.subruleOptions.contains(optionID.getText()) ) { // block
|
if ( g.isLexer() && Grammar.LexerSubruleOptions.contains(optionID.getText()) ) { // block
|
||||||
|
g.tool.errMgr.grammarError(ErrorType.ILLEGAL_OPTION,
|
||||||
|
g.fileName,
|
||||||
|
optionID,
|
||||||
|
optionID.getText());
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
if ( !g.isLexer() && Grammar.ParserSubruleOptions.contains(optionID.getText()) ) { // block
|
||||||
g.tool.errMgr.grammarError(ErrorType.ILLEGAL_OPTION,
|
g.tool.errMgr.grammarError(ErrorType.ILLEGAL_OPTION,
|
||||||
g.fileName,
|
g.fileName,
|
||||||
optionID,
|
optionID,
|
||||||
|
|
|
@ -136,7 +136,7 @@ public enum ErrorType {
|
||||||
IMPORT_NAME_CLASH(113, "<arg.typeString> grammar <arg.name> and imported <arg2.typeString> grammar <arg2.name> both generate <arg2.recognizerName>", ErrorSeverity.ERROR),
|
IMPORT_NAME_CLASH(113, "<arg.typeString> grammar <arg.name> and imported <arg2.typeString> grammar <arg2.name> both generate <arg2.recognizerName>", ErrorSeverity.ERROR),
|
||||||
AST_OP_WITH_NON_AST_OUTPUT_OPTION(114, " <arg>", ErrorSeverity.ERROR),
|
AST_OP_WITH_NON_AST_OUTPUT_OPTION(114, " <arg>", ErrorSeverity.ERROR),
|
||||||
AST_OP_IN_ALT_WITH_REWRITE(115, "", ErrorSeverity.ERROR),
|
AST_OP_IN_ALT_WITH_REWRITE(115, "", ErrorSeverity.ERROR),
|
||||||
WILDCARD_AS_ROOT(116, "", ErrorSeverity.ERROR),
|
// WILDCARD_AS_ROOT(116, "", ErrorSeverity.ERROR),
|
||||||
CONFLICTING_OPTION_IN_TREE_FILTER(117, "", ErrorSeverity.ERROR),
|
CONFLICTING_OPTION_IN_TREE_FILTER(117, "", ErrorSeverity.ERROR),
|
||||||
ALL_OPS_NEED_SAME_ASSOC(118, "all operators of alt <arg> of left-recursive rule must have same associativity", ErrorSeverity.WARNING),
|
ALL_OPS_NEED_SAME_ASSOC(118, "all operators of alt <arg> of left-recursive rule must have same associativity", ErrorSeverity.WARNING),
|
||||||
LEFT_RECURSION_CYCLES(119, "The following sets of rules are mutually left-recursive <arg:{c| [<c:{r|<r.name>}; separator=\", \">]}; separator=\" and \">", ErrorSeverity.ERROR),
|
LEFT_RECURSION_CYCLES(119, "The following sets of rules are mutually left-recursive <arg:{c| [<c:{r|<r.name>}; separator=\", \">]}; separator=\" and \">", ErrorSeverity.ERROR),
|
||||||
|
@ -149,6 +149,7 @@ public enum ErrorType {
|
||||||
IMPLICIT_STRING_DEFINITION(126, "cannot create implicit token for string literal <arg> in non-combined grammar", ErrorSeverity.ERROR),
|
IMPLICIT_STRING_DEFINITION(126, "cannot create implicit token for string literal <arg> in non-combined grammar", ErrorSeverity.ERROR),
|
||||||
// ALIAS_REASSIGNMENT(127, "token literal <arg> aliased to new token name <arg2>", ErrorSeverity.WARNING),
|
// ALIAS_REASSIGNMENT(127, "token literal <arg> aliased to new token name <arg2>", ErrorSeverity.WARNING),
|
||||||
ATTRIBUTE_IN_LEXER_ACTION(128, "attribute references not allowed in lexer actions: $<arg>", ErrorSeverity.ERROR),
|
ATTRIBUTE_IN_LEXER_ACTION(128, "attribute references not allowed in lexer actions: $<arg>", ErrorSeverity.ERROR),
|
||||||
|
WILDCARD_IN_PARSER(129, "wildcard '.' not allowed in parsers", ErrorSeverity.ERROR),
|
||||||
|
|
||||||
/** Documentation comment is unterminated */
|
/** Documentation comment is unterminated */
|
||||||
//UNTERMINATED_DOC_COMMENT(, "", ErrorSeverity.ERROR),
|
//UNTERMINATED_DOC_COMMENT(, "", ErrorSeverity.ERROR),
|
||||||
|
|
|
@ -82,7 +82,10 @@ public class Grammar implements AttributeResolver {
|
||||||
public static final Set<String> ruleOptions = new HashSet<String>() {{
|
public static final Set<String> ruleOptions = new HashSet<String>() {{
|
||||||
}};
|
}};
|
||||||
|
|
||||||
public static final Set<String> subruleOptions = new HashSet<String>() {{
|
public static final Set<String> ParserSubruleOptions = new HashSet<String>() {{
|
||||||
|
}};
|
||||||
|
|
||||||
|
public static final Set<String> LexerSubruleOptions = new HashSet<String>() {{
|
||||||
add("greedy");
|
add("greedy");
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ public class TestASTStructure {
|
||||||
// gunit test on line 18
|
// gunit test on line 18
|
||||||
RuleReturnScope rstruct = (RuleReturnScope)execParser("grammarSpec", "\n parser grammar P;\n tokens { A, B }\n @header {foo}\n a : A;\n ", 18);
|
RuleReturnScope rstruct = (RuleReturnScope)execParser("grammarSpec", "\n parser grammar P;\n tokens { A, B }\n @header {foo}\n a : A;\n ", 18);
|
||||||
Object actual = ((Tree)rstruct.getTree()).toStringTree();
|
Object actual = ((Tree)rstruct.getTree()).toStringTree();
|
||||||
Object expecting = "(PARSER_GRAMMAR P (tokens { A (= B '33')) (@ header {foo}) (RULES (RULE a (BLOCK (ALT A)))))";
|
Object expecting = "(PARSER_GRAMMAR P (tokens { A B) (@ header {foo}) (RULES (RULE a (BLOCK (ALT A)))))";
|
||||||
assertEquals("testing rule grammarSpec", expecting, actual);
|
assertEquals("testing rule grammarSpec", expecting, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ public class TestASTStructure {
|
||||||
// gunit test on line 30
|
// gunit test on line 30
|
||||||
RuleReturnScope rstruct = (RuleReturnScope)execParser("grammarSpec", "\n parser grammar P;\n @header {foo}\n tokens { A,B }\n a : A;\n ", 30);
|
RuleReturnScope rstruct = (RuleReturnScope)execParser("grammarSpec", "\n parser grammar P;\n @header {foo}\n tokens { A,B }\n a : A;\n ", 30);
|
||||||
Object actual = ((Tree)rstruct.getTree()).toStringTree();
|
Object actual = ((Tree)rstruct.getTree()).toStringTree();
|
||||||
Object expecting = "(PARSER_GRAMMAR P (@ header {foo}) (tokens { A (= B '33')) (RULES (RULE a (BLOCK (ALT A)))))";
|
Object expecting = "(PARSER_GRAMMAR P (@ header {foo}) (tokens { A B) (RULES (RULE a (BLOCK (ALT A)))))";
|
||||||
assertEquals("testing rule grammarSpec", expecting, actual);
|
assertEquals("testing rule grammarSpec", expecting, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,9 +113,9 @@ public class TestASTStructure {
|
||||||
|
|
||||||
@Test public void test_rule3() throws Exception {
|
@Test public void test_rule3() throws Exception {
|
||||||
// gunit test on line 60
|
// gunit test on line 60
|
||||||
RuleReturnScope rstruct = (RuleReturnScope)execParser("rule", "\n public a[int i] returns [int y]\n options {backtrack=true;}\n @init {blort}\n : ID ;\n ", 60);
|
RuleReturnScope rstruct = (RuleReturnScope)execParser("rule", "\n a[int i] returns [int y]\n @init {blort}\n : ID ;\n ", 60);
|
||||||
Object actual = ((Tree)rstruct.getTree()).toStringTree();
|
Object actual = ((Tree)rstruct.getTree()).toStringTree();
|
||||||
Object expecting = "(RULE a (RULEMODIFIERS public) int i (returns int y) (OPTIONS (= backtrack true)) (@ init {blort}) (BLOCK (ALT ID)))";
|
Object expecting = "(RULE a int i (returns int y) (@ init {blort}) (BLOCK (ALT ID)))";
|
||||||
assertEquals("testing rule rule", expecting, actual);
|
assertEquals("testing rule rule", expecting, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -169,7 +169,7 @@ public class TestATNSerialization extends BaseTest {
|
||||||
"5->2 EPSILON 0,0,0\n" +
|
"5->2 EPSILON 0,0,0\n" +
|
||||||
"5->3 EPSILON 0,0,0\n" +
|
"5->3 EPSILON 0,0,0\n" +
|
||||||
"6->1 EPSILON 0,0,0\n" +
|
"6->1 EPSILON 0,0,0\n" +
|
||||||
"0:5 1\n";
|
"0:5\n";
|
||||||
ATN atn = createATN(g);
|
ATN atn = createATN(g);
|
||||||
String result = ATNSerializer.getDecoded(g, atn);
|
String result = ATNSerializer.getDecoded(g, atn);
|
||||||
assertEquals(expecting, result);
|
assertEquals(expecting, result);
|
||||||
|
@ -204,7 +204,7 @@ public class TestATNSerialization extends BaseTest {
|
||||||
"8->3 EPSILON 0,0,0\n" +
|
"8->3 EPSILON 0,0,0\n" +
|
||||||
"8->5 EPSILON 0,0,0\n" +
|
"8->5 EPSILON 0,0,0\n" +
|
||||||
"9->1 EPSILON 0,0,0\n" +
|
"9->1 EPSILON 0,0,0\n" +
|
||||||
"0:8 1\n";
|
"0:8\n";
|
||||||
ATN atn = createATN(g);
|
ATN atn = createATN(g);
|
||||||
String result = ATNSerializer.getDecoded(g, atn);
|
String result = ATNSerializer.getDecoded(g, atn);
|
||||||
assertEquals(expecting, result);
|
assertEquals(expecting, result);
|
||||||
|
@ -236,7 +236,7 @@ public class TestATNSerialization extends BaseTest {
|
||||||
"6->7 EPSILON 0,0,0\n" +
|
"6->7 EPSILON 0,0,0\n" +
|
||||||
"7->8 ATOM 2,0,0\n" +
|
"7->8 ATOM 2,0,0\n" +
|
||||||
"8->1 EPSILON 0,0,0\n" +
|
"8->1 EPSILON 0,0,0\n" +
|
||||||
"0:5 1\n";
|
"0:5\n";
|
||||||
ATN atn = createATN(g);
|
ATN atn = createATN(g);
|
||||||
String result = ATNSerializer.getDecoded(g, atn);
|
String result = ATNSerializer.getDecoded(g, atn);
|
||||||
assertEquals(expecting, result);
|
assertEquals(expecting, result);
|
||||||
|
@ -298,7 +298,7 @@ public class TestATNSerialization extends BaseTest {
|
||||||
"6->2 EPSILON 0,0,0\n" +
|
"6->2 EPSILON 0,0,0\n" +
|
||||||
"7->8 ATOM 98,0,0\n" +
|
"7->8 ATOM 98,0,0\n" +
|
||||||
"8->4 EPSILON 0,0,0\n" +
|
"8->4 EPSILON 0,0,0\n" +
|
||||||
"0:0 1\n";
|
"0:0\n";
|
||||||
ATN atn = createATN(lg);
|
ATN atn = createATN(lg);
|
||||||
String result = ATNSerializer.getDecoded(lg, atn);
|
String result = ATNSerializer.getDecoded(lg, atn);
|
||||||
assertEquals(expecting, result);
|
assertEquals(expecting, result);
|
||||||
|
@ -321,7 +321,7 @@ public class TestATNSerialization extends BaseTest {
|
||||||
"1->3 EPSILON 0,0,0\n" +
|
"1->3 EPSILON 0,0,0\n" +
|
||||||
"3->4 RANGE 48,57,0\n" +
|
"3->4 RANGE 48,57,0\n" +
|
||||||
"4->2 EPSILON 0,0,0\n" +
|
"4->2 EPSILON 0,0,0\n" +
|
||||||
"0:0 1\n";
|
"0:0\n";
|
||||||
ATN atn = createATN(lg);
|
ATN atn = createATN(lg);
|
||||||
String result = ATNSerializer.getDecoded(lg, atn);
|
String result = ATNSerializer.getDecoded(lg, atn);
|
||||||
assertEquals(expecting, result);
|
assertEquals(expecting, result);
|
||||||
|
@ -346,7 +346,7 @@ public class TestATNSerialization extends BaseTest {
|
||||||
"3->4 ATOM 97,0,0\n" +
|
"3->4 ATOM 97,0,0\n" +
|
||||||
"4->5 ATOM -1,0,0\n" +
|
"4->5 ATOM -1,0,0\n" +
|
||||||
"5->2 EPSILON 0,0,0\n" +
|
"5->2 EPSILON 0,0,0\n" +
|
||||||
"0:0 1\n";
|
"0:0\n";
|
||||||
ATN atn = createATN(lg);
|
ATN atn = createATN(lg);
|
||||||
String result = ATNSerializer.getDecoded(lg, atn);
|
String result = ATNSerializer.getDecoded(lg, atn);
|
||||||
assertEquals(expecting, result);
|
assertEquals(expecting, result);
|
||||||
|
@ -374,8 +374,8 @@ public class TestATNSerialization extends BaseTest {
|
||||||
"4->6 SET 0,0,0\n" +
|
"4->6 SET 0,0,0\n" +
|
||||||
"5->4 EPSILON 0,0,0\n" +
|
"5->4 EPSILON 0,0,0\n" +
|
||||||
"6->2 EPSILON 0,0,0\n" +
|
"6->2 EPSILON 0,0,0\n" +
|
||||||
"0:0 1\n" +
|
"0:0\n" +
|
||||||
"1:5 1\n";
|
"1:5\n";
|
||||||
ATN atn = createATN(lg);
|
ATN atn = createATN(lg);
|
||||||
String result = ATNSerializer.getDecoded(lg, atn);
|
String result = ATNSerializer.getDecoded(lg, atn);
|
||||||
assertEquals(expecting, result);
|
assertEquals(expecting, result);
|
||||||
|
@ -405,8 +405,8 @@ public class TestATNSerialization extends BaseTest {
|
||||||
"6->4 EPSILON 0,0,0\n" +
|
"6->4 EPSILON 0,0,0\n" +
|
||||||
"6->7 EPSILON 0,0,0\n" +
|
"6->7 EPSILON 0,0,0\n" +
|
||||||
"7->2 EPSILON 0,0,0\n" +
|
"7->2 EPSILON 0,0,0\n" +
|
||||||
"0:0 1\n" +
|
"0:0\n" +
|
||||||
"1:6 1\n";
|
"1:6\n";
|
||||||
ATN atn = createATN(lg);
|
ATN atn = createATN(lg);
|
||||||
String result = ATNSerializer.getDecoded(lg, atn);
|
String result = ATNSerializer.getDecoded(lg, atn);
|
||||||
assertEquals(expecting, result);
|
assertEquals(expecting, result);
|
||||||
|
@ -453,7 +453,7 @@ public class TestATNSerialization extends BaseTest {
|
||||||
"12->13 ATOM 99,0,0\n" +
|
"12->13 ATOM 99,0,0\n" +
|
||||||
"13->14 ACTION 2,1,0\n" +
|
"13->14 ACTION 2,1,0\n" +
|
||||||
"14->6 EPSILON 0,0,0\n" +
|
"14->6 EPSILON 0,0,0\n" +
|
||||||
"0:0 1\n";
|
"0:0\n";
|
||||||
ATN atn = createATN(lg);
|
ATN atn = createATN(lg);
|
||||||
String result = ATNSerializer.getDecoded(lg, atn);
|
String result = ATNSerializer.getDecoded(lg, atn);
|
||||||
assertEquals(expecting, result);
|
assertEquals(expecting, result);
|
||||||
|
@ -477,7 +477,7 @@ public class TestATNSerialization extends BaseTest {
|
||||||
"1->3 EPSILON 0,0,0\n" +
|
"1->3 EPSILON 0,0,0\n" +
|
||||||
"3->4 NOT_SET 0,0,0\n" +
|
"3->4 NOT_SET 0,0,0\n" +
|
||||||
"4->2 EPSILON 0,0,0\n" +
|
"4->2 EPSILON 0,0,0\n" +
|
||||||
"0:0 1\n";
|
"0:0\n";
|
||||||
ATN atn = createATN(lg);
|
ATN atn = createATN(lg);
|
||||||
String result = ATNSerializer.getDecoded(lg, atn);
|
String result = ATNSerializer.getDecoded(lg, atn);
|
||||||
assertEquals(expecting, result);
|
assertEquals(expecting, result);
|
||||||
|
@ -501,7 +501,7 @@ public class TestATNSerialization extends BaseTest {
|
||||||
"1->3 EPSILON 0,0,0\n" +
|
"1->3 EPSILON 0,0,0\n" +
|
||||||
"3->4 SET 0,0,0\n" +
|
"3->4 SET 0,0,0\n" +
|
||||||
"4->2 EPSILON 0,0,0\n" +
|
"4->2 EPSILON 0,0,0\n" +
|
||||||
"0:0 1\n";
|
"0:0\n";
|
||||||
ATN atn = createATN(lg);
|
ATN atn = createATN(lg);
|
||||||
String result = ATNSerializer.getDecoded(lg, atn);
|
String result = ATNSerializer.getDecoded(lg, atn);
|
||||||
assertEquals(expecting, result);
|
assertEquals(expecting, result);
|
||||||
|
@ -525,7 +525,7 @@ public class TestATNSerialization extends BaseTest {
|
||||||
"1->3 EPSILON 0,0,0\n" +
|
"1->3 EPSILON 0,0,0\n" +
|
||||||
"3->4 NOT_SET 0,0,0\n" +
|
"3->4 NOT_SET 0,0,0\n" +
|
||||||
"4->2 EPSILON 0,0,0\n" +
|
"4->2 EPSILON 0,0,0\n" +
|
||||||
"0:0 1\n";
|
"0:0\n";
|
||||||
ATN atn = createATN(lg);
|
ATN atn = createATN(lg);
|
||||||
String result = ATNSerializer.getDecoded(lg, atn);
|
String result = ATNSerializer.getDecoded(lg, atn);
|
||||||
assertEquals(expecting, result);
|
assertEquals(expecting, result);
|
||||||
|
@ -586,9 +586,9 @@ public class TestATNSerialization extends BaseTest {
|
||||||
"18->19 WILDCARD 0,0,0\n" +
|
"18->19 WILDCARD 0,0,0\n" +
|
||||||
"19->20 ACTION 2,1,0\n" +
|
"19->20 ACTION 2,1,0\n" +
|
||||||
"20->7 EPSILON 0,0,0\n" +
|
"20->7 EPSILON 0,0,0\n" +
|
||||||
"0:0 1\n" +
|
"0:0\n" +
|
||||||
"1:1 1\n" +
|
"1:1\n" +
|
||||||
"2:11 1\n";
|
"2:11\n";
|
||||||
ATN atn = createATN(lg);
|
ATN atn = createATN(lg);
|
||||||
String result = ATNSerializer.getDecoded(lg, atn);
|
String result = ATNSerializer.getDecoded(lg, atn);
|
||||||
assertEquals(expecting, result);
|
assertEquals(expecting, result);
|
||||||
|
@ -615,7 +615,7 @@ public class TestATNSerialization extends BaseTest {
|
||||||
"3->4 NOT_SET 0,0,0\n" +
|
"3->4 NOT_SET 0,0,0\n" +
|
||||||
"4->5 NOT_SET 1,0,0\n" +
|
"4->5 NOT_SET 1,0,0\n" +
|
||||||
"5->2 EPSILON 0,0,0\n" +
|
"5->2 EPSILON 0,0,0\n" +
|
||||||
"0:0 1\n";
|
"0:0\n";
|
||||||
ATN atn = createATN(lg);
|
ATN atn = createATN(lg);
|
||||||
String result = ATNSerializer.getDecoded(lg, atn);
|
String result = ATNSerializer.getDecoded(lg, atn);
|
||||||
assertEquals(expecting, result);
|
assertEquals(expecting, result);
|
||||||
|
@ -671,8 +671,8 @@ public class TestATNSerialization extends BaseTest {
|
||||||
"15->7 EPSILON 0,0,0\n" +
|
"15->7 EPSILON 0,0,0\n" +
|
||||||
"16->17 ATOM 100,0,0\n" +
|
"16->17 ATOM 100,0,0\n" +
|
||||||
"17->9 EPSILON 0,0,0\n" +
|
"17->9 EPSILON 0,0,0\n" +
|
||||||
"0:0 1\n" +
|
"0:0\n" +
|
||||||
"1:1 1\n";
|
"1:1\n";
|
||||||
ATN atn = createATN(lg);
|
ATN atn = createATN(lg);
|
||||||
String result = ATNSerializer.getDecoded(lg, atn);
|
String result = ATNSerializer.getDecoded(lg, atn);
|
||||||
assertEquals(expecting, result);
|
assertEquals(expecting, result);
|
||||||
|
@ -721,9 +721,9 @@ public class TestATNSerialization extends BaseTest {
|
||||||
"12->6 EPSILON 0,0,0\n" +
|
"12->6 EPSILON 0,0,0\n" +
|
||||||
"13->14 ATOM 99,0,0\n" +
|
"13->14 ATOM 99,0,0\n" +
|
||||||
"14->8 EPSILON 0,0,0\n" +
|
"14->8 EPSILON 0,0,0\n" +
|
||||||
"0:0 1\n" +
|
"0:0\n" +
|
||||||
"1:1 1\n" +
|
"1:1\n" +
|
||||||
"2:2 1\n";
|
"2:2\n";
|
||||||
ATN atn = createATN(lg);
|
ATN atn = createATN(lg);
|
||||||
String result = ATNSerializer.getDecoded(lg, atn);
|
String result = ATNSerializer.getDecoded(lg, atn);
|
||||||
assertEquals(expecting, result);
|
assertEquals(expecting, result);
|
||||||
|
|
|
@ -1,623 +0,0 @@
|
||||||
/*
|
|
||||||
[The "BSD license"]
|
|
||||||
Copyright (c) 2011 Terence Parr
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions
|
|
||||||
are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in the
|
|
||||||
documentation and/or other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
||||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
||||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
||||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
||||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
||||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.antlr.v4.test;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
public class TestNonGreedyLoops extends BaseTest {
|
|
||||||
@Test public void testNongreedyLoopOnEndIsNop() throws Exception {
|
|
||||||
String grammar =
|
|
||||||
"grammar T;\n" +
|
|
||||||
"s @after {dumpDFA();} : any ID EOF {System.out.println(_input.getText(Interval.of(0,_input.index()-1)));} ;\n" +
|
|
||||||
"any : .* ;\n"+
|
|
||||||
"INT : '0'..'9'+ ;\n" +
|
|
||||||
"ID : 'a'..'z'+ ;\n" +
|
|
||||||
"WS : (' '|'\\n')+ {skip();} ;\n";
|
|
||||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
"x", true);
|
|
||||||
assertEquals("x\n" +
|
|
||||||
"Decision 0:\n" +
|
|
||||||
"s0-ID->:s1=>2\n", found);
|
|
||||||
assertEquals(null, this.stderrDuringParse);
|
|
||||||
|
|
||||||
found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
"34 x", true);
|
|
||||||
assertEquals("34x\n" +
|
|
||||||
"Decision 0:\n" +
|
|
||||||
"s0-INT->:s1=>2\n", found);
|
|
||||||
assertEquals("line 1:0 extraneous input '34' expecting ID\n", this.stderrDuringParse);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test public void testNongreedyPlusLoopOnEndIsNop() throws Exception {
|
|
||||||
String grammar =
|
|
||||||
"grammar T;\n" +
|
|
||||||
"s @after {dumpDFA();} : any ID EOF {System.out.println(_input.getText(Interval.of(0,_input.index()-1)));} ;\n" +
|
|
||||||
"any : .+ ;\n"+ // .+ on end of rule always gives no viable alt. can't bypass but can't match
|
|
||||||
"INT : '0'..'9'+ ;\n" +
|
|
||||||
"ID : 'a'..'z'+ ;\n" +
|
|
||||||
"WS : (' '|'\\n')+ {skip();} ;\n";
|
|
||||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
"x", true);
|
|
||||||
assertEquals("x\n" +
|
|
||||||
"Decision 0:\n" +
|
|
||||||
"s0-ID->:s1=>2\n", found);
|
|
||||||
assertEquals("line 1:0 no viable alternative at input 'x'\n", this.stderrDuringParse);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test public void testNongreedyLoopInOtherRule() throws Exception {
|
|
||||||
String grammar =
|
|
||||||
"grammar T;\n" +
|
|
||||||
"s @after {dumpDFA();} : a {System.out.println(\"alt 1\");} | b {System.out.println(\"alt 2\");} ;\n" +
|
|
||||||
"a : .* ID ;\n"+
|
|
||||||
"b : .* INT ;\n"+
|
|
||||||
"INT : '0'..'9'+ ;\n" +
|
|
||||||
"ID : 'a'..'z'+ ;\n" +
|
|
||||||
"WS : (' '|'\\n')+ {skip();} ;\n";
|
|
||||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
"x", true);
|
|
||||||
assertEquals("alt 1\n" +
|
|
||||||
"Decision 0:\n" +
|
|
||||||
"s0-ID->s1\n" +
|
|
||||||
"s1-EOF->:s2=>1\n" +
|
|
||||||
"\n" +
|
|
||||||
"Decision 1:\n" +
|
|
||||||
"s0-ID->:s1=>2\n", found);
|
|
||||||
assertEquals(null, this.stderrDuringParse);
|
|
||||||
|
|
||||||
found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
"34", true);
|
|
||||||
assertEquals("alt 2\n" +
|
|
||||||
"Decision 0:\n" +
|
|
||||||
"s0-INT->s1\n" +
|
|
||||||
"s1-EOF->:s2=>2\n" +
|
|
||||||
"\n" +
|
|
||||||
"Decision 2:\n" +
|
|
||||||
"s0-INT->:s1=>2\n", found);
|
|
||||||
assertEquals(null, this.stderrDuringParse);
|
|
||||||
|
|
||||||
found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
"34 x", true);
|
|
||||||
assertEquals("alt 1\n" +
|
|
||||||
"Decision 0:\n" +
|
|
||||||
"s0-INT->s1\n" +
|
|
||||||
"s1-ID->s2\n" +
|
|
||||||
"s2-EOF->:s3=>1\n" +
|
|
||||||
"\n" +
|
|
||||||
"Decision 1:\n" +
|
|
||||||
"s0-INT->:s1=>1\n" +
|
|
||||||
"s0-ID->:s2=>2\n", found);
|
|
||||||
assertEquals(null, this.stderrDuringParse);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test public void testNongreedyPlusLoopInOtherRule() throws Exception {
|
|
||||||
String grammar =
|
|
||||||
"grammar T;\n" +
|
|
||||||
"s @after {dumpDFA();} : a {System.out.println(\"alt 1\");} | b {System.out.println(\"alt 2\");} ;\n" +
|
|
||||||
"a : .+ ID ;\n"+
|
|
||||||
"b : .+ INT ;\n"+
|
|
||||||
"INT : '0'..'9'+ ;\n" +
|
|
||||||
"ID : 'a'..'z'+ ;\n" +
|
|
||||||
"WS : (' '|'\\n')+ {skip();} ;\n";
|
|
||||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
"2 3 x", true);
|
|
||||||
assertEquals("alt 1\n" +
|
|
||||||
"Decision 0:\n" +
|
|
||||||
"s0-INT->s1\n" +
|
|
||||||
"s1-INT->s2\n" +
|
|
||||||
"s2-ID->s3\n" +
|
|
||||||
"s3-EOF->:s4=>1\n" +
|
|
||||||
"\n" +
|
|
||||||
"Decision 1:\n" +
|
|
||||||
"s0-INT->:s1=>1\n" +
|
|
||||||
"s0-ID->:s2=>2\n", found);
|
|
||||||
assertEquals(null, this.stderrDuringParse);
|
|
||||||
|
|
||||||
found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
"2 3", true);
|
|
||||||
assertEquals("alt 2\n" +
|
|
||||||
"Decision 0:\n" +
|
|
||||||
"s0-INT->s1\n" +
|
|
||||||
"s1-INT->s2\n" +
|
|
||||||
"s2-EOF->:s3=>2\n" +
|
|
||||||
"\n" +
|
|
||||||
"Decision 2:\n" +
|
|
||||||
"s0-INT->:s1=>2\n", found);
|
|
||||||
assertEquals("line 1:0 no viable alternative at input '2'\n", this.stderrDuringParse);
|
|
||||||
|
|
||||||
found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
"a b c 3", true);
|
|
||||||
assertEquals("alt 2\n" +
|
|
||||||
"Decision 0:\n" +
|
|
||||||
"s0-ID->s1\n" +
|
|
||||||
"s1-ID->s2\n" +
|
|
||||||
"s2-INT->s3\n" +
|
|
||||||
"s2-ID->s2\n" +
|
|
||||||
"s3-EOF->:s4=>2\n" +
|
|
||||||
"\n" +
|
|
||||||
"Decision 2:\n" +
|
|
||||||
"s0-INT->:s2=>2\n" +
|
|
||||||
"s0-ID->:s1=>1\n", found);
|
|
||||||
assertEquals(null, this.stderrDuringParse);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test public void testNongreedyLoopInOneAlt() throws Exception {
|
|
||||||
String grammar =
|
|
||||||
"grammar T;\n" +
|
|
||||||
"s @after {dumpDFA();} : a {System.out.println(\"alt 1\");} EOF | b {System.out.println(\"alt 2\");} EOF ;\n" +
|
|
||||||
"a : .* ;\n"+ // s comes here upon ID but then bypasses, error on EOF
|
|
||||||
"b : INT ;\n"+
|
|
||||||
"INT : '0'..'9'+ ;\n" +
|
|
||||||
"ID : 'a'..'z'+ ;\n" +
|
|
||||||
"WS : (' '|'\\n')+ {skip();} ;\n";
|
|
||||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
"x", true);
|
|
||||||
assertEquals("alt 1\n" +
|
|
||||||
"Decision 0:\n" +
|
|
||||||
"s0-ID->:s1=>1\n", found);
|
|
||||||
assertNull(this.stderrDuringParse);
|
|
||||||
|
|
||||||
found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
"34", true);
|
|
||||||
assertEquals("alt 1\n" +
|
|
||||||
"Decision 0:\n" +
|
|
||||||
"s0-INT->s1\n" +
|
|
||||||
"s1-EOF->:s2=>1\n", found); // resolves INT EOF to alt 1 from s since ambig 'tween a and b
|
|
||||||
assertEquals("line 1:2 reportAmbiguity d=0: ambigAlts={1..2}, input='34'\n",
|
|
||||||
this.stderrDuringParse);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test public void testNongreedyLoopCantSeeEOF() throws Exception {
|
|
||||||
String grammar =
|
|
||||||
"grammar T;\n" +
|
|
||||||
"s @after {dumpDFA();} : block EOF {System.out.println(_input.getText(Interval.of(0,_input.index()-1)));} ;\n" +
|
|
||||||
"block : '{' .* '}' ;\n"+
|
|
||||||
"EQ : '=' ;\n" +
|
|
||||||
"INT : '0'..'9'+ ;\n" +
|
|
||||||
"ID : 'a'..'z'+ ;\n" +
|
|
||||||
"WS : (' '|'\\n')+ {skip();} ;\n";
|
|
||||||
String input =
|
|
||||||
"{ }";
|
|
||||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
input, true);
|
|
||||||
assertEquals("{}\n" +
|
|
||||||
"Decision 0:\n" +
|
|
||||||
"s0-'}'->:s1=>2\n", found);
|
|
||||||
input =
|
|
||||||
"{a b { }";
|
|
||||||
found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
input, true);
|
|
||||||
assertEquals("{ab{}\n" +
|
|
||||||
"Decision 0:\n" +
|
|
||||||
"s0-'{'->:s1=>1\n" +
|
|
||||||
"s0-'}'->:s2=>2\n" +
|
|
||||||
"s0-ID->:s1=>1\n", found);
|
|
||||||
input =
|
|
||||||
"{ } a 2 { }"; // FAILS to match since it terminates loop at first { }
|
|
||||||
found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
input, true);
|
|
||||||
assertEquals("", found); // should not print output; resync kills rest of input til '}' then returns normally
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test public void testNongreedyLoop() throws Exception {
|
|
||||||
String grammar =
|
|
||||||
"grammar T;\n" +
|
|
||||||
"s @after {dumpDFA();} : ifstat ';' EOF {System.out.println(_input.getText(Interval.of(0,_input.index()-1)));} ;\n" +
|
|
||||||
"ifstat : 'if' '(' .* ')' block ;\n" +
|
|
||||||
"block : '{' '}' ;\n"+
|
|
||||||
"EQ : '=' ;\n" +
|
|
||||||
"INT : '0'..'9'+ ;\n" +
|
|
||||||
"ID : 'a'..'z'+ ;\n" +
|
|
||||||
"WS : (' '|'\\n')+ {skip();} ;\n";
|
|
||||||
String input =
|
|
||||||
"if ( x=34 ) { } ;";
|
|
||||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
input, true);
|
|
||||||
assertEquals("if(x=34){};\n" +
|
|
||||||
"Decision 0:\n" +
|
|
||||||
"s0-')'->s2\n" +
|
|
||||||
"s0-'='->:s1=>1\n" +
|
|
||||||
"s0-INT->:s1=>1\n" +
|
|
||||||
"s0-ID->:s1=>1\n" +
|
|
||||||
"s2-'{'->s3\n" +
|
|
||||||
"s3-'}'->:s4=>2\n", found);
|
|
||||||
input =
|
|
||||||
"if ( ))) ) { } ;";
|
|
||||||
found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
input, true);
|
|
||||||
assertEquals("if()))){};\n" +
|
|
||||||
"Decision 0:\n" +
|
|
||||||
"s0-')'->s1\n" +
|
|
||||||
"s1-'{'->s3\n" +
|
|
||||||
"s1-')'->:s2=>1\n" +
|
|
||||||
"s3-'}'->:s4=>2\n", found);
|
|
||||||
input =
|
|
||||||
"if (() { } a 2) { } ;"; // The first { } should match block so should stop
|
|
||||||
found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
input, true);
|
|
||||||
assertEquals("", found); // should not finish to print output
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test public void testNongreedyLoopPassingThroughAnotherNongreedy() throws Exception {
|
|
||||||
String grammar =
|
|
||||||
"grammar T;\n" +
|
|
||||||
"s @after {dumpDFA();} : ifstat ';' EOF {System.out.println(_input.getText(Interval.of(0,_input.index()-1)));} ;\n" +
|
|
||||||
"ifstat : 'if' '(' .* ')' block ;\n" +
|
|
||||||
"block : '{' (block|.)* '}' ;\n"+
|
|
||||||
"EQ : '=' ;\n" +
|
|
||||||
"INT : '0'..'9'+ ;\n" +
|
|
||||||
"ID : 'a'..'z'+ ;\n" +
|
|
||||||
"WS : (' '|'\\n')+ {skip();} ;\n";
|
|
||||||
String input =
|
|
||||||
"if ( x=34 ) { {return a} b 34 } ;";
|
|
||||||
String found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
input, true);
|
|
||||||
assertEquals("if(x=34){{returna}b34};\n" +
|
|
||||||
"Decision 0:\n" +
|
|
||||||
"s0-')'->s2\n" +
|
|
||||||
"s0-'='->:s1=>1\n" +
|
|
||||||
"s0-INT->:s1=>1\n" +
|
|
||||||
"s0-ID->:s1=>1\n" +
|
|
||||||
"s2-'{'->s3\n" +
|
|
||||||
"s3-'{'->s4\n" +
|
|
||||||
"s4-'}'->:s5=>2\n" +
|
|
||||||
"s4-ID->s4\n" +
|
|
||||||
"\n" +
|
|
||||||
"Decision 1:\n" +
|
|
||||||
"s0-'{'->:s1=>1\n" +
|
|
||||||
"s0-INT->:s2=>2\n" +
|
|
||||||
"s0-ID->:s2=>2\n" +
|
|
||||||
"\n" +
|
|
||||||
"Decision 2:\n" +
|
|
||||||
"s0-'{'->:s1=>1\n" +
|
|
||||||
"s0-'}'->:s3=>2\n" +
|
|
||||||
"s0-INT->:s2=>1\n" +
|
|
||||||
"s0-ID->:s2=>1\n", found);
|
|
||||||
|
|
||||||
input =
|
|
||||||
"if ( ()) ) { {return a} b 34 } ;";
|
|
||||||
found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
input, true);
|
|
||||||
assertEquals("if(())){{returna}b34};\n" +
|
|
||||||
"Decision 0:\n" +
|
|
||||||
"s0-')'->s2\n" +
|
|
||||||
"s0-'('->:s1=>1\n" +
|
|
||||||
"s2-'{'->s4\n" +
|
|
||||||
"s2-')'->:s3=>1\n" +
|
|
||||||
"s4-'{'->s5\n" +
|
|
||||||
"s5-'}'->:s6=>2\n" +
|
|
||||||
"s5-ID->s5\n" +
|
|
||||||
"\n" +
|
|
||||||
"Decision 1:\n" +
|
|
||||||
"s0-'{'->:s1=>1\n" +
|
|
||||||
"s0-INT->:s2=>2\n" +
|
|
||||||
"s0-ID->:s2=>2\n" +
|
|
||||||
"\n" +
|
|
||||||
"Decision 2:\n" +
|
|
||||||
"s0-'{'->:s1=>1\n" +
|
|
||||||
"s0-'}'->:s3=>2\n" +
|
|
||||||
"s0-INT->:s2=>1\n" +
|
|
||||||
"s0-ID->:s2=>1\n", found);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test public void testStatLoopNongreedyNotNecessary() throws Exception {
|
|
||||||
// EOF on end means LL(*) can identify when to stop the loop.
|
|
||||||
String grammar =
|
|
||||||
"grammar T;\n" +
|
|
||||||
"s @after {dumpDFA();} : stat* ID '=' ID ';' EOF {System.out.println(_input.getText(Interval.of(0,_input.index()-1)));} ;\n" +
|
|
||||||
"stat : 'if' '(' INT ')' stat\n" +
|
|
||||||
" | 'return' INT ';'\n" +
|
|
||||||
" | ID '=' (INT|ID) ';'\n" +
|
|
||||||
" | block\n" +
|
|
||||||
" ;\n" +
|
|
||||||
"block : '{' stat* '}' ;\n"+
|
|
||||||
"EQ : '=' ;\n" +
|
|
||||||
"INT : '0'..'9'+ ;\n" +
|
|
||||||
"ID : 'a'..'z'+ ;\n" +
|
|
||||||
"WS : (' '|'\\n')+ {skip();} ;\n";
|
|
||||||
String input =
|
|
||||||
"x=1; a=b;";
|
|
||||||
String found = null;
|
|
||||||
found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
input, true);
|
|
||||||
assertEquals("x=1;a=b;\n" +
|
|
||||||
"Decision 0:\n" +
|
|
||||||
"s0-ID->s1\n" +
|
|
||||||
"s1-'='->s2\n" +
|
|
||||||
"s2-INT->:s3=>1\n" +
|
|
||||||
"s2-ID->s4\n" +
|
|
||||||
"s4-';'->s5\n" +
|
|
||||||
"s5-EOF->:s6=>2\n", found);
|
|
||||||
input =
|
|
||||||
"if ( 1 ) { x=3; { return 4; } } return 99; abc=def;";
|
|
||||||
found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
input, true);
|
|
||||||
assertEquals("if(1){x=3;{return4;}}return99;abc=def;\n" +
|
|
||||||
"Decision 0:\n" +
|
|
||||||
"s0-'if'->:s1=>1\n" +
|
|
||||||
"s0-'return'->:s2=>1\n" +
|
|
||||||
"s0-ID->s3\n" +
|
|
||||||
"s3-'='->s4\n" +
|
|
||||||
"s4-ID->s5\n" +
|
|
||||||
"s5-';'->s6\n" +
|
|
||||||
"s6-EOF->:s7=>2\n", found);
|
|
||||||
input =
|
|
||||||
"x=1; a=3;"; // FAILS to match since it can't match last element
|
|
||||||
execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
input, true);
|
|
||||||
// can't match EOF to ID '=' '3' ';'
|
|
||||||
assertEquals("line 1:9 no viable alternative at input '<EOF>'\n",
|
|
||||||
this.stderrDuringParse);
|
|
||||||
|
|
||||||
input =
|
|
||||||
"x=1; a=b; z=3;"; // FAILS to match since it can't match last element
|
|
||||||
execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
input, true);
|
|
||||||
assertEquals("line 1:14 no viable alternative at input '<EOF>'\n",
|
|
||||||
this.stderrDuringParse);
|
|
||||||
// should not finish to print output
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test public void testStatLoopNongreedyNecessary() throws Exception {
|
|
||||||
// stops scanning ahead at end of rule s since decision is nongreedy.
|
|
||||||
// this says: "match statements until we see a=b; assignment; ignore any
|
|
||||||
// statements that follow."
|
|
||||||
String grammar =
|
|
||||||
"grammar T;\n" +
|
|
||||||
"random : s ;" + // call s so s isn't followed by EOF directly
|
|
||||||
"s @after {dumpDFA();} : (options {greedy=false;} : stat)* ID '=' ID ';'\n" +
|
|
||||||
" {System.out.println(_input.getText(Interval.of(0,_input.index()-1)));} ;\n" +
|
|
||||||
"stat : 'if' '(' INT ')' stat\n" +
|
|
||||||
" | 'return' INT ';'\n" +
|
|
||||||
" | ID '=' (INT|ID) ';'\n" +
|
|
||||||
" | block\n" +
|
|
||||||
" ;\n" +
|
|
||||||
"block : '{' stat* '}' ;\n"+
|
|
||||||
"EQ : '=' ;\n" +
|
|
||||||
"INT : '0'..'9'+ ;\n" +
|
|
||||||
"ID : 'a'..'z'+ ;\n" +
|
|
||||||
"WS : (' '|'\\n')+ {skip();} ;\n";
|
|
||||||
String input =
|
|
||||||
"x=1; a=b; x=y;";
|
|
||||||
String found = null;
|
|
||||||
found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
input, true);
|
|
||||||
assertEquals("x=1;a=b;\n" +
|
|
||||||
"Decision 0:\n" +
|
|
||||||
"s0-ID->s1\n" +
|
|
||||||
"s1-'='->s2\n" +
|
|
||||||
"s2-INT->:s3=>1\n" +
|
|
||||||
"s2-ID->s4\n" +
|
|
||||||
"s4-';'->:s5=>2\n", found); // ignores x=1 that follows first a=b assignment
|
|
||||||
input =
|
|
||||||
"if ( 1 ) { x=3; { return 4; } } return 99; abc=def;";
|
|
||||||
found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
input, true);
|
|
||||||
assertEquals("if(1){x=3;{return4;}}return99;abc=def;\n" +
|
|
||||||
"Decision 0:\n" +
|
|
||||||
"s0-'if'->:s1=>1\n" +
|
|
||||||
"s0-'return'->:s2=>1\n" +
|
|
||||||
"s0-ID->s3\n" +
|
|
||||||
"s3-'='->s4\n" +
|
|
||||||
"s4-ID->s5\n" +
|
|
||||||
"s5-';'->:s6=>2\n", found);
|
|
||||||
input =
|
|
||||||
"x=1; a=3;"; // FAILS to match since it can't match either stat
|
|
||||||
execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
input, true);
|
|
||||||
// can't match EOF to ID '=' '0' ';'
|
|
||||||
assertEquals("line 1:9 no viable alternative at input '<EOF>'\n",
|
|
||||||
this.stderrDuringParse);
|
|
||||||
input =
|
|
||||||
"x=1; a=b; z=3;"; // stops at a=b; ignores z=3;
|
|
||||||
found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
input, true);
|
|
||||||
assertEquals("x=1;a=b;\n" +
|
|
||||||
"Decision 0:\n" +
|
|
||||||
"s0-ID->s1\n" +
|
|
||||||
"s1-'='->s2\n" +
|
|
||||||
"s2-INT->:s3=>1\n" +
|
|
||||||
"s2-ID->s4\n" +
|
|
||||||
"s4-';'->:s5=>2\n", found); // should not finish all input
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test public void testHTMLTags() throws Exception {
|
|
||||||
String grammar =
|
|
||||||
"grammar T;\n" +
|
|
||||||
"s @after {dumpDFA();} : (item)+ {System.out.println(_input.getText(Interval.of(0,_input.index()-1)));} ;\n" +
|
|
||||||
"item : tag | . ;\n" +
|
|
||||||
"tag : '<' '/'? .* '>' ;\n" +
|
|
||||||
"EQ : '=' ;\n" +
|
|
||||||
"COMMA : ',' ;\n" +
|
|
||||||
"ID : 'a'..'z'+ ;\n" +
|
|
||||||
"STR : '\"' .* '\"' ;\n" +
|
|
||||||
"INT : '0'..'9'+;\n" +
|
|
||||||
"WS : (' '|'\\n') {skip();} ;\n";
|
|
||||||
|
|
||||||
String found = null;
|
|
||||||
found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
"<a>foo</a>", true);
|
|
||||||
assertEquals("<a>foo</a>\n" +
|
|
||||||
"Decision 1:\n" +
|
|
||||||
"s0-'<'->s1\n" +
|
|
||||||
"s0-ID->:s5=>2\n" +
|
|
||||||
"s1-'/'->s2\n" +
|
|
||||||
"s1-ID->s2\n" +
|
|
||||||
"s2-'>'->s3\n" +
|
|
||||||
"s2-ID->s2\n" +
|
|
||||||
"s3-EOF->s6^\n" +
|
|
||||||
"s3-'<'->s4^\n" +
|
|
||||||
"s3-ID->s3\n" +
|
|
||||||
"\n" +
|
|
||||||
"Decision 2:\n" +
|
|
||||||
"s0-'/'->:s2=>1\n" +
|
|
||||||
"s0-ID->:s1=>2\n" +
|
|
||||||
"\n" +
|
|
||||||
"Decision 3:\n" +
|
|
||||||
"s0-'>'->:s2=>2\n" +
|
|
||||||
"s0-ID->:s1=>1\n", found);
|
|
||||||
assertEquals("line 1:6 reportAttemptingFullContext d=1, input='<a>foo<'\n" +
|
|
||||||
"line 1:6 reportAmbiguity d=1: ambigAlts={1..2}, input='<a>foo<'\n" +
|
|
||||||
"line 1:10 reportAttemptingFullContext d=1, input='</a>'\n" +
|
|
||||||
"line 1:10 reportAmbiguity d=1: ambigAlts={1..2}, input='</a>'\n" +
|
|
||||||
"line 1:7 reportAmbiguity d=2: ambigAlts={1..2}, input='/'\n",
|
|
||||||
this.stderrDuringParse);
|
|
||||||
|
|
||||||
found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
"<a></a>", true);
|
|
||||||
assertEquals("<a></a>\n" +
|
|
||||||
"Decision 1:\n" +
|
|
||||||
"s0-'<'->s1\n" +
|
|
||||||
"s1-'/'->s2\n" +
|
|
||||||
"s1-ID->s2\n" +
|
|
||||||
"s2-'>'->s3\n" +
|
|
||||||
"s2-ID->s2\n" +
|
|
||||||
"s3-EOF->s5^\n" +
|
|
||||||
"s3-'<'->s4^\n" +
|
|
||||||
"\n" +
|
|
||||||
"Decision 2:\n" +
|
|
||||||
"s0-'/'->:s2=>1\n" +
|
|
||||||
"s0-ID->:s1=>2\n" +
|
|
||||||
"\n" +
|
|
||||||
"Decision 3:\n" +
|
|
||||||
"s0-'>'->:s2=>2\n" +
|
|
||||||
"s0-ID->:s1=>1\n", found);
|
|
||||||
found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
"</b><a src=\"abc\", width=32>", true);
|
|
||||||
assertEquals("</b><asrc=\"abc\",width=32>\n" +
|
|
||||||
"Decision 1:\n" +
|
|
||||||
"s0-'<'->s1\n" +
|
|
||||||
"s1-'/'->s2\n" +
|
|
||||||
"s1-ID->s2\n" +
|
|
||||||
"s2-'>'->s3\n" +
|
|
||||||
"s2-'='->s2\n" +
|
|
||||||
"s2-','->s2\n" +
|
|
||||||
"s2-ID->s2\n" +
|
|
||||||
"s2-STR->s2\n" +
|
|
||||||
"s2-INT->s2\n" +
|
|
||||||
"s3-EOF->s5^\n" +
|
|
||||||
"s3-'<'->s4^\n" +
|
|
||||||
"\n" +
|
|
||||||
"Decision 2:\n" +
|
|
||||||
"s0-'/'->:s1=>1\n" +
|
|
||||||
"s0-ID->:s2=>2\n" +
|
|
||||||
"\n" +
|
|
||||||
"Decision 3:\n" +
|
|
||||||
"s0-'>'->:s2=>2\n" +
|
|
||||||
"s0-'='->:s1=>1\n" +
|
|
||||||
"s0-','->:s1=>1\n" +
|
|
||||||
"s0-ID->:s1=>1\n" +
|
|
||||||
"s0-STR->:s1=>1\n" +
|
|
||||||
"s0-INT->:s1=>1\n", found);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** lookahead prediction with '.' can be misleading since nongreedy. Lookahead
|
|
||||||
* that sees into a non-greedy loop, thinks it is greedy.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testFindHTMLTags() throws Exception {
|
|
||||||
String grammar =
|
|
||||||
"grammar T;\n" +
|
|
||||||
"s @after {dumpDFA();} : ( .* (tag {System.out.println($tag.text);} |header) )* EOF;\n" +
|
|
||||||
"tag : '<' .+ '>' ;\n" +
|
|
||||||
"header : 'x' 'y' ;\n" +
|
|
||||||
"EQ : '=' ;\n" +
|
|
||||||
"COMMA : ',' ;\n" +
|
|
||||||
"ID : 'a'..'z'+ ;\n" +
|
|
||||||
"STR : '\"' .* '\"' ;\n" +
|
|
||||||
"INT : '0'..'9'+;\n" +
|
|
||||||
"WS : (' '|'\\n') {skip();} ;\n";
|
|
||||||
|
|
||||||
String found = null;
|
|
||||||
System.out.println(grammar);
|
|
||||||
found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
",=foo <a x= 3>32skidoo<a><img>", true);
|
|
||||||
assertEquals("<ax=3>\n" +
|
|
||||||
"<a>\n" +
|
|
||||||
"<img>\n" +
|
|
||||||
"Decision 0:\n" + // .*
|
|
||||||
"s0-'<'->s2\n" +
|
|
||||||
"s0-'='->:s1=>1\n" +
|
|
||||||
"s0-','->:s1=>1\n" +
|
|
||||||
"s0-ID->:s1=>1\n" +
|
|
||||||
"s0-INT->:s1=>1\n" +
|
|
||||||
"s2-ID->s3\n" +
|
|
||||||
"s3-'x'->s4\n" +
|
|
||||||
"s3-'>'->:s5=>2\n" +
|
|
||||||
"s3-INT->s3\n" +
|
|
||||||
"s4-'='->s3\n" +
|
|
||||||
"\n" +
|
|
||||||
"Decision 3:\n" + // .+
|
|
||||||
"s0-'x'->:s1=>1\n" +
|
|
||||||
"s0-'>'->:s2=>2\n" +
|
|
||||||
"s0-'='->:s1=>1\n" +
|
|
||||||
"s0-ID->:s1=>1\n" +
|
|
||||||
"s0-INT->:s1=>1\n", found);
|
|
||||||
assertEquals(null,
|
|
||||||
this.stderrDuringParse);
|
|
||||||
|
|
||||||
found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
"x x<a>", true);
|
|
||||||
assertEquals("<a>\n" +
|
|
||||||
"Decision 0:\n" +
|
|
||||||
"s0-'x'->s1\n" +
|
|
||||||
"s0-'<'->s4\n" +
|
|
||||||
"s1-'x'->:s2=>1\n" +
|
|
||||||
"s1-'<'->:s3=>1\n" +
|
|
||||||
"s4-ID->s5\n" +
|
|
||||||
"s5-'>'->:s6=>2\n" +
|
|
||||||
"\n" +
|
|
||||||
"Decision 3:\n" +
|
|
||||||
"s0-'>'->:s2=>2\n" +
|
|
||||||
"s0-ID->:s1=>1\n", found);
|
|
||||||
// gets line 1:3 no viable alternative at input '>'. Why??
|
|
||||||
// oH! it sees .+ and figures it matches > so <> predicts tag CORRECT!
|
|
||||||
// Seeing '.' in a lookahead prediction can be misleading!!
|
|
||||||
found = execParser("T.g4", grammar, "TParser", "TLexer", "s",
|
|
||||||
"x <><a>", true);
|
|
||||||
assertEquals("<\n" +
|
|
||||||
"<a>\n" +
|
|
||||||
"Decision 0:\n" +
|
|
||||||
"s0-'x'->s1\n" +
|
|
||||||
"s0-'>'->:s6=>1\n" +
|
|
||||||
"s0-'<'->s3\n" +
|
|
||||||
"s1-'<'->:s2=>1\n" +
|
|
||||||
"s3-'>'->s4\n" +
|
|
||||||
"s3-ID->s4\n" +
|
|
||||||
"s4-'>'->:s7=>2\n" +
|
|
||||||
"s4-'<'->:s5=>2\n" +
|
|
||||||
"\n" +
|
|
||||||
"Decision 3:\n" +
|
|
||||||
"s0-'>'->:s1=>2\n" +
|
|
||||||
"s0-ID->:s2=>1\n", // doesn't match tag; null
|
|
||||||
found);
|
|
||||||
assertEquals("line 1:3 no viable alternative at input '>'\n",
|
|
||||||
this.stderrDuringParse);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue