forked from jasder/antlr
ok, got new simple semantics in for sempreds; no forced actions and preds always eval unless ctx dependent, in which case they need to bein original rule.
[git-p4: depot-paths = "//depot/code/antlr4/main/": change = 8970]
This commit is contained in:
parent
328444244f
commit
a7f2bc31ac
|
@ -229,15 +229,15 @@ public class Recognizer<ATNInterpreter> {
|
||||||
/** Create context for a rule reference IN fromRuleIndex using parent _localctx.
|
/** Create context for a rule reference IN fromRuleIndex using parent _localctx.
|
||||||
* Used only when there are arguments to the rule function. _localctx
|
* Used only when there are arguments to the rule function. _localctx
|
||||||
* must be correct context for fromRuleIndex.
|
* must be correct context for fromRuleIndex.
|
||||||
*/
|
|
||||||
public RuleContext newContext(RuleContext _localctx, int s, int fromRuleIndex, int actionIndex) {
|
public RuleContext newContext(RuleContext _localctx, int s, int fromRuleIndex, int actionIndex) {
|
||||||
return new ParserRuleContext(_localctx, s);
|
return new ParserRuleContext(_localctx, s);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
/** Map a rule index to appropriate RuleContext subclass. Used when rule
|
/** Map a rule index to appropriate RuleContext subclass. Used when rule
|
||||||
* has no arguments.
|
* has no arguments.
|
||||||
*/
|
|
||||||
public RuleContext newContext(RuleContext _localctx, int s, int targetRuleIndex) {
|
public RuleContext newContext(RuleContext _localctx, int s, int targetRuleIndex) {
|
||||||
return new ParserRuleContext(_localctx, s);
|
return new ParserRuleContext(_localctx, s);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ public class ParserATNSimulator extends ATNSimulator {
|
||||||
*
|
*
|
||||||
* The full stack at any moment is [config.outerContext + config.context].
|
* The full stack at any moment is [config.outerContext + config.context].
|
||||||
*/
|
*/
|
||||||
protected RuleContext originalContext;
|
protected RuleContext outerContext;
|
||||||
|
|
||||||
protected Set<ATNConfig> closureBusy = new HashSet<ATNConfig>();
|
protected Set<ATNConfig> closureBusy = new HashSet<ATNConfig>();
|
||||||
|
|
||||||
|
@ -82,20 +82,20 @@ public class ParserATNSimulator extends ATNSimulator {
|
||||||
// System.out.println(dot.getDOT(atn.rules.get(1), parser.getRuleNames()));
|
// System.out.println(dot.getDOT(atn.rules.get(1), parser.getRuleNames()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public int adaptivePredict(TokenStream input, int decision, RuleContext originalContext) {
|
public int adaptivePredict(TokenStream input, int decision, RuleContext outerContext) {
|
||||||
predict_calls++;
|
predict_calls++;
|
||||||
DFA dfa = decisionToDFA[decision];
|
DFA dfa = decisionToDFA[decision];
|
||||||
if ( dfa==null || dfa.s0==null ) {
|
if ( dfa==null || dfa.s0==null ) {
|
||||||
ATNState startState = atn.decisionToState.get(decision);
|
ATNState startState = atn.decisionToState.get(decision);
|
||||||
decisionToDFA[decision] = dfa = new DFA(startState);
|
decisionToDFA[decision] = dfa = new DFA(startState);
|
||||||
dfa.decision = decision;
|
dfa.decision = decision;
|
||||||
return predictATN(dfa, input, decision, originalContext, false);
|
return predictATN(dfa, input, decision, outerContext, false);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
//dump(dfa);
|
//dump(dfa);
|
||||||
// start with the DFA
|
// start with the DFA
|
||||||
int m = input.mark();
|
int m = input.mark();
|
||||||
int alt = execDFA(input, dfa, dfa.s0, originalContext);
|
int alt = execDFA(input, dfa, dfa.s0, outerContext);
|
||||||
input.seek(m);
|
input.seek(m);
|
||||||
return alt;
|
return alt;
|
||||||
}
|
}
|
||||||
|
@ -103,13 +103,13 @@ public class ParserATNSimulator extends ATNSimulator {
|
||||||
|
|
||||||
public int predictATN(DFA dfa, TokenStream input,
|
public int predictATN(DFA dfa, TokenStream input,
|
||||||
int decision,
|
int decision,
|
||||||
RuleContext originalContext,
|
RuleContext outerContext,
|
||||||
boolean useContext)
|
boolean useContext)
|
||||||
{
|
{
|
||||||
if ( originalContext==null ) originalContext = RuleContext.EMPTY;
|
if ( outerContext==null ) outerContext = RuleContext.EMPTY;
|
||||||
this.originalContext = originalContext;
|
this.outerContext = outerContext;
|
||||||
RuleContext ctx = RuleContext.EMPTY;
|
RuleContext ctx = RuleContext.EMPTY;
|
||||||
if ( useContext ) ctx = originalContext;
|
if ( useContext ) ctx = outerContext;
|
||||||
OrderedHashSet<ATNConfig> s0_closure =
|
OrderedHashSet<ATNConfig> s0_closure =
|
||||||
computeStartState(dfa.atnStartState, ctx);
|
computeStartState(dfa.atnStartState, ctx);
|
||||||
dfa.s0 = addDFAState(dfa, s0_closure);
|
dfa.s0 = addDFAState(dfa, s0_closure);
|
||||||
|
@ -121,7 +121,7 @@ public class ParserATNSimulator extends ATNSimulator {
|
||||||
int alt = 0;
|
int alt = 0;
|
||||||
int m = input.mark();
|
int m = input.mark();
|
||||||
try {
|
try {
|
||||||
alt = execATN(input, dfa, m, s0_closure, originalContext, useContext);
|
alt = execATN(input, dfa, m, s0_closure, useContext);
|
||||||
}
|
}
|
||||||
catch (NoViableAltException nvae) { dumpDeadEndConfigs(nvae); throw nvae; }
|
catch (NoViableAltException nvae) { dumpDeadEndConfigs(nvae); throw nvae; }
|
||||||
finally {
|
finally {
|
||||||
|
@ -136,14 +136,14 @@ public class ParserATNSimulator extends ATNSimulator {
|
||||||
DFA dfa = new DFA(startState);
|
DFA dfa = new DFA(startState);
|
||||||
RuleContext ctx = new ParserRuleContext();
|
RuleContext ctx = new ParserRuleContext();
|
||||||
OrderedHashSet<ATNConfig> s0_closure = computeStartState(startState, ctx);
|
OrderedHashSet<ATNConfig> s0_closure = computeStartState(startState, ctx);
|
||||||
return execATN(input, dfa, input.index(), s0_closure, ctx, false);
|
return execATN(input, dfa, input.index(), s0_closure, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int execDFA(TokenStream input, DFA dfa, DFAState s0, RuleContext originalContext) {
|
public int execDFA(TokenStream input, DFA dfa, DFAState s0, RuleContext outerContext) {
|
||||||
if ( dfa_debug ) System.out.println("DFA decision "+dfa.decision+" exec LA(1)=="+input.LT(1));
|
if ( dfa_debug ) System.out.println("DFA decision "+dfa.decision+" exec LA(1)=="+input.LT(1));
|
||||||
// dump(dfa);
|
// dump(dfa);
|
||||||
if ( originalContext==null ) originalContext = RuleContext.EMPTY;
|
if ( outerContext==null ) outerContext = RuleContext.EMPTY;
|
||||||
this.originalContext = originalContext;
|
this.outerContext = outerContext;
|
||||||
DFAState prevAcceptState = null;
|
DFAState prevAcceptState = null;
|
||||||
DFAState s = s0;
|
DFAState s = s0;
|
||||||
int t = input.LA(1);
|
int t = input.LA(1);
|
||||||
|
@ -153,16 +153,16 @@ public class ParserATNSimulator extends ATNSimulator {
|
||||||
if ( dfa_debug ) System.out.println("DFA state "+s.stateNumber+" LA(1)=="+t);
|
if ( dfa_debug ) System.out.println("DFA state "+s.stateNumber+" LA(1)=="+t);
|
||||||
// TODO: ctxSensitive
|
// TODO: ctxSensitive
|
||||||
if ( s.isCtxSensitive ) {
|
if ( s.isCtxSensitive ) {
|
||||||
Integer predI = s.ctxToPrediction.get(originalContext);
|
Integer predI = s.ctxToPrediction.get(outerContext);
|
||||||
if ( dfa_debug ) System.out.println("ctx sensitive state "+originalContext+"->"+predI+
|
if ( dfa_debug ) System.out.println("ctx sensitive state "+outerContext+"->"+predI+
|
||||||
" in "+s);
|
" in "+s);
|
||||||
if ( predI!=null ) return predI;
|
if ( predI!=null ) return predI;
|
||||||
// System.out.println("start all over with ATN; can't use DFA");
|
// System.out.println("start all over with ATN; can't use DFA");
|
||||||
// start all over with ATN; can't use DFA
|
// start all over with ATN; can't use DFA
|
||||||
input.seek(start);
|
input.seek(start);
|
||||||
DFA throwAwayDFA = new DFA(dfa.atnStartState);
|
DFA throwAwayDFA = new DFA(dfa.atnStartState);
|
||||||
int alt = execATN(input, throwAwayDFA, start, s0.configs, originalContext, false);
|
int alt = execATN(input, throwAwayDFA, start, s0.configs, false);
|
||||||
s.ctxToPrediction.put(originalContext, alt);
|
s.ctxToPrediction.put(outerContext, alt);
|
||||||
return alt;
|
return alt;
|
||||||
}
|
}
|
||||||
if ( s.isAcceptState ) {
|
if ( s.isAcceptState ) {
|
||||||
|
@ -182,7 +182,7 @@ public class ParserATNSimulator extends ATNSimulator {
|
||||||
" at DFA state "+s.stateNumber);
|
" at DFA state "+s.stateNumber);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
alt = execATN(input, dfa, start, s.configs, originalContext, false);
|
alt = execATN(input, dfa, start, s.configs, false);
|
||||||
// this adds edge even if next state is accept for
|
// this adds edge even if next state is accept for
|
||||||
// same alt; e.g., s0-A->:s1=>2-B->:s2=>2
|
// same alt; e.g., s0-A->:s1=>2-B->:s2=>2
|
||||||
// TODO: This next stuff kills edge, but extra states remain. :(
|
// TODO: This next stuff kills edge, but extra states remain. :(
|
||||||
|
@ -229,7 +229,6 @@ public class ParserATNSimulator extends ATNSimulator {
|
||||||
DFA dfa,
|
DFA dfa,
|
||||||
int startIndex,
|
int startIndex,
|
||||||
OrderedHashSet<ATNConfig> s0,
|
OrderedHashSet<ATNConfig> s0,
|
||||||
RuleContext originalContext,
|
|
||||||
boolean useContext)
|
boolean useContext)
|
||||||
{
|
{
|
||||||
if ( debug ) System.out.println("ATN decision "+dfa.decision+" exec LA(1)=="+input.LT(1));
|
if ( debug ) System.out.println("ATN decision "+dfa.decision+" exec LA(1)=="+input.LT(1));
|
||||||
|
@ -270,7 +269,7 @@ public class ParserATNSimulator extends ATNSimulator {
|
||||||
Set<Integer> ambigAlts = getAmbiguousAlts(reach);
|
Set<Integer> ambigAlts = getAmbiguousAlts(reach);
|
||||||
if ( ambigAlts!=null ) {
|
if ( ambigAlts!=null ) {
|
||||||
if ( debug ) {
|
if ( debug ) {
|
||||||
ATNState loc = atn.states.get(originalContext.s);
|
ATNState loc = atn.states.get(outerContext.s);
|
||||||
String rname = "n/a";
|
String rname = "n/a";
|
||||||
if ( parser !=null ) rname = parser.getRuleNames()[loc.ruleIndex];
|
if ( parser !=null ) rname = parser.getRuleNames()[loc.ruleIndex];
|
||||||
System.out.println("AMBIG in "+rname+" for alt "+ambigAlts+" upon "+
|
System.out.println("AMBIG in "+rname+" for alt "+ambigAlts+" upon "+
|
||||||
|
@ -280,15 +279,15 @@ public class ParserATNSimulator extends ATNSimulator {
|
||||||
dfa.conflict = true; // at least one DFA state is ambiguous
|
dfa.conflict = true; // at least one DFA state is ambiguous
|
||||||
if ( !userWantsCtxSensitive ) reportConflict(startIndex, input.index(), ambigAlts, reach);
|
if ( !userWantsCtxSensitive ) reportConflict(startIndex, input.index(), ambigAlts, reach);
|
||||||
|
|
||||||
// ATNState loc = atn.states.get(originalContext.s);
|
// ATNState loc = atn.states.get(outerContext.s);
|
||||||
// String rname = recog.getRuleNames()[loc.ruleIndex];
|
// String rname = recog.getRuleNames()[loc.ruleIndex];
|
||||||
// System.out.println("AMBIG orig="+originalContext.toString((BaseRecognizer)recog)+" for alt "+ambigAlts+" upon "+
|
// System.out.println("AMBIG orig="+outerContext.toString((BaseRecognizer)recog)+" for alt "+ambigAlts+" upon "+
|
||||||
// input.toString(startIndex, input.index()));
|
// input.toString(startIndex, input.index()));
|
||||||
if ( !userWantsCtxSensitive || useContext ) {
|
if ( !userWantsCtxSensitive || useContext ) {
|
||||||
resolveToMinAlt(reach, ambigAlts);
|
resolveToMinAlt(reach, ambigAlts);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return retryWithContext(input, dfa, startIndex, originalContext,
|
return retryWithContext(input, dfa, startIndex, outerContext,
|
||||||
closure, t, reach, ambigAlts);
|
closure, t, reach, ambigAlts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -324,7 +323,7 @@ public class ParserATNSimulator extends ATNSimulator {
|
||||||
|
|
||||||
if ( prevAccept==null ) {
|
if ( prevAccept==null ) {
|
||||||
System.out.println("no viable token at input "+input.LT(1)+", index "+input.index());
|
System.out.println("no viable token at input "+input.LT(1)+", index "+input.index());
|
||||||
NoViableAltException nvae = new NoViableAltException(parser, input, closure, originalContext);
|
NoViableAltException nvae = new NoViableAltException(parser, input, closure, outerContext);
|
||||||
nvae.startIndex = startIndex;
|
nvae.startIndex = startIndex;
|
||||||
throw nvae;
|
throw nvae;
|
||||||
}
|
}
|
||||||
|
@ -446,6 +445,13 @@ public class ParserATNSimulator extends ATNSimulator {
|
||||||
closure(config, configs, closureBusy);
|
closure(config, configs, closureBusy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO: If we are doing predicates, there is no point in pursuing
|
||||||
|
closure operations if we reach a DFA state that uniquely predicts
|
||||||
|
alternative. We will not be caching that DFA state and it is a
|
||||||
|
waste to pursue the closure. Might have to advance when we do
|
||||||
|
ambig detection thought :(
|
||||||
|
*/
|
||||||
|
|
||||||
protected void closure(ATNConfig config,
|
protected void closure(ATNConfig config,
|
||||||
OrderedHashSet<ATNConfig> configs,
|
OrderedHashSet<ATNConfig> configs,
|
||||||
Set<ATNConfig> closureBusy)
|
Set<ATNConfig> closureBusy)
|
||||||
|
@ -488,100 +494,77 @@ public class ParserATNSimulator extends ATNSimulator {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ATNConfig getEpsilonTarget(ATNConfig config, Transition t, boolean ignorePreds) {
|
public ATNConfig getEpsilonTarget(ATNConfig config, Transition t, boolean ignorePreds) {
|
||||||
ATNConfig c = null;
|
|
||||||
if ( t instanceof RuleTransition ) {
|
if ( t instanceof RuleTransition ) {
|
||||||
ATNState p = config.state;
|
return ruleTransition(config, t);
|
||||||
RuleContext newContext;
|
|
||||||
if ( parser != null ) {
|
|
||||||
RuleContext ctx = getCurrentExecContext(config);
|
|
||||||
int argIndex = ((RuleTransition) t).argIndex;
|
|
||||||
int targetRuleIndex = t.target.ruleIndex;
|
|
||||||
if ( debug ) {
|
|
||||||
System.out.println("CALL rule "+parser.getRuleNames()[t.target.ruleIndex]+
|
|
||||||
", arg="+ argIndex +
|
|
||||||
", using context="+ctx);
|
|
||||||
}
|
|
||||||
int fromRuleIndex = config.state.ruleIndex;
|
|
||||||
if ( argIndex>=0 && ctx!=null ) {
|
|
||||||
// we have an argument and we know its context or it's not depedent
|
|
||||||
/*
|
|
||||||
// we're forced to exec args, even if dependent on _localctx.
|
|
||||||
// If no actual context to use, create dummy context to use
|
|
||||||
// for arg eval only.
|
|
||||||
if ( ctx == null ) {
|
|
||||||
// get dummy context for fromRuleIndex
|
|
||||||
ctx = parser.newContext(null, config.state.stateNumber, fromRuleIndex);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
newContext = parser.newContext(ctx, t.target.stateNumber,
|
|
||||||
fromRuleIndex,
|
|
||||||
argIndex);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// there is no argument or there is a dependent arg but
|
|
||||||
// we are unable to identify the proper context
|
|
||||||
newContext = parser.newContext(ctx, t.target.stateNumber, targetRuleIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
newContext.invokingState = p.stateNumber;
|
|
||||||
// System.out.println("new ctx type is "+newContext.getClass().getSimpleName());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
newContext = new RuleContext(config.context, p.stateNumber, t.target.stateNumber);
|
|
||||||
}
|
|
||||||
c = new ATNConfig(config, t.target, newContext);
|
|
||||||
}
|
}
|
||||||
else if ( t instanceof PredicateTransition ) {
|
else if ( t instanceof PredicateTransition ) {
|
||||||
PredicateTransition pt = (PredicateTransition)t;
|
return predTransition(config, (PredicateTransition)t, ignorePreds);
|
||||||
if ( debug ) {
|
|
||||||
System.out.println("PRED (ignore="+ignorePreds+") "+pt.ruleIndex+":"+pt.predIndex+
|
|
||||||
", ctx dependent="+pt.isCtxDependent+
|
|
||||||
", reachesIntoOuterContext="+config.reachesIntoOuterContext);
|
|
||||||
if ( parser != null ) System.out.println("rule surrounding pred is "+
|
|
||||||
parser.getRuleNames()[pt.ruleIndex]);
|
|
||||||
}
|
|
||||||
// preds are epsilon if we're not doing preds (we saw an action).
|
|
||||||
// if we are doing preds, pred must eval to true
|
|
||||||
// Cannot exec preds out of context if they are context dependent
|
|
||||||
RuleContext ctx = getCurrentExecContext(config);
|
|
||||||
boolean ctxIssue = pt.isCtxDependent && config.reachesIntoOuterContext>0;
|
|
||||||
boolean seeThroughPred =
|
|
||||||
ignorePreds || ctxIssue ||
|
|
||||||
(!ctxIssue && parser.sempred(ctx, pt.ruleIndex, pt.predIndex));
|
|
||||||
if ( seeThroughPred ) {
|
|
||||||
c = new ATNConfig(config, t.target);
|
|
||||||
c.traversedPredicate = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if ( t instanceof ActionTransition ) {
|
else if ( t instanceof ActionTransition ) {
|
||||||
c = new ATNConfig(config, t.target);
|
return actionTransition(config, t);
|
||||||
|
}
|
||||||
|
else if ( t.isEpsilon() ) {
|
||||||
|
return new ATNConfig(config, t.target);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ATNConfig actionTransition(ATNConfig config, Transition t) {
|
||||||
ActionTransition at = (ActionTransition)t;
|
ActionTransition at = (ActionTransition)t;
|
||||||
if ( debug ) System.out.println("ACTION edge "+at.ruleIndex+":"+at.actionIndex);
|
if ( debug ) System.out.println("ACTION edge "+at.ruleIndex+":"+at.actionIndex);
|
||||||
if ( at.actionIndex>=0 ) {
|
|
||||||
if ( debug ) System.out.println("DO ACTION "+at.ruleIndex+":"+at.actionIndex);
|
|
||||||
RuleContext ctx = getCurrentExecContext(config);
|
|
||||||
boolean ctxIssue = at.isCtxDependent && config.reachesIntoOuterContext>0;
|
|
||||||
// Only exec forced action that isCtxDependent if we are not
|
|
||||||
// doing global FOLLOW; we don't know context
|
|
||||||
if ( !ctxIssue ) {
|
|
||||||
parser.action(ctx, at.ruleIndex, at.actionIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// non-forced action traversed to get to t.target
|
|
||||||
if ( debug && !config.traversedAction ) {
|
if ( debug && !config.traversedAction ) {
|
||||||
System.out.println("NONFORCED; pruning future pred eval derived from s"+
|
System.out.println("NONFORCED; pruning future pred eval derived from s"+
|
||||||
config.state.stateNumber);
|
config.state.stateNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ATNConfig c = new ATNConfig(config, t.target);
|
||||||
c.traversedAction = true;
|
c.traversedAction = true;
|
||||||
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ATNConfig predTransition(ATNConfig config, PredicateTransition pt,
|
||||||
|
boolean ignorePreds)
|
||||||
|
{
|
||||||
|
if ( debug ) {
|
||||||
|
System.out.println("PRED (ignore="+ignorePreds+") "+pt.ruleIndex+":"+pt.predIndex+
|
||||||
|
", ctx dependent="+pt.isCtxDependent);
|
||||||
|
if ( parser != null ) System.out.println("rule surrounding pred is "+
|
||||||
|
parser.getRuleNames()[pt.ruleIndex]);
|
||||||
}
|
}
|
||||||
else if ( t.isEpsilon() ) {
|
// We know the correct context and exactly one spot: in the original
|
||||||
c = new ATNConfig(config, t.target);
|
// rule that invokes the ATN simulation. We know we are in this rule
|
||||||
|
// when the context stack is empty and we've not dipped into
|
||||||
|
// the outer context.
|
||||||
|
boolean inContext =
|
||||||
|
config.context==RuleContext.EMPTY && config.reachesIntoOuterContext==0;
|
||||||
|
RuleContext ctx = null;
|
||||||
|
if ( inContext ) ctx = outerContext;
|
||||||
|
|
||||||
|
// We see through the predicate if:
|
||||||
|
// 1) we are ignoring them
|
||||||
|
// 2) we aren't ignoring them but it is not context dependent and
|
||||||
|
// pred is true
|
||||||
|
// 3) we aren't ignoring them, it is context dependent, but
|
||||||
|
// we know the context and pred is true
|
||||||
|
ATNConfig c = null;
|
||||||
|
boolean seeThroughPred =
|
||||||
|
ignorePreds ||
|
||||||
|
(!ignorePreds&&!pt.isCtxDependent&&parser.sempred(ctx, pt.ruleIndex, pt.predIndex))||
|
||||||
|
(!ignorePreds&&pt.isCtxDependent&&inContext&&parser.sempred(ctx, pt.ruleIndex, pt.predIndex));
|
||||||
|
if ( seeThroughPred ) {
|
||||||
|
c = new ATNConfig(config, pt.target);
|
||||||
|
c.traversedPredicate = true;
|
||||||
}
|
}
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ATNConfig ruleTransition(ATNConfig config, Transition t) {
|
||||||
|
ATNState p = config.state;
|
||||||
|
RuleContext newContext =
|
||||||
|
new RuleContext(config.context, p.stateNumber, t.target.stateNumber);
|
||||||
|
return new ATNConfig(config, t.target, newContext);
|
||||||
|
}
|
||||||
|
|
||||||
public void reportConflict(int startIndex, int stopIndex, Set<Integer> alts, OrderedHashSet<ATNConfig> configs) {
|
public void reportConflict(int startIndex, int stopIndex, Set<Integer> alts, OrderedHashSet<ATNConfig> configs) {
|
||||||
if ( parser!=null ) parser.reportConflict(startIndex, stopIndex, alts, configs);
|
if ( parser!=null ) parser.reportConflict(startIndex, stopIndex, alts, configs);
|
||||||
}
|
}
|
||||||
|
@ -611,7 +594,7 @@ public class ParserATNSimulator extends ATNSimulator {
|
||||||
public RuleContext getCurrentExecContext(ATNConfig config) {
|
public RuleContext getCurrentExecContext(ATNConfig config) {
|
||||||
RuleContext ctx = config.context; // use context created after entry into interp
|
RuleContext ctx = config.context; // use context created after entry into interp
|
||||||
if ( ctx == RuleContext.EMPTY ) {
|
if ( ctx == RuleContext.EMPTY ) {
|
||||||
if ( config.reachesIntoOuterContext==0 ) ctx = originalContext;
|
if ( config.reachesIntoOuterContext==0 ) ctx = outerContext;
|
||||||
else ctx = null; // no context if we in outer context
|
else ctx = null; // no context if we in outer context
|
||||||
}
|
}
|
||||||
return ctx;
|
return ctx;
|
||||||
|
|
|
@ -35,7 +35,7 @@ import java.util.ArrayList;
|
||||||
<parser>
|
<parser>
|
||||||
>>
|
>>
|
||||||
|
|
||||||
Parser(parser, scopes, funcs, atn, argFuncs, actionFuncs, sempredFuncs) ::= <<
|
Parser(parser, scopes, funcs, atn, sempredFuncs) ::= <<
|
||||||
@SuppressWarnings({"all", "warnings", "unchecked", "unused"})
|
@SuppressWarnings({"all", "warnings", "unchecked", "unused"})
|
||||||
public class <parser.name> extends Parser {
|
public class <parser.name> extends Parser {
|
||||||
public static final int
|
public static final int
|
||||||
|
@ -59,33 +59,22 @@ public class <parser.name> extends Parser {
|
||||||
@Override
|
@Override
|
||||||
public ATN getATN() { return _ATN; }
|
public ATN getATN() { return _ATN; }
|
||||||
|
|
||||||
<dumpActions(parser, argFuncs, actionFuncs, sempredFuncs)>
|
<if(sempredFuncs)>
|
||||||
|
public boolean sempred(RuleContext _localctx, int ruleIndex, int predIndex) {
|
||||||
/** Map a rule index to appropriate RuleContext subclass */
|
switch ( ruleIndex ) {
|
||||||
public RuleContext newContext(RuleContext _localctx, int s, int targetRuleIndex) {
|
<parser.sempredFuncs.values:{f|
|
||||||
switch ( targetRuleIndex ) {
|
case <f.ruleIndex> : return <f.name>_sempred((<f.ctxType>)_localctx, predIndex);}; separator="\n">
|
||||||
<parser.funcs:{rf |
|
|
||||||
case <rf.index> : return new <rf.ctxType>(_localctx, s);}; separator="\n">
|
|
||||||
}
|
}
|
||||||
return null;
|
return true;
|
||||||
}
|
}
|
||||||
|
<sempredFuncs.values; separator="\n">
|
||||||
|
<endif>
|
||||||
|
|
||||||
<atn>
|
<atn>
|
||||||
}
|
}
|
||||||
>>
|
>>
|
||||||
|
|
||||||
dumpActions(recog, argFuncs, actionFuncs, sempredFuncs) ::= <<
|
dumpActions(recog, argFuncs, actionFuncs, sempredFuncs) ::= <<
|
||||||
<if(argFuncs)>
|
|
||||||
/** Create context for a rule reference IN fromRuleIndex using parent _localctx */
|
|
||||||
public RuleContext newContext(RuleContext _localctx, int s, int fromRuleIndex, int actionIndex) {
|
|
||||||
switch ( fromRuleIndex ) {
|
|
||||||
<recog.argFuncs.values:{f|
|
|
||||||
case <f.ruleIndex> : return <f.name>_argEval((<f.ctxType>)_localctx, s, actionIndex);}; separator="\n">
|
|
||||||
}
|
|
||||||
return new RuleContext(_localctx, s);
|
|
||||||
}
|
|
||||||
<argFuncs.values; separator="\n">
|
|
||||||
<endif>
|
|
||||||
<if(actionFuncs)>
|
<if(actionFuncs)>
|
||||||
public void action(RuleContext _localctx, int ruleIndex, int actionIndex) {
|
public void action(RuleContext _localctx, int ruleIndex, int actionIndex) {
|
||||||
switch ( ruleIndex ) {
|
switch ( ruleIndex ) {
|
||||||
|
@ -114,17 +103,6 @@ public <p.name>(TokenStream input) {
|
||||||
}
|
}
|
||||||
>>
|
>>
|
||||||
|
|
||||||
RuleArgFunction(r, actions) ::= <<
|
|
||||||
/** arg computations for rules called FROM and evaluated in context of <r.name> */
|
|
||||||
public RuleContext <r.name>_argEval(<r.ctxType> _localctx, int s, int actionIndex) {
|
|
||||||
switch ( actionIndex ) {
|
|
||||||
<actions:{index|
|
|
||||||
case <index> : return new <r.actions.(index).ctxType>(_localctx, s, <actions.(index)>);}; separator="\n">
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
>>
|
|
||||||
|
|
||||||
RuleActionFunction(r, actions) ::= <<
|
RuleActionFunction(r, actions) ::= <<
|
||||||
public void <r.name>_action(<r.ctxType> _localctx, int actionIndex) {
|
public void <r.name>_action(<r.ctxType> _localctx, int actionIndex) {
|
||||||
switch ( actionIndex ) {
|
switch ( actionIndex ) {
|
||||||
|
|
|
@ -159,26 +159,6 @@ public class OutputModelController {
|
||||||
}
|
}
|
||||||
rsf.actions.put(g.sempreds.get(p), new Action(delegate, p));
|
rsf.actions.put(g.sempreds.get(p), new Action(delegate, p));
|
||||||
}
|
}
|
||||||
else if ( a.getType()==ANTLRParser.FORCED_ACTION ) {
|
|
||||||
RuleActionFunction raf = parser.actionFuncs.get(r);
|
|
||||||
if ( raf==null ) {
|
|
||||||
raf = new RuleActionFunction(delegate, r, function.ctxType);
|
|
||||||
parser.actionFuncs.put(r, raf);
|
|
||||||
}
|
|
||||||
raf.actions.put(g.actions.get(a), new ForcedAction(delegate, a));
|
|
||||||
}
|
|
||||||
else if ( a.getType()==ANTLRParser.ARG_ACTION ) {
|
|
||||||
RuleArgFunction raf = parser.argFuncs.get(r);
|
|
||||||
if ( raf==null ) {
|
|
||||||
raf = new RuleArgFunction(delegate, r, function.ctxType);
|
|
||||||
parser.argFuncs.put(r, raf);
|
|
||||||
}
|
|
||||||
GrammarAST callNode = (GrammarAST)a.getParent();
|
|
||||||
String invokedRuleName = callNode.getText();
|
|
||||||
Rule invokedRule = g.getRule(invokedRuleName);
|
|
||||||
String invokedCtxType = gen.target.getRuleFunctionContextStructName(invokedRule);
|
|
||||||
raf.actions.put(g.actions.get(a), new ArgAction(delegate, a, invokedCtxType));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( function.ruleCtx.isEmpty() ) function.ruleCtx = null;
|
if ( function.ruleCtx.isEmpty() ) function.ruleCtx = null;
|
||||||
|
|
|
@ -44,10 +44,6 @@ public class Parser extends OutputModelObject {
|
||||||
|
|
||||||
@ModelElement public List<RuleFunction> funcs = new ArrayList<RuleFunction>();
|
@ModelElement public List<RuleFunction> funcs = new ArrayList<RuleFunction>();
|
||||||
@ModelElement public SerializedATN atn;
|
@ModelElement public SerializedATN atn;
|
||||||
@ModelElement public LinkedHashMap<Rule, RuleArgFunction> argFuncs =
|
|
||||||
new LinkedHashMap<Rule, RuleArgFunction>();
|
|
||||||
@ModelElement public LinkedHashMap<Rule, RuleActionFunction> actionFuncs =
|
|
||||||
new LinkedHashMap<Rule, RuleActionFunction>();
|
|
||||||
@ModelElement public LinkedHashMap<Rule, RuleSempredFunction> sempredFuncs =
|
@ModelElement public LinkedHashMap<Rule, RuleSempredFunction> sempredFuncs =
|
||||||
new LinkedHashMap<Rule, RuleSempredFunction>();
|
new LinkedHashMap<Rule, RuleSempredFunction>();
|
||||||
|
|
||||||
|
|
|
@ -1,39 +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.codegen.model;
|
|
||||||
|
|
||||||
import org.antlr.v4.codegen.OutputModelFactory;
|
|
||||||
import org.antlr.v4.tool.Rule;
|
|
||||||
|
|
||||||
public class RuleArgFunction extends RuleActionFunction {
|
|
||||||
public RuleArgFunction(OutputModelFactory factory, Rule r, String ctxType) {
|
|
||||||
super(factory, r, ctxType);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -65,10 +65,12 @@ public class TestSemPredEvalParser extends BaseTest {
|
||||||
assertEquals(expecting, found);
|
assertEquals(expecting, found);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** In this case, we use predicates that depend on global information
|
||||||
|
* like we would do for a symbol table. We simply execute
|
||||||
|
* the predicates assuming that all necessary information is available.
|
||||||
|
* The i++ action is done outside of the prediction and so it is executed.
|
||||||
|
*/
|
||||||
@Test public void testToLeftWithVaryingPredicate() throws Exception {
|
@Test public void testToLeftWithVaryingPredicate() throws Exception {
|
||||||
// alternate predicted alt to ensure DFA doesn't cache
|
|
||||||
// must use forced action since i++ must exec; FOLLOW(a) sees
|
|
||||||
// both preds since it loops around in s.
|
|
||||||
String grammar =
|
String grammar =
|
||||||
"grammar T;\n" +
|
"grammar T;\n" +
|
||||||
"@members {int i=0;}\n" +
|
"@members {int i=0;}\n" +
|
||||||
|
@ -92,6 +94,63 @@ public class TestSemPredEvalParser extends BaseTest {
|
||||||
assertEquals(expecting, found);
|
assertEquals(expecting, found);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In this case, we're passing a parameter into a rule that uses that
|
||||||
|
* information to predict the alternatives. This is the special case
|
||||||
|
* where we know exactly which context we are in. The context stack
|
||||||
|
* is empty and we have not dipped into the outer context to make a decision.
|
||||||
|
*/
|
||||||
|
@Test public void testPredicateDependentOnArg() throws Exception {
|
||||||
|
String grammar =
|
||||||
|
"grammar T;\n" +
|
||||||
|
"@members {int i=0;}\n" +
|
||||||
|
"s : a[2] a[1];\n" +
|
||||||
|
"a[int i]" +
|
||||||
|
" : {$i==1}? ID {System.out.println(\"alt 1\");}\n" +
|
||||||
|
" | {$i==2}? ID {System.out.println(\"alt 2\");}\n" +
|
||||||
|
" ;\n" +
|
||||||
|
"ID : 'a'..'z'+ ;\n" +
|
||||||
|
"INT : '0'..'9'+;\n" +
|
||||||
|
"WS : (' '|'\\n') {skip();} ;\n";
|
||||||
|
|
||||||
|
String found = execParser("T.g", grammar, "TParser", "TLexer", "s",
|
||||||
|
"a b", false);
|
||||||
|
String expecting =
|
||||||
|
"alt 2\n" +
|
||||||
|
"alt 1\n";
|
||||||
|
assertEquals(expecting, found);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** In this case, we have to ensure that the predicates are not
|
||||||
|
tested during the closure after recognizing the 1st ID. The
|
||||||
|
closure will fall off the end of 'a' 1st time and reach into the
|
||||||
|
a[1] rule invocation. It should not execute predicates because it
|
||||||
|
does not know what the parameter is. The context stack will not
|
||||||
|
be empty and so they should be ignored. It will not affect
|
||||||
|
recognition, however. We are really making sure the ATN
|
||||||
|
simulation doesn't crash with context object issues when it
|
||||||
|
encounters preds during FOLLOW.
|
||||||
|
*/
|
||||||
|
@Test public void testPredicateDependentOnArg2() throws Exception {
|
||||||
|
String grammar =
|
||||||
|
"grammar T;\n" +
|
||||||
|
"@members {int i=0;}\n" +
|
||||||
|
"s : a[2] a[1];\n" +
|
||||||
|
"a[int i]" +
|
||||||
|
" : {$i==1}? ID\n" +
|
||||||
|
" | {$i==2}? ID\n" +
|
||||||
|
" ;\n" +
|
||||||
|
"ID : 'a'..'z'+ ;\n" +
|
||||||
|
"INT : '0'..'9'+;\n" +
|
||||||
|
"WS : (' '|'\\n') {skip();} ;\n";
|
||||||
|
|
||||||
|
String found = execParser("T.g", grammar, "TParser", "TLexer", "s",
|
||||||
|
"a b", false);
|
||||||
|
String expecting =
|
||||||
|
"";
|
||||||
|
assertEquals(expecting, found);
|
||||||
|
}
|
||||||
|
|
||||||
@Test public void testToRightWithVaryingPredicate() throws Exception {
|
@Test public void testToRightWithVaryingPredicate() throws Exception {
|
||||||
// alternate predicted alt to ensure DFA doesn't cache
|
// alternate predicted alt to ensure DFA doesn't cache
|
||||||
String grammar =
|
String grammar =
|
||||||
|
@ -117,238 +176,6 @@ public class TestSemPredEvalParser extends BaseTest {
|
||||||
assertEquals(expecting, found);
|
assertEquals(expecting, found);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The ATN simulator ignores the unforced action but sees the forced
|
|
||||||
* actions at the start of both alternatives. Further, during closure,
|
|
||||||
* it sees the 2nd forced action s1b in the 1st alternative. Once parsing
|
|
||||||
* begins, it executes the "parse" action as well as the forced actions
|
|
||||||
* in the 1st alternative.
|
|
||||||
*/
|
|
||||||
@Test public void testForcedAction() throws Exception {
|
|
||||||
String grammar =
|
|
||||||
"grammar T;\n" +
|
|
||||||
"@members {" +
|
|
||||||
"void f(Object s) {System.out.println(s);}\n" +
|
|
||||||
"}\n" +
|
|
||||||
"s : {f(\"parse\");} {{f(\"s1a\");}} ID {{f(\"s1b\");}} | {{f(\"s2\");}} ID ;\n" +
|
|
||||||
"ID : 'a'..'z'+ ;\n" +
|
|
||||||
"INT : '0'..'9'+;\n" +
|
|
||||||
"WS : (' '|'\\n') {skip();} ;\n";
|
|
||||||
|
|
||||||
String found = execParser("T.g", grammar, "TParser", "TLexer", "s",
|
|
||||||
"x x y", false);
|
|
||||||
String expecting =
|
|
||||||
"s1a\n" +
|
|
||||||
"s2\n" +
|
|
||||||
"s1b\n" +
|
|
||||||
"parse\n" +
|
|
||||||
"s1a\n" +
|
|
||||||
"s1b\n";
|
|
||||||
assertEquals(expecting, found);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** To distinguish the alternatives of rule e, we compute FOLLOW(e),
|
|
||||||
* which includes all tokens that can be matched following all
|
|
||||||
* references to e. In this case, it sees two forced actions after
|
|
||||||
* references to e that it must execute.
|
|
||||||
*/
|
|
||||||
@Test public void testForcedActionInGlobalFOLLOW() throws Exception {
|
|
||||||
String grammar =
|
|
||||||
"grammar T;\n" +
|
|
||||||
"@members {" +
|
|
||||||
"void f(Object s) {System.out.println(s);}\n" +
|
|
||||||
"}\n" +
|
|
||||||
"s : e {{f(\"s1\");}} {f(\"alt 1\");} '!' ;\n" +
|
|
||||||
"t : e {{f(\"t1\");}} ID ;\n" +
|
|
||||||
"e : ID | ;\n" + // non-LL(1) so we use ATN
|
|
||||||
"ID : 'a'..'z'+ ;\n" +
|
|
||||||
"INT : '0'..'9'+;\n" +
|
|
||||||
"WS : (' '|'\\n') {skip();} ;\n";
|
|
||||||
|
|
||||||
String found = execParser("T.g", grammar, "TParser", "TLexer", "s",
|
|
||||||
"a!", false);
|
|
||||||
String expecting =
|
|
||||||
"s1\n" + // do s1, t1 once during s0 computation from epsilon edge in e
|
|
||||||
"t1\n" +
|
|
||||||
"s1\n" + // do them again during closure after passing ID in e
|
|
||||||
"t1\n" +
|
|
||||||
"s1\n" + // now we are parsing
|
|
||||||
"alt 1\n";
|
|
||||||
assertEquals(expecting, found);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Actions that depend on local scope information such as local
|
|
||||||
* variables and arguments are executed if we are sure we have the
|
|
||||||
* right RuleContext object. Have the correct context for any rule
|
|
||||||
* that we invoke during ATN simulation (unless we fall off the edge
|
|
||||||
* of the initial rule into the outer context. see other unit
|
|
||||||
* tests). In this case, the forced actions are accessing the
|
|
||||||
* argument of the surrounding rule so everything is okay.
|
|
||||||
*/
|
|
||||||
@Test public void testForcedDepedentActionInContext() throws Exception {
|
|
||||||
String grammar =
|
|
||||||
"grammar T;\n" +
|
|
||||||
"@members {" +
|
|
||||||
"void f(Object s) {System.out.println(s);}\n" +
|
|
||||||
"}\n" +
|
|
||||||
"s : a[99] ;\n" +
|
|
||||||
"a[int i] : {f(\"parse\");} {{f($i);}} ID {{f(\"s1b\");}} | {{f($i+1);}} ID ;\n" +
|
|
||||||
"ID : 'a'..'z'+ ;\n" +
|
|
||||||
"INT : '0'..'9'+;\n" +
|
|
||||||
"WS : (' '|'\\n') {skip();} ;\n";
|
|
||||||
|
|
||||||
String found = execParser("T.g", grammar, "TParser", "TLexer", "s",
|
|
||||||
"x x y", false);
|
|
||||||
String expecting =
|
|
||||||
"99\n" +
|
|
||||||
"100\n" +
|
|
||||||
"s1b\n" +
|
|
||||||
"parse\n" +
|
|
||||||
"99\n" +
|
|
||||||
"s1b\n";
|
|
||||||
assertEquals(expecting, found);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** In this case, we also know what the context is for $i. During
|
|
||||||
* ATN simulation, rule a invokes rule b which creates the correct context.
|
|
||||||
*/
|
|
||||||
@Test public void testForcedDepedentActionInContext2() throws Exception {
|
|
||||||
String grammar =
|
|
||||||
"grammar T;\n" +
|
|
||||||
"@members {" +
|
|
||||||
"void f(Object s) {System.out.println(s);}\n" +
|
|
||||||
"}\n" +
|
|
||||||
"s : a ;\n" +
|
|
||||||
"a : {f(\"parse\");} b {{f(\"s1b\");}} | b ;\n" +
|
|
||||||
"b returns [int i=32] : {{f($i);}} ID ;\n" +
|
|
||||||
"ID : 'a'..'z'+ ;\n" +
|
|
||||||
"INT : '0'..'9'+;\n" +
|
|
||||||
"WS : (' '|'\\n') {skip();} ;\n";
|
|
||||||
|
|
||||||
String found = execParser("T.g", grammar, "TParser", "TLexer", "s",
|
|
||||||
"x x y", false);
|
|
||||||
String expecting =
|
|
||||||
"32\n" +
|
|
||||||
"32\n" +
|
|
||||||
"s1b\n" +
|
|
||||||
"parse\n" +
|
|
||||||
"32\n" +
|
|
||||||
"s1b\n";
|
|
||||||
assertEquals(expecting, found);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** We must execute all arguments for rules that we invoked during ATN
|
|
||||||
* simulation. In this case, the evaluation of the arguments is clearly
|
|
||||||
* okay because the arguments are not dependent on local context.
|
|
||||||
*/
|
|
||||||
@Test public void testArgEval() throws Exception {
|
|
||||||
String grammar =
|
|
||||||
"grammar T;\n" +
|
|
||||||
"@members {" +
|
|
||||||
"void f(Object s) {System.out.println(s);}\n" +
|
|
||||||
"}\n" +
|
|
||||||
"s : {f(\"parse\");} b[1] {{f(\"s1b\");}} | b[2] ;\n" +
|
|
||||||
"b[int i] : {{f($i);}} ID ;\n" +
|
|
||||||
"ID : 'a'..'z'+ ;\n" +
|
|
||||||
"INT : '0'..'9'+;\n" +
|
|
||||||
"WS : (' '|'\\n') {skip();} ;\n";
|
|
||||||
|
|
||||||
String found = execParser("T.g", grammar, "TParser", "TLexer", "s",
|
|
||||||
"x x y", false);
|
|
||||||
String expecting =
|
|
||||||
"1\n" +
|
|
||||||
"2\n" +
|
|
||||||
"s1b\n" +
|
|
||||||
"parse\n" +
|
|
||||||
"1\n" +
|
|
||||||
"s1b\n";
|
|
||||||
assertEquals(expecting, found);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The arguments to rule b access $i, which makes it dependent upon
|
|
||||||
* the local context. It's okay, because we are in the proper spot.
|
|
||||||
*/
|
|
||||||
@Test public void testDepedentArgEval() throws Exception {
|
|
||||||
String grammar =
|
|
||||||
"grammar T;\n" +
|
|
||||||
"@members {" +
|
|
||||||
"void f(Object s) {System.out.println(s);}\n" +
|
|
||||||
"}\n" +
|
|
||||||
"s : a[1] ;\n" +
|
|
||||||
"a[int i] : {f(\"parse\");} b[$i] {{f(\"s1b\");}} | b[$i+1] ;\n" +
|
|
||||||
"b[int i] : {{f($i);}} ID ;\n" +
|
|
||||||
"ID : 'a'..'z'+ ;\n" +
|
|
||||||
"INT : '0'..'9'+;\n" +
|
|
||||||
"WS : (' '|'\\n') {skip();} ;\n";
|
|
||||||
|
|
||||||
String found = execParser("T.g", grammar, "TParser", "TLexer", "s",
|
|
||||||
"x x y", false);
|
|
||||||
String expecting =
|
|
||||||
"1\n" +
|
|
||||||
"2\n" +
|
|
||||||
"s1b\n" +
|
|
||||||
"parse\n" +
|
|
||||||
"1\n" +
|
|
||||||
"s1b\n";
|
|
||||||
assertEquals(expecting, found);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The call chain is s a e but since e is optional, we invoke the
|
|
||||||
* global follow of e. This runs into the foo rule invocations, both of
|
|
||||||
* which have parameters that are dependent upon the local context.
|
|
||||||
* Because we can never be sure if we are in the proper context (without
|
|
||||||
* using full LL(*) context parsing), we can't ever execute dependent
|
|
||||||
* actions encountered during global follows. We create an uninitialized
|
|
||||||
* foo_ctx object for use while chasing edges in foo.
|
|
||||||
*/
|
|
||||||
@Test public void testDepedentArgEvalInGlobalFOLLOW() throws Exception {
|
|
||||||
String grammar =
|
|
||||||
"grammar T;\n" +
|
|
||||||
"@members {" +
|
|
||||||
"int f(int s) {System.out.println(s); return s;}\n" +
|
|
||||||
"}\n" +
|
|
||||||
"s : a[1] ;\n" +
|
|
||||||
"a[int i] : e foo[f($i)] ;\n" +
|
|
||||||
"b[int i] : e foo[f($i+1)] ;\n" +
|
|
||||||
"e : ID | ;\n" +
|
|
||||||
"foo[int k] : ID ';' ;\n" +
|
|
||||||
"ID : 'a'..'z'+ ;\n" +
|
|
||||||
"INT : '0'..'9'+;\n" +
|
|
||||||
"WS : (' '|'\\n') {skip();} ;\n";
|
|
||||||
|
|
||||||
String found = execParser("T.g", grammar, "TParser", "TLexer", "s",
|
|
||||||
"x y;", false);
|
|
||||||
String expecting =
|
|
||||||
"1\n"; // f($i) executed only during real parse during call to foo.
|
|
||||||
assertEquals(expecting, found);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Same as the previous test except that we have dependent forced
|
|
||||||
* actions in the global fall not dependent rule arguments.
|
|
||||||
*/
|
|
||||||
@Test public void testForcedDepedentActionInGlobalFOLLOW() throws Exception {
|
|
||||||
String grammar =
|
|
||||||
"grammar T;\n" +
|
|
||||||
"@members {" +
|
|
||||||
"int f(int s) {System.out.println(s); return s;}\n" +
|
|
||||||
"}\n" +
|
|
||||||
"s : a[1] ;\n" +
|
|
||||||
"a[int i] : e {{f($i);}} ID '!' ;\n" +
|
|
||||||
"b[int i] : e {{f($i+1);}} ID '?' ;\n" +
|
|
||||||
"e : ID | ;\n" +
|
|
||||||
"foo[int k] : ID ';' ;\n" +
|
|
||||||
"ID : 'a'..'z'+ ;\n" +
|
|
||||||
"INT : '0'..'9'+;\n" +
|
|
||||||
"WS : (' '|'\\n') {skip();} ;\n";
|
|
||||||
|
|
||||||
String found = execParser("T.g", grammar, "TParser", "TLexer", "s",
|
|
||||||
"x y !", false);
|
|
||||||
String expecting =
|
|
||||||
"1\n";
|
|
||||||
assertEquals(expecting, found);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** During a global follow operation, we still execute semantic
|
/** During a global follow operation, we still execute semantic
|
||||||
* predicates as long as they are not dependent on local context
|
* predicates as long as they are not dependent on local context
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue