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:
parrt 2011-07-31 11:58:59 -08:00
parent 328444244f
commit a7f2bc31ac
7 changed files with 166 additions and 441 deletions
runtime/Java/src/org/antlr/v4/runtime
tool
resources/org/antlr/v4/tool/templates/codegen/Java
src/org/antlr/v4/codegen
test/org/antlr/v4/test

View File

@ -229,15 +229,15 @@ public class Recognizer<ATNInterpreter> {
/** Create context for a rule reference IN fromRuleIndex using parent _localctx.
* Used only when there are arguments to the rule function. _localctx
* must be correct context for fromRuleIndex.
*/
public RuleContext newContext(RuleContext _localctx, int s, int fromRuleIndex, int actionIndex) {
return new ParserRuleContext(_localctx, s);
}
*/
/** Map a rule index to appropriate RuleContext subclass. Used when rule
* has no arguments.
*/
public RuleContext newContext(RuleContext _localctx, int s, int targetRuleIndex) {
return new ParserRuleContext(_localctx, s);
}
*/
}

View File

@ -62,7 +62,7 @@ public class ParserATNSimulator extends ATNSimulator {
*
* The full stack at any moment is [config.outerContext + config.context].
*/
protected RuleContext originalContext;
protected RuleContext outerContext;
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()));
}
public int adaptivePredict(TokenStream input, int decision, RuleContext originalContext) {
public int adaptivePredict(TokenStream input, int decision, RuleContext outerContext) {
predict_calls++;
DFA dfa = decisionToDFA[decision];
if ( dfa==null || dfa.s0==null ) {
ATNState startState = atn.decisionToState.get(decision);
decisionToDFA[decision] = dfa = new DFA(startState);
dfa.decision = decision;
return predictATN(dfa, input, decision, originalContext, false);
return predictATN(dfa, input, decision, outerContext, false);
}
else {
//dump(dfa);
// start with the DFA
int m = input.mark();
int alt = execDFA(input, dfa, dfa.s0, originalContext);
int alt = execDFA(input, dfa, dfa.s0, outerContext);
input.seek(m);
return alt;
}
@ -103,13 +103,13 @@ public class ParserATNSimulator extends ATNSimulator {
public int predictATN(DFA dfa, TokenStream input,
int decision,
RuleContext originalContext,
RuleContext outerContext,
boolean useContext)
{
if ( originalContext==null ) originalContext = RuleContext.EMPTY;
this.originalContext = originalContext;
if ( outerContext==null ) outerContext = RuleContext.EMPTY;
this.outerContext = outerContext;
RuleContext ctx = RuleContext.EMPTY;
if ( useContext ) ctx = originalContext;
if ( useContext ) ctx = outerContext;
OrderedHashSet<ATNConfig> s0_closure =
computeStartState(dfa.atnStartState, ctx);
dfa.s0 = addDFAState(dfa, s0_closure);
@ -121,7 +121,7 @@ public class ParserATNSimulator extends ATNSimulator {
int alt = 0;
int m = input.mark();
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; }
finally {
@ -136,14 +136,14 @@ public class ParserATNSimulator extends ATNSimulator {
DFA dfa = new DFA(startState);
RuleContext ctx = new ParserRuleContext();
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));
// dump(dfa);
if ( originalContext==null ) originalContext = RuleContext.EMPTY;
this.originalContext = originalContext;
if ( outerContext==null ) outerContext = RuleContext.EMPTY;
this.outerContext = outerContext;
DFAState prevAcceptState = null;
DFAState s = s0;
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);
// TODO: ctxSensitive
if ( s.isCtxSensitive ) {
Integer predI = s.ctxToPrediction.get(originalContext);
if ( dfa_debug ) System.out.println("ctx sensitive state "+originalContext+"->"+predI+
Integer predI = s.ctxToPrediction.get(outerContext);
if ( dfa_debug ) System.out.println("ctx sensitive state "+outerContext+"->"+predI+
" in "+s);
if ( predI!=null ) return predI;
// System.out.println("start all over with ATN; can't use DFA");
// start all over with ATN; can't use DFA
input.seek(start);
DFA throwAwayDFA = new DFA(dfa.atnStartState);
int alt = execATN(input, throwAwayDFA, start, s0.configs, originalContext, false);
s.ctxToPrediction.put(originalContext, alt);
int alt = execATN(input, throwAwayDFA, start, s0.configs, false);
s.ctxToPrediction.put(outerContext, alt);
return alt;
}
if ( s.isAcceptState ) {
@ -182,7 +182,7 @@ public class ParserATNSimulator extends ATNSimulator {
" at DFA state "+s.stateNumber);
}
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
// same alt; e.g., s0-A->:s1=>2-B->:s2=>2
// TODO: This next stuff kills edge, but extra states remain. :(
@ -229,7 +229,6 @@ public class ParserATNSimulator extends ATNSimulator {
DFA dfa,
int startIndex,
OrderedHashSet<ATNConfig> s0,
RuleContext originalContext,
boolean useContext)
{
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);
if ( ambigAlts!=null ) {
if ( debug ) {
ATNState loc = atn.states.get(originalContext.s);
ATNState loc = atn.states.get(outerContext.s);
String rname = "n/a";
if ( parser !=null ) rname = parser.getRuleNames()[loc.ruleIndex];
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
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];
// 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()));
if ( !userWantsCtxSensitive || useContext ) {
resolveToMinAlt(reach, ambigAlts);
}
else {
return retryWithContext(input, dfa, startIndex, originalContext,
return retryWithContext(input, dfa, startIndex, outerContext,
closure, t, reach, ambigAlts);
}
}
@ -324,7 +323,7 @@ public class ParserATNSimulator extends ATNSimulator {
if ( prevAccept==null ) {
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;
throw nvae;
}
@ -446,6 +445,13 @@ public class ParserATNSimulator extends ATNSimulator {
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,
OrderedHashSet<ATNConfig> configs,
Set<ATNConfig> closureBusy)
@ -488,100 +494,77 @@ public class ParserATNSimulator extends ATNSimulator {
}
public ATNConfig getEpsilonTarget(ATNConfig config, Transition t, boolean ignorePreds) {
ATNConfig c = null;
if ( t instanceof RuleTransition ) {
ATNState p = config.state;
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);
return ruleTransition(config, t);
}
else if ( t instanceof PredicateTransition ) {
PredicateTransition pt = (PredicateTransition)t;
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;
}
return predTransition(config, (PredicateTransition)t, ignorePreds);
}
else if ( t instanceof ActionTransition ) {
c = new ATNConfig(config, t.target);
ActionTransition at = (ActionTransition)t;
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 ) {
System.out.println("NONFORCED; pruning future pred eval derived from s"+
config.state.stateNumber);
}
c.traversedAction = true;
}
return actionTransition(config, t);
}
else if ( t.isEpsilon() ) {
c = new ATNConfig(config, t.target);
return new ATNConfig(config, t.target);
}
return null;
}
public ATNConfig actionTransition(ATNConfig config, Transition t) {
ActionTransition at = (ActionTransition)t;
if ( debug ) System.out.println("ACTION edge "+at.ruleIndex+":"+at.actionIndex);
if ( debug && !config.traversedAction ) {
System.out.println("NONFORCED; pruning future pred eval derived from s"+
config.state.stateNumber);
}
ATNConfig c = new ATNConfig(config, t.target);
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]);
}
// We know the correct context and exactly one spot: in the original
// 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;
}
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) {
if ( parser!=null ) parser.reportConflict(startIndex, stopIndex, alts, configs);
}
@ -611,7 +594,7 @@ public class ParserATNSimulator extends ATNSimulator {
public RuleContext getCurrentExecContext(ATNConfig config) {
RuleContext ctx = config.context; // use context created after entry into interp
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
}
return ctx;

View File

@ -35,7 +35,7 @@ import java.util.ArrayList;
<parser>
>>
Parser(parser, scopes, funcs, atn, argFuncs, actionFuncs, sempredFuncs) ::= <<
Parser(parser, scopes, funcs, atn, sempredFuncs) ::= <<
@SuppressWarnings({"all", "warnings", "unchecked", "unused"})
public class <parser.name> extends Parser {
public static final int
@ -59,33 +59,22 @@ public class <parser.name> extends Parser {
@Override
public ATN getATN() { return _ATN; }
<dumpActions(parser, argFuncs, actionFuncs, sempredFuncs)>
/** Map a rule index to appropriate RuleContext subclass */
public RuleContext newContext(RuleContext _localctx, int s, int targetRuleIndex) {
switch ( targetRuleIndex ) {
<parser.funcs:{rf |
case <rf.index> : return new <rf.ctxType>(_localctx, s);}; separator="\n">
}
return null;
}
<if(sempredFuncs)>
public boolean sempred(RuleContext _localctx, int ruleIndex, int predIndex) {
switch ( ruleIndex ) {
<parser.sempredFuncs.values:{f|
case <f.ruleIndex> : return <f.name>_sempred((<f.ctxType>)_localctx, predIndex);}; separator="\n">
}
return true;
}
<sempredFuncs.values; separator="\n">
<endif>
<atn>
}
>>
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)>
public void action(RuleContext _localctx, int ruleIndex, int actionIndex) {
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) ::= <<
public void <r.name>_action(<r.ctxType> _localctx, int actionIndex) {
switch ( actionIndex ) {

View File

@ -159,26 +159,6 @@ public class OutputModelController {
}
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;

View File

@ -44,10 +44,6 @@ public class Parser extends OutputModelObject {
@ModelElement public List<RuleFunction> funcs = new ArrayList<RuleFunction>();
@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 =
new LinkedHashMap<Rule, RuleSempredFunction>();

View File

@ -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);
}
}

View File

@ -65,10 +65,12 @@ public class TestSemPredEvalParser extends BaseTest {
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 {
// 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 =
"grammar T;\n" +
"@members {int i=0;}\n" +
@ -92,6 +94,63 @@ public class TestSemPredEvalParser extends BaseTest {
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 {
// alternate predicted alt to ensure DFA doesn't cache
String grammar =
@ -117,238 +176,6 @@ public class TestSemPredEvalParser extends BaseTest {
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
* predicates as long as they are not dependent on local context
*/