forked from jasder/antlr
adding new files
[git-p4: depot-paths = "//depot/code/antlr4/main/": change = 8659]
This commit is contained in:
parent
2ddeb7c769
commit
061fff09f9
|
@ -27,7 +27,6 @@
|
||||||
*/
|
*/
|
||||||
package org.antlr.v4.runtime;
|
package org.antlr.v4.runtime;
|
||||||
|
|
||||||
import org.antlr.v4.runtime.misc.LABitSet;
|
|
||||||
|
|
||||||
/** A parser for TokenStreams. "parser grammars" result in a subclass
|
/** A parser for TokenStreams. "parser grammars" result in a subclass
|
||||||
* of this.
|
* of this.
|
||||||
|
@ -53,8 +52,7 @@ public class Parser extends BaseRecognizer {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Object getMissingSymbol(RecognitionException e,
|
protected Object getMissingSymbol(RecognitionException e,
|
||||||
int expectedTokenType,
|
int expectedTokenType)
|
||||||
LABitSet follow)
|
|
||||||
{
|
{
|
||||||
String tokenText = null;
|
String tokenText = null;
|
||||||
if ( expectedTokenType== Token.EOF ) tokenText = "<missing EOF>";
|
if ( expectedTokenType== Token.EOF ) tokenText = "<missing EOF>";
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package org.antlr.v4.runtime.atn;
|
package org.antlr.v4.runtime.atn;
|
||||||
|
|
||||||
import org.antlr.v4.automata.ATNSerializer;
|
import org.antlr.v4.misc.IntervalSet;
|
||||||
import org.antlr.v4.misc.*;
|
|
||||||
import org.antlr.v4.runtime.RuleContext;
|
import org.antlr.v4.runtime.RuleContext;
|
||||||
import org.antlr.v4.tool.*;
|
import org.antlr.v4.tool.*;
|
||||||
|
|
||||||
|
@ -85,22 +84,4 @@ public class ATN {
|
||||||
public int getNumberOfDecisions() {
|
public int getNumberOfDecisions() {
|
||||||
return decisionToATNState.size();
|
return decisionToATNState.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Used by Java target to encode short/int array as chars in string. */
|
|
||||||
public String getSerializedAsString() {
|
|
||||||
return new String(Utils.toCharArray(getSerialized()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Integer> getSerialized() {
|
|
||||||
return new ATNSerializer(this).serialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
public char[] getSerializedAsChars() {
|
|
||||||
return Utils.toCharArray(new ATNSerializer(this).serialize());
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDecoded() {
|
|
||||||
return new ATNSerializer(this).decode(Utils.toCharArray(getSerialized()));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,7 @@ package org.antlr.v4.runtime.atn;
|
||||||
|
|
||||||
import org.antlr.v4.misc.*;
|
import org.antlr.v4.misc.*;
|
||||||
import org.antlr.v4.parse.ANTLRParser;
|
import org.antlr.v4.parse.ANTLRParser;
|
||||||
import org.antlr.v4.runtime.dfa.*;
|
import org.antlr.v4.runtime.dfa.DFAState;
|
||||||
import org.antlr.v4.tool.Grammar;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
@ -138,6 +137,7 @@ public abstract class ATNInterpreter {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
public static void dump(DFA dfa, Grammar g) {
|
public static void dump(DFA dfa, Grammar g) {
|
||||||
DOTGenerator dot = new DOTGenerator(g);
|
DOTGenerator dot = new DOTGenerator(g);
|
||||||
String output = dot.getDOT(dfa, false);
|
String output = dot.getDOT(dfa, false);
|
||||||
|
@ -147,4 +147,5 @@ public abstract class ATNInterpreter {
|
||||||
public static void dump(DFA dfa) {
|
public static void dump(DFA dfa) {
|
||||||
dump(dfa, null);
|
dump(dfa, null);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,10 +22,6 @@ public class ActionTransition extends Transition {
|
||||||
return true; // we are to be ignored by analysis 'cept for predicates
|
return true; // we are to be ignored by analysis 'cept for predicates
|
||||||
}
|
}
|
||||||
|
|
||||||
public int compareTo(Object o) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
if ( actionAST!=null ) return "{"+actionAST.getText()+"}";
|
if ( actionAST!=null ) return "{"+actionAST.getText()+"}";
|
||||||
return "action_"+ruleIndex+":"+actionIndex;
|
return "action_"+ruleIndex+":"+actionIndex;
|
||||||
|
|
|
@ -19,28 +19,6 @@ public class AtomTransition extends Transition {
|
||||||
|
|
||||||
public IntervalSet label() { return IntervalSet.of(label); }
|
public IntervalSet label() { return IntervalSet.of(label); }
|
||||||
|
|
||||||
public int hashCode() { return label; }
|
|
||||||
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if ( o==null ) return false;
|
|
||||||
if ( this == o ) return true; // equals if same object
|
|
||||||
if ( o.getClass() == SetTransition.class ) {
|
|
||||||
return IntervalSet.of(label).equals(o);
|
|
||||||
}
|
|
||||||
return label!=((AtomTransition)o).label;
|
|
||||||
}
|
|
||||||
|
|
||||||
// public boolean intersect(Label other) {
|
|
||||||
// if ( other.getClass() == AtomTransition.class ) {
|
|
||||||
// return label==((AtomTransition)other).label;
|
|
||||||
// }
|
|
||||||
// return ((SetLabel)other).label.member(this.label);
|
|
||||||
// }
|
|
||||||
|
|
||||||
public int compareTo(Object o) {
|
|
||||||
return this.label-((AtomTransition)o).label;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString(Grammar g) {
|
public String toString(Grammar g) {
|
||||||
if (g!=null ) return g.getTokenDisplayName(label);
|
if (g!=null ) return g.getTokenDisplayName(label);
|
||||||
|
|
|
@ -7,10 +7,6 @@ public class EpsilonTransition extends Transition {
|
||||||
|
|
||||||
public boolean isEpsilon() { return true; }
|
public boolean isEpsilon() { return true; }
|
||||||
|
|
||||||
public int compareTo(Object o) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString(Grammar g) {
|
public String toString(Grammar g) {
|
||||||
return "epsilon";
|
return "epsilon";
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
package org.antlr.v4.runtime.atn;
|
package org.antlr.v4.runtime.atn;
|
||||||
|
|
||||||
import org.antlr.v4.misc.IntervalSet;
|
import org.antlr.v4.misc.IntervalSet;
|
||||||
import org.antlr.v4.tool.Grammar;
|
import org.antlr.v4.tool.*;
|
||||||
import org.antlr.v4.tool.GrammarAST;
|
|
||||||
|
|
||||||
public class NotSetTransition extends SetTransition {
|
public class NotSetTransition extends SetTransition {
|
||||||
public NotSetTransition(GrammarAST ast, IntervalSet label, ATNState target) {
|
public NotSetTransition(GrammarAST ast, IntervalSet label, ATNState target) {
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package org.antlr.v4.runtime.atn;
|
package org.antlr.v4.runtime.atn;
|
||||||
|
|
||||||
import org.antlr.v4.analysis.SemanticContext;
|
import org.antlr.v4.tool.*;
|
||||||
import org.antlr.v4.tool.Grammar;
|
|
||||||
import org.antlr.v4.tool.GrammarAST;
|
|
||||||
|
|
||||||
/** TODO: this is old comment:
|
/** TODO: this is old comment:
|
||||||
* A tree of semantic predicates from the grammar AST if label==SEMPRED.
|
* A tree of semantic predicates from the grammar AST if label==SEMPRED.
|
||||||
|
@ -14,12 +12,10 @@ public class PredicateTransition extends Transition {
|
||||||
public int ruleIndex;
|
public int ruleIndex;
|
||||||
public int predIndex;
|
public int predIndex;
|
||||||
public GrammarAST predAST;
|
public GrammarAST predAST;
|
||||||
public SemanticContext semanticContext;
|
|
||||||
|
|
||||||
public PredicateTransition(GrammarAST predicateASTNode, ATNState target) {
|
public PredicateTransition(GrammarAST predicateASTNode, ATNState target) {
|
||||||
super(target);
|
super(target);
|
||||||
this.predAST = predicateASTNode;
|
this.predAST = predicateASTNode;
|
||||||
this.semanticContext = new SemanticContext.Predicate(predicateASTNode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public PredicateTransition(ATNState target, int ruleIndex, int predIndex) {
|
public PredicateTransition(ATNState target, int ruleIndex, int predIndex) {
|
||||||
|
@ -30,29 +26,7 @@ public class PredicateTransition extends Transition {
|
||||||
|
|
||||||
public boolean isEpsilon() { return true; }
|
public boolean isEpsilon() { return true; }
|
||||||
|
|
||||||
public int compareTo(Object o) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int hashCode() {
|
|
||||||
return semanticContext.hashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if ( o==null ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( this == o ) {
|
|
||||||
return true; // equals if same object
|
|
||||||
}
|
|
||||||
if ( !(o instanceof PredicateTransition) ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return semanticContext.equals(((PredicateTransition)o).semanticContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
if ( semanticContext!=null ) return semanticContext.toString();
|
|
||||||
if ( predAST!=null ) return predAST.getText();
|
if ( predAST!=null ) return predAST.getText();
|
||||||
return "pred-"+ruleIndex+":"+predIndex;
|
return "pred-"+ruleIndex+":"+predIndex;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package org.antlr.v4.runtime.atn;
|
package org.antlr.v4.runtime.atn;
|
||||||
|
|
||||||
import org.antlr.v4.misc.CharSupport;
|
import org.antlr.v4.misc.*;
|
||||||
import org.antlr.v4.misc.IntervalSet;
|
|
||||||
|
|
||||||
public class RangeTransition extends Transition {
|
public class RangeTransition extends Transition {
|
||||||
public int from;
|
public int from;
|
||||||
|
@ -15,10 +14,6 @@ public class RangeTransition extends Transition {
|
||||||
super(target);
|
super(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int compareTo(Object o) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IntervalSet label() { return IntervalSet.of(from,to); }
|
public IntervalSet label() { return IntervalSet.of(from,to); }
|
||||||
|
|
||||||
|
|
|
@ -34,8 +34,4 @@ public class RuleTransition extends Transition {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEpsilon() { return true; }
|
public boolean isEpsilon() { return true; }
|
||||||
|
|
||||||
public int compareTo(Object o) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,28 +22,6 @@ public class SetTransition extends Transition {
|
||||||
|
|
||||||
public IntervalSet label() { return label; }
|
public IntervalSet label() { return label; }
|
||||||
|
|
||||||
public int compareTo(Object o) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// public boolean intersect(Label other) {
|
|
||||||
// if ( other.getClass() == SetTransition.class ) {
|
|
||||||
// return label.and(((SetTransition)other).label).isNil();
|
|
||||||
// }
|
|
||||||
// return label.member(((AtomTransition)other).label);
|
|
||||||
// }
|
|
||||||
|
|
||||||
public int hashCode() { return label.hashCode(); }
|
|
||||||
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if ( o==null ) return false;
|
|
||||||
if ( this == o ) return true; // equals if same object
|
|
||||||
if ( o.getClass() == AtomTransition.class ) {
|
|
||||||
o = IntervalSet.of(((AtomTransition)o).label);
|
|
||||||
}
|
|
||||||
return this.label.equals(((SetTransition)o).label);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString(Grammar g) {
|
public String toString(Grammar g) {
|
||||||
return label.toString(g);
|
return label.toString(g);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
package org.antlr.v4.runtime.atn;
|
|
||||||
|
|
||||||
/** ATN simulation thread state */
|
|
||||||
public class ThreadState {
|
|
||||||
public int addr;
|
|
||||||
public int alt; // or speculatively matched token type for lexers
|
|
||||||
public ATNStack context;
|
|
||||||
public int inputIndex = -1; // char (or token?) index from 0
|
|
||||||
public int inputMarker = -1; // accept states track input markers in case we need to rewind
|
|
||||||
|
|
||||||
public ThreadState(int addr, int alt, ATNStack context) {
|
|
||||||
this.addr = addr;
|
|
||||||
this.alt = alt;
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ThreadState(ThreadState t) {
|
|
||||||
this.addr = t.addr;
|
|
||||||
this.alt = t.alt;
|
|
||||||
this.context = t.context;
|
|
||||||
this.inputIndex = t.inputIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if ( o==null ) return false;
|
|
||||||
if ( this==o ) return true;
|
|
||||||
ThreadState other = (ThreadState)o;
|
|
||||||
return this.addr==other.addr &&
|
|
||||||
this.alt==other.alt &&
|
|
||||||
this.context.equals(other.context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int hashCode() { return addr + context.hashCode(); }
|
|
||||||
|
|
||||||
public String toString() {
|
|
||||||
if ( context.parent==null ) {
|
|
||||||
return "("+addr+","+alt+")";
|
|
||||||
}
|
|
||||||
return "("+addr+","+alt+","+context+")";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,8 +3,7 @@ package org.antlr.v4.runtime.atn;
|
||||||
import org.antlr.v4.misc.IntervalSet;
|
import org.antlr.v4.misc.IntervalSet;
|
||||||
import org.antlr.v4.tool.Grammar;
|
import org.antlr.v4.tool.Grammar;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.*;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/** An ATN transition between any two ATN states. Subclasses define
|
/** An ATN transition between any two ATN states. Subclasses define
|
||||||
* atom, set, epsilon, action, predicate, rule transitions.
|
* atom, set, epsilon, action, predicate, rule transitions.
|
||||||
|
@ -18,7 +17,7 @@ import java.util.Map;
|
||||||
* the states. We'll use the term Edge for the DFA to distinguish them from
|
* the states. We'll use the term Edge for the DFA to distinguish them from
|
||||||
* ATN transitions.
|
* ATN transitions.
|
||||||
*/
|
*/
|
||||||
public abstract class Transition implements Comparable {
|
public abstract class Transition {
|
||||||
// constants for serialization
|
// constants for serialization
|
||||||
public static final int EPSILON = 1;
|
public static final int EPSILON = 1;
|
||||||
public static final int RANGE = 2;
|
public static final int RANGE = 2;
|
||||||
|
|
|
@ -4,9 +4,6 @@ import org.antlr.v4.tool.Grammar;
|
||||||
|
|
||||||
public class WildcardTransition extends Transition {
|
public class WildcardTransition extends Transition {
|
||||||
public WildcardTransition(ATNState target) { super(target); }
|
public WildcardTransition(ATNState target) { super(target); }
|
||||||
public int compareTo(Object o) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString(Grammar g) {
|
public String toString(Grammar g) {
|
||||||
|
|
|
@ -2,7 +2,11 @@ package org.antlr.v4;
|
||||||
|
|
||||||
import org.antlr.runtime.*;
|
import org.antlr.runtime.*;
|
||||||
import org.antlr.tool.DOTGenerator;
|
import org.antlr.tool.DOTGenerator;
|
||||||
|
import org.antlr.v4.analysis.AnalysisPipeline;
|
||||||
|
import org.antlr.v4.automata.*;
|
||||||
|
import org.antlr.v4.codegen.CodeGenPipeline;
|
||||||
import org.antlr.v4.parse.*;
|
import org.antlr.v4.parse.*;
|
||||||
|
import org.antlr.v4.semantics.SemanticPipeline;
|
||||||
import org.antlr.v4.tool.*;
|
import org.antlr.v4.tool.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -65,6 +69,10 @@ public class Tool {
|
||||||
new Option("launch_ST_inspector", "-dbgST", "launch StringTemplate visualizer on generated code"),
|
new Option("launch_ST_inspector", "-dbgST", "launch StringTemplate visualizer on generated code"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The internal options are for my use on the command line during dev
|
||||||
|
public static boolean internalOption_PrintGrammarTree = false;
|
||||||
|
public static boolean internalOption_ShowATNConfigsInDFA = false;
|
||||||
|
|
||||||
public final String[] args;
|
public final String[] args;
|
||||||
|
|
||||||
protected List<String> grammarFiles = new ArrayList<String>();
|
protected List<String> grammarFiles = new ArrayList<String>();
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
package org.antlr.v4.analysis;
|
||||||
|
|
||||||
|
import org.antlr.v4.misc.IntervalSet;
|
||||||
|
import org.antlr.v4.runtime.atn.*;
|
||||||
|
import org.antlr.v4.tool.Grammar;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class AnalysisPipeline {
|
||||||
|
public Grammar g;
|
||||||
|
|
||||||
|
public AnalysisPipeline(Grammar g) {
|
||||||
|
this.g = g;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void process() {
|
||||||
|
// LEFT-RECURSION CHECK
|
||||||
|
LeftRecursionDetector lr = new LeftRecursionDetector(g.atn);
|
||||||
|
lr.check();
|
||||||
|
if ( lr.listOfRecursiveCycles.size()>0 ) return; // bail out
|
||||||
|
|
||||||
|
// BUILD DFA FOR EACH DECISION
|
||||||
|
if ( !g.isLexer() ) processParserOrTreeParser();
|
||||||
|
}
|
||||||
|
|
||||||
|
void processParserOrTreeParser() {
|
||||||
|
g.decisionLOOK =
|
||||||
|
new Vector<IntervalSet[]>(g.atn.getNumberOfDecisions()+1);
|
||||||
|
for (DecisionState s : g.atn.decisionToATNState) {
|
||||||
|
System.out.println("\nDECISION "+s.decision+" in rule "+s.rule.name);
|
||||||
|
|
||||||
|
LL1Analyzer anal = new LL1Analyzer(g.atn);
|
||||||
|
IntervalSet[] look = anal.getDecisionLookahead(s);
|
||||||
|
System.out.println("look="+ Arrays.toString(look));
|
||||||
|
g.decisionLOOK.setSize(s.decision+1);
|
||||||
|
g.decisionLOOK.set(s.decision, look);
|
||||||
|
System.out.println("LL(1)? "+disjoint(look));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return lookahead depth at which lookahead sets are disjoint or return 0 */
|
||||||
|
public static boolean disjoint(IntervalSet[] altLook) {
|
||||||
|
boolean collision = false;
|
||||||
|
IntervalSet combined = new IntervalSet();
|
||||||
|
for (int a=1; a<altLook.length; a++) {
|
||||||
|
IntervalSet look = altLook[a];
|
||||||
|
if ( !look.and(combined).isNil() ) {
|
||||||
|
System.out.println("alt "+a+" not disjoint with "+combined+"; look = "+look);
|
||||||
|
collision = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
combined.addAll(look);
|
||||||
|
}
|
||||||
|
return !collision;
|
||||||
|
}
|
||||||
|
}
|
|
@ -215,4 +215,21 @@ public class ATNSerializer {
|
||||||
if ( atn.g!=null ) return atn.g.getTokenDisplayName(t);
|
if ( atn.g!=null ) return atn.g.getTokenDisplayName(t);
|
||||||
return String.valueOf(t);
|
return String.valueOf(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Used by Java target to encode short/int array as chars in string. */
|
||||||
|
public static String getSerializedAsString(ATN atn) {
|
||||||
|
return new String(Utils.toCharArray(getSerialized(atn)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Integer> getSerialized(ATN atn) {
|
||||||
|
return new ATNSerializer(atn).serialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static char[] getSerializedAsChars(ATN atn) {
|
||||||
|
return Utils.toCharArray(new ATNSerializer(atn).serialize());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getDecoded(ATN atn) {
|
||||||
|
return new ATNSerializer(atn).decode(Utils.toCharArray(getSerialized(atn)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package org.antlr.v4.automata;
|
package org.antlr.v4.automata;
|
||||||
|
|
||||||
|
|
||||||
import org.antlr.runtime.RecognitionException;
|
import org.antlr.runtime.*;
|
||||||
import org.antlr.runtime.tree.*;
|
import org.antlr.runtime.tree.*;
|
||||||
import org.antlr.v4.misc.*;
|
import org.antlr.v4.misc.*;
|
||||||
import org.antlr.v4.parse.*;
|
import org.antlr.v4.parse.*;
|
||||||
|
|
|
@ -0,0 +1,254 @@
|
||||||
|
package org.antlr.v4.codegen;
|
||||||
|
|
||||||
|
import org.antlr.runtime.*;
|
||||||
|
import org.antlr.v4.codegen.model.RuleFunction;
|
||||||
|
import org.antlr.v4.codegen.model.actions.*;
|
||||||
|
import org.antlr.v4.parse.*;
|
||||||
|
import org.antlr.v4.tool.*;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class ActionTranslator implements ActionSplitterListener {
|
||||||
|
public static final Map<String, Class> rulePropToModelMap = new HashMap<String, Class>() {{
|
||||||
|
put("start", RulePropertyRef_start.class);
|
||||||
|
put("stop", RulePropertyRef_stop.class);
|
||||||
|
put("tree", RulePropertyRef_tree.class);
|
||||||
|
put("text", RulePropertyRef_text.class);
|
||||||
|
put("st", RulePropertyRef_st.class);
|
||||||
|
}};
|
||||||
|
|
||||||
|
public static final Map<String, Class> tokenPropToModelMap = new HashMap<String, Class>() {{
|
||||||
|
put("text", TokenPropertyRef_text.class);
|
||||||
|
put("type", TokenPropertyRef_type.class);
|
||||||
|
put("line", TokenPropertyRef_line.class);
|
||||||
|
put("index", TokenPropertyRef_index.class);
|
||||||
|
put("pos", TokenPropertyRef_pos.class);
|
||||||
|
put("channel", TokenPropertyRef_channel.class);
|
||||||
|
put("tree", TokenPropertyRef_tree.class);
|
||||||
|
put("int", TokenPropertyRef_int.class);
|
||||||
|
}};
|
||||||
|
|
||||||
|
ActionAST node;
|
||||||
|
RuleFunction rf;
|
||||||
|
List<ActionChunk> chunks = new ArrayList<ActionChunk>();
|
||||||
|
OutputModelFactory factory;
|
||||||
|
|
||||||
|
public ActionTranslator(OutputModelFactory factory, ActionAST node) {
|
||||||
|
this.factory = factory;
|
||||||
|
this.node = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<ActionChunk> translateAction(OutputModelFactory factory,
|
||||||
|
RuleFunction rf,
|
||||||
|
Token tokenWithinAction,
|
||||||
|
ActionAST node)
|
||||||
|
{
|
||||||
|
String action = tokenWithinAction.getText();
|
||||||
|
int firstCurly = action.indexOf('{');
|
||||||
|
int lastCurly = action.lastIndexOf('}');
|
||||||
|
if ( firstCurly>=0 && lastCurly>=0 ) {
|
||||||
|
action = action.substring(firstCurly+1, lastCurly); // trim {...}
|
||||||
|
}
|
||||||
|
return translateActionChunk(factory, rf, action, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<ActionChunk> translateActionChunk(OutputModelFactory factory,
|
||||||
|
RuleFunction rf,
|
||||||
|
String action,
|
||||||
|
ActionAST node)
|
||||||
|
{
|
||||||
|
Token tokenWithinAction = node.token;
|
||||||
|
ActionTranslator translator = new ActionTranslator(factory, node);
|
||||||
|
translator.rf = rf;
|
||||||
|
System.out.println("translate "+action);
|
||||||
|
ANTLRStringStream in = new ANTLRStringStream(action);
|
||||||
|
in.setLine(tokenWithinAction.getLine());
|
||||||
|
in.setCharPositionInLine(tokenWithinAction.getCharPositionInLine());
|
||||||
|
ActionSplitter trigger = new ActionSplitter(in, translator);
|
||||||
|
// forces eval, triggers listener methods
|
||||||
|
trigger.getActionTokens();
|
||||||
|
return translator.chunks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void attr(String expr, Token x) {
|
||||||
|
System.out.println("attr "+x);
|
||||||
|
Attribute a = node.resolver.resolveToAttribute(x.getText(), node);
|
||||||
|
if ( a!=null ) {
|
||||||
|
switch ( a.dict.type ) {
|
||||||
|
case ARG: chunks.add(new ArgRef(x.getText())); break;
|
||||||
|
case RET: chunks.add(new RetValueRef(x.getText())); break;
|
||||||
|
// case PREDEFINED_RULE: chunks.add(new RetValueRef(x.getText())); break;
|
||||||
|
// case PREDEFINED_TREE_RULE: chunks.add(new RetValueRef(x.getText())); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( node.resolver.resolvesToToken(x.getText(), node) ) {
|
||||||
|
chunks.add(new TokenRef(getTokenLabel(x.getText()))); // $label
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( node.resolver.resolvesToListLabel(x.getText(), node) ) {
|
||||||
|
return; // $ids for ids+=ID etc...
|
||||||
|
}
|
||||||
|
if ( node.resolver.resolveToDynamicScope(x.getText(), node)!=null ) {
|
||||||
|
chunks.add(new DynScopeRef(getDynamicScopeName(x.getText()))); // $S for scope S is ok
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// switch ( a.dict.type ) {
|
||||||
|
// case ARG: chunks.add(new ArgRef(x.getText())); break;
|
||||||
|
// case RET: chunks.add(new RetValueRef(x.getText())); break;
|
||||||
|
// case PREDEFINED_RULE: chunks.add(new RetValueRef(x.getText())); break;
|
||||||
|
// case PREDEFINED_LEXER_RULE: chunks.add(new RetValueRef(x.getText())); break;
|
||||||
|
// case PREDEFINED_TREE_RULE: chunks.add(new RetValueRef(x.getText())); break;
|
||||||
|
// case GLOBAL_SCOPE: chunks.add(new RetValueRef(x.getText())); break;
|
||||||
|
// case RULE_SCOPE: chunks.add(new RetValueRef(x.getText())); break;
|
||||||
|
// case TOKEN: chunks.add(new TokenRef(x.getText())); break;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** $x.y = expr; */
|
||||||
|
public void setQualifiedAttr(String expr, Token x, Token y, Token rhs) {
|
||||||
|
System.out.println("setQAttr "+x+"."+y+"="+rhs);
|
||||||
|
// x has to be current rule; just set y attr
|
||||||
|
List<ActionChunk> rhsChunks = translateActionChunk(factory,rf,rhs.getText(),node);
|
||||||
|
chunks.add(new SetAttr(y.getText(), rhsChunks));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void qualifiedAttr(String expr, Token x, Token y) {
|
||||||
|
System.out.println("qattr "+x+"."+y);
|
||||||
|
Attribute a = node.resolver.resolveToAttribute(x.getText(), y.getText(), node);
|
||||||
|
switch ( a.dict.type ) {
|
||||||
|
case ARG: chunks.add(new ArgRef(y.getText())); break; // has to be current rule
|
||||||
|
case RET:
|
||||||
|
if ( factory.currentRule.size()>0 && factory.currentRule.peek().name.equals(x.getText()) ) {
|
||||||
|
chunks.add(new RetValueRef(y.getText())); break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
chunks.add(new QRetValueRef(getRuleLabel(x.getText()), y.getText())); break;
|
||||||
|
}
|
||||||
|
case PREDEFINED_RULE: chunks.add(getRulePropertyRef(x, y)); break;
|
||||||
|
case TOKEN: chunks.add(getTokenPropertyRef(x, y)); break;
|
||||||
|
// case PREDEFINED_LEXER_RULE: chunks.add(new RetValueRef(x.getText())); break;
|
||||||
|
// case PREDEFINED_TREE_RULE: chunks.add(new RetValueRef(x.getText())); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttr(String expr, Token x, Token rhs) {
|
||||||
|
System.out.println("setAttr "+x+" "+rhs);
|
||||||
|
List<ActionChunk> rhsChunks = translateActionChunk(factory,rf,rhs.getText(),node);
|
||||||
|
chunks.add(new SetAttr(x.getText(), rhsChunks));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dynamicScopeAttr(String expr, Token x, Token y) {
|
||||||
|
System.out.println("scoped "+x+"."+y);
|
||||||
|
// we assume valid, just gen code
|
||||||
|
chunks.add(new DynScopeAttrRef(getDynamicScopeName(x.getText()), y.getText()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDynamicScopeAttr(String expr, Token x, Token y, Token rhs) {
|
||||||
|
List<ActionChunk> rhsChunks = translateActionChunk(factory,rf,rhs.getText(),node);
|
||||||
|
chunks.add(new SetDynScopeAttr(getDynamicScopeName(x.getText()), y.getText(), rhsChunks));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dynamicNegativeIndexedScopeAttr(String expr, Token x, Token y, Token index) {
|
||||||
|
List<ActionChunk> indexChunks = translateActionChunk(factory,rf,index.getText(),node);
|
||||||
|
chunks.add(new DynScopeAttrRef_negIndex(getDynamicScopeName(x.getText()), y.getText(), indexChunks));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDynamicNegativeIndexedScopeAttr(String expr, Token x, Token y, Token index, Token rhs) {
|
||||||
|
List<ActionChunk> indexChunks = translateActionChunk(factory,rf,index.getText(),node);
|
||||||
|
List<ActionChunk> rhsChunks = translateActionChunk(factory,rf,rhs.getText(),node);
|
||||||
|
chunks.add(new SetDynScopeAttr_negIndex(getDynamicScopeName(x.getText()), y.getText(), indexChunks, rhsChunks));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dynamicAbsoluteIndexedScopeAttr(String expr, Token x, Token y, Token index) {
|
||||||
|
List<ActionChunk> indexChunks = translateActionChunk(factory,rf,index.getText(),node);
|
||||||
|
chunks.add(new DynScopeAttrRef_index(getDynamicScopeName(x.getText()), y.getText(), indexChunks));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDynamicAbsoluteIndexedScopeAttr(String expr, Token x, Token y, Token index, Token rhs) {
|
||||||
|
List<ActionChunk> indexChunks = translateActionChunk(factory,rf,index.getText(),node);
|
||||||
|
List<ActionChunk> rhsChunks = translateActionChunk(factory,rf,rhs.getText(),node);
|
||||||
|
chunks.add(new SetDynScopeAttr_index(getDynamicScopeName(x.getText()), y.getText(), indexChunks, rhsChunks));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void templateInstance(String expr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void indirectTemplateInstance(String expr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExprAttribute(String expr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSTAttribute(String expr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void templateExpr(String expr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unknownSyntax(Token t) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void text(String text) {
|
||||||
|
chunks.add(new ActionText(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
TokenPropertyRef getTokenPropertyRef(Token x, Token y) {
|
||||||
|
try {
|
||||||
|
Class c = tokenPropToModelMap.get(y.getText());
|
||||||
|
Constructor ctor = c.getConstructor(new Class[] {String.class});
|
||||||
|
TokenPropertyRef ref =
|
||||||
|
(TokenPropertyRef)ctor.newInstance(getRuleLabel(x.getText()));
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
factory.g.tool.errMgr.toolError(ErrorType.INTERNAL_ERROR, e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
RulePropertyRef getRulePropertyRef(Token x, Token y) {
|
||||||
|
try {
|
||||||
|
Class c = rulePropToModelMap.get(y.getText());
|
||||||
|
Constructor ctor = c.getConstructor(new Class[] {String.class});
|
||||||
|
RulePropertyRef ref =
|
||||||
|
(RulePropertyRef)ctor.newInstance(getRuleLabel(x.getText()));
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
factory.g.tool.errMgr.toolError(ErrorType.INTERNAL_ERROR, e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTokenLabel(String x) {
|
||||||
|
if ( node.resolver.resolvesToLabel(x, node) ) return x;
|
||||||
|
return factory.gen.target.getImplicitTokenLabel(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRuleLabel(String x) {
|
||||||
|
if ( node.resolver.resolvesToLabel(x, node) ) return x;
|
||||||
|
return factory.gen.target.getImplicitRuleLabel(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDynamicScopeName(String x) {
|
||||||
|
String scope;
|
||||||
|
if ( factory.g.getRule(x)==null ) {
|
||||||
|
scope = factory.gen.target.getGlobalDynamicScopeStructName(x);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
scope = factory.gen.target.getRuleDynamicScopeStructName(x);
|
||||||
|
}
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public String getTokenLabel(String x, ActionAST node) {
|
||||||
|
// Alternative alt = node.resolver.
|
||||||
|
// Rule r = node.ATNState.rule;
|
||||||
|
// if ( r.tokenRefs.get(x)!=null ) return true;
|
||||||
|
// LabelElementPair anyLabelDef = getAnyLabelDef(x);
|
||||||
|
// if ( anyLabelDef!=null && anyLabelDef.type== LabelType.TOKEN_LABEL ) return true;
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
}
|
|
@ -0,0 +1,180 @@
|
||||||
|
package org.antlr.v4.codegen;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.model.OutputModelObject;
|
||||||
|
import org.antlr.v4.misc.Utils;
|
||||||
|
import org.antlr.v4.runtime.Token;
|
||||||
|
import org.antlr.v4.tool.*;
|
||||||
|
import org.stringtemplate.v4.*;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/** General controller for code gen. Can instantiate sub generator(s).
|
||||||
|
*/
|
||||||
|
public class CodeGenerator {
|
||||||
|
public static boolean LAUNCH_ST_INSPECTOR = false;
|
||||||
|
public static final String TEMPLATE_ROOT = "org/antlr/v4/tool/templates/codegen";
|
||||||
|
public static final String VOCAB_FILE_EXTENSION = ".tokens";
|
||||||
|
public final static String vocabFilePattern =
|
||||||
|
"<tokens.keys:{t | <t>=<tokens.(t)>\n}>" +
|
||||||
|
"<literals.keys:{t | <t>=<literals.(t)>\n}>";
|
||||||
|
|
||||||
|
public Grammar g;
|
||||||
|
public Target target;
|
||||||
|
public STGroup templates;
|
||||||
|
|
||||||
|
public int lineWidth = 72;
|
||||||
|
|
||||||
|
public CodeGenerator(Grammar g) {
|
||||||
|
this.g = g;
|
||||||
|
String language = g.getOption("language", "Java");
|
||||||
|
loadLanguageTarget(language);
|
||||||
|
loadTemplates(language);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadLanguageTarget(String language) {
|
||||||
|
String targetName = "org.antlr.v4.codegen."+language+"Target";
|
||||||
|
try {
|
||||||
|
Class c = Class.forName(targetName);
|
||||||
|
target = (Target)c.newInstance();
|
||||||
|
}
|
||||||
|
catch (ClassNotFoundException cnfe) {
|
||||||
|
target = new Target(); // use default
|
||||||
|
}
|
||||||
|
catch (InstantiationException ie) {
|
||||||
|
g.tool.errMgr.toolError(ErrorType.CANNOT_CREATE_TARGET_GENERATOR,
|
||||||
|
targetName,
|
||||||
|
ie);
|
||||||
|
}
|
||||||
|
catch (IllegalAccessException cnfe) {
|
||||||
|
g.tool.errMgr.toolError(ErrorType.CANNOT_CREATE_TARGET_GENERATOR,
|
||||||
|
targetName,
|
||||||
|
cnfe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadTemplates(String language) {
|
||||||
|
try {
|
||||||
|
templates = new STGroupFile(TEMPLATE_ROOT+"/"+language+"/"+language+".stg");
|
||||||
|
templates.registerRenderer(Integer.class, new NumberRenderer());
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException iae) {
|
||||||
|
g.tool.errMgr.toolError(ErrorType.CANNOT_CREATE_TARGET_GENERATOR,
|
||||||
|
language);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if ( EMIT_TEMPLATE_DELIMITERS ) {
|
||||||
|
// templates.emitDebugStartStopStrings(true);
|
||||||
|
// templates.doNotEmitDebugStringsForTemplate("codeFileExtension");
|
||||||
|
// templates.doNotEmitDebugStringsForTemplate("headerFileExtension");
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
public ST generate() {
|
||||||
|
OutputModelFactory factory;
|
||||||
|
if ( g.isParser() || g.isCombined() || g.isTreeGrammar() ) {
|
||||||
|
factory = new ParserFactory(this);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
factory = new LexerFactory(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputModelWalker walker = new OutputModelWalker(g.tool, templates);
|
||||||
|
OutputModelObject outputModel = factory.buildOutputModel();
|
||||||
|
ST st = walker.walk(outputModel);
|
||||||
|
|
||||||
|
if (CodeGenerator.LAUNCH_ST_INSPECTOR) {
|
||||||
|
st.inspect();
|
||||||
|
//if ( templates.isDefined("headerFile") ) headerFileST.inspect();
|
||||||
|
}
|
||||||
|
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generate a token vocab file with all the token names/types. For example:
|
||||||
|
* ID=7
|
||||||
|
* FOR=8
|
||||||
|
* 'for'=8
|
||||||
|
*
|
||||||
|
* This is independent of the target language; used by antlr internally
|
||||||
|
*/
|
||||||
|
ST getTokenVocabOutput() {
|
||||||
|
ST vocabFileST = new ST(vocabFilePattern);
|
||||||
|
Map<String,Integer> tokens = new HashMap<String,Integer>();
|
||||||
|
// make constants for the token names
|
||||||
|
for (String t : g.tokenNameToTypeMap.keySet()) {
|
||||||
|
int tokenType = g.tokenNameToTypeMap.get(t);
|
||||||
|
if ( tokenType>=Token.MIN_TOKEN_TYPE ) {
|
||||||
|
tokens.put(t, Utils.integer(tokenType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vocabFileST.add("tokens", tokens);
|
||||||
|
|
||||||
|
// now dump the strings
|
||||||
|
Map<String,Integer> literals = new HashMap<String,Integer>();
|
||||||
|
for (String literal : g.stringLiteralToTypeMap.keySet()) {
|
||||||
|
int tokenType = g.stringLiteralToTypeMap.get(literal);
|
||||||
|
if ( tokenType>=Token.MIN_TOKEN_TYPE ) {
|
||||||
|
literals.put(literal, Utils.integer(tokenType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vocabFileST.add("literals", literals);
|
||||||
|
|
||||||
|
return vocabFileST;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(ST outputFileST) {
|
||||||
|
// WRITE FILES
|
||||||
|
try {
|
||||||
|
target.genRecognizerFile(this,g,outputFileST);
|
||||||
|
if ( templates.isDefined("headerFile") ) {
|
||||||
|
ST extST = templates.getInstanceOf("headerFileExtension");
|
||||||
|
ST headerFileST = null;
|
||||||
|
target.genRecognizerHeaderFile(this,g,headerFileST,extST.render(lineWidth));
|
||||||
|
}
|
||||||
|
// write out the vocab interchange file; used by antlr,
|
||||||
|
// does not change per target
|
||||||
|
ST tokenVocabSerialization = getTokenVocabOutput();
|
||||||
|
String vocabFileName = getVocabFileName();
|
||||||
|
if ( vocabFileName!=null ) {
|
||||||
|
write(tokenVocabSerialization, vocabFileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException ioe) {
|
||||||
|
g.tool.errMgr.toolError(ErrorType.CANNOT_WRITE_FILE,
|
||||||
|
getVocabFileName(),
|
||||||
|
ioe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(ST code, String fileName) throws IOException {
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
Writer w = g.tool.getOutputFile(g, fileName);
|
||||||
|
STWriter wr = new AutoIndentWriter(w);
|
||||||
|
wr.setLineWidth(lineWidth);
|
||||||
|
code.write(wr);
|
||||||
|
w.close();
|
||||||
|
long stop = System.currentTimeMillis();
|
||||||
|
System.out.println("render time for "+fileName+": "+(int)(stop-start)+"ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generate TParser.java and TLexer.java from T.g if combined, else
|
||||||
|
* just use T.java as output regardless of type.
|
||||||
|
*/
|
||||||
|
public String getRecognizerFileName() {
|
||||||
|
ST extST = templates.getInstanceOf("codeFileExtension");
|
||||||
|
String recognizerName = g.getRecognizerName();
|
||||||
|
return recognizerName+extST.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** What is the name of the vocab file generated for this grammar?
|
||||||
|
* Returns null if no .tokens file should be generated.
|
||||||
|
*/
|
||||||
|
public String getVocabFileName() {
|
||||||
|
// if ( g.isBuiltFromString() ) {
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
return g.name+VOCAB_FILE_EXTENSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,167 @@
|
||||||
|
package org.antlr.v4.codegen;
|
||||||
|
|
||||||
|
import org.antlr.v4.analysis.AnalysisPipeline;
|
||||||
|
import org.antlr.v4.codegen.model.*;
|
||||||
|
import org.antlr.v4.misc.IntervalSet;
|
||||||
|
import org.antlr.v4.parse.ANTLRParser;
|
||||||
|
import org.antlr.v4.runtime.atn.*;
|
||||||
|
import org.antlr.v4.tool.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/** Create output objects wthin rule functions */
|
||||||
|
public abstract class OutputModelFactory {
|
||||||
|
public Grammar g;
|
||||||
|
public CodeGenerator gen;
|
||||||
|
|
||||||
|
// Context ptrs
|
||||||
|
public OutputModelObject file; // root
|
||||||
|
public Stack<RuleFunction> currentRule = new Stack<RuleFunction>();
|
||||||
|
public Alternative currentAlt;
|
||||||
|
|
||||||
|
protected OutputModelFactory(CodeGenerator gen) {
|
||||||
|
this.gen = gen;
|
||||||
|
this.g = gen.g;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract OutputModelObject buildOutputModel();
|
||||||
|
|
||||||
|
public CodeBlock epsilon() { return new CodeBlock(this); }
|
||||||
|
|
||||||
|
public CodeBlock alternative(List<SrcOp> elems) { return new CodeBlock(this, elems); }
|
||||||
|
|
||||||
|
public SrcOp action(GrammarAST ast) { return new Action(this, ast); }
|
||||||
|
|
||||||
|
public SrcOp forcedAction(GrammarAST ast) { return new ForcedAction(this, ast); }
|
||||||
|
|
||||||
|
public SrcOp sempred(GrammarAST ast) { return new SemPred(this, ast); }
|
||||||
|
|
||||||
|
public abstract List<SrcOp> ruleRef(GrammarAST ID, GrammarAST label, GrammarAST args);
|
||||||
|
|
||||||
|
public abstract List<SrcOp> tokenRef(GrammarAST ID, GrammarAST label, GrammarAST args);
|
||||||
|
|
||||||
|
public abstract List<SrcOp> stringRef(GrammarAST ID, GrammarAST label);
|
||||||
|
|
||||||
|
public Choice getChoiceBlock(BlockAST blkAST, List<CodeBlock> alts) {
|
||||||
|
int decision = ((DecisionState)blkAST.atnState).decision;
|
||||||
|
if ( AnalysisPipeline.disjoint(g.decisionLOOK.get(decision)) ) {
|
||||||
|
return getLL1ChoiceBlock(blkAST, alts);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return getLLStarChoiceBlock(blkAST, alts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Choice getEBNFBlock(GrammarAST ebnfRoot, List<CodeBlock> alts) {
|
||||||
|
int decision;
|
||||||
|
if ( ebnfRoot.getType()==ANTLRParser.POSITIVE_CLOSURE ) {
|
||||||
|
decision = ((PlusBlockStartState)ebnfRoot.atnState).loopBackState.decision;
|
||||||
|
}
|
||||||
|
else if ( ebnfRoot.getType()==ANTLRParser.CLOSURE ) {
|
||||||
|
decision = ((BlockStartState)ebnfRoot.atnState).decision;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
decision = ((DecisionState)ebnfRoot.atnState).decision;
|
||||||
|
}
|
||||||
|
if ( AnalysisPipeline.disjoint(g.decisionLOOK.get(decision)) ) {
|
||||||
|
return getLL1EBNFBlock(ebnfRoot, alts);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return getLLStarEBNFBlock(ebnfRoot, alts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Choice getLL1ChoiceBlock(BlockAST blkAST, List<CodeBlock> alts) {
|
||||||
|
return new LL1AltBlock(this, blkAST, alts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Choice getLLStarChoiceBlock(BlockAST blkAST, List<CodeBlock> alts) {
|
||||||
|
return new AltBlock(this, blkAST, alts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Choice getLL1EBNFBlock(GrammarAST ebnfRoot, List<CodeBlock> alts) {
|
||||||
|
int ebnf = 0;
|
||||||
|
if ( ebnfRoot!=null ) ebnf = ebnfRoot.getType();
|
||||||
|
Choice c = null;
|
||||||
|
switch ( ebnf ) {
|
||||||
|
case ANTLRParser.OPTIONAL :
|
||||||
|
if ( alts.size()==1 ) c = new LL1OptionalBlockSingleAlt(this, ebnfRoot, alts);
|
||||||
|
else c = new LL1OptionalBlock(this, ebnfRoot, alts);
|
||||||
|
break;
|
||||||
|
case ANTLRParser.CLOSURE :
|
||||||
|
if ( alts.size()==1 ) c = new LL1StarBlockSingleAlt(this, ebnfRoot, alts);
|
||||||
|
else c = new LL1StarBlock(this, ebnfRoot, alts);
|
||||||
|
break;
|
||||||
|
case ANTLRParser.POSITIVE_CLOSURE :
|
||||||
|
if ( alts.size()==1 ) c = new LL1PlusBlockSingleAlt(this, ebnfRoot, alts);
|
||||||
|
else c = new LL1PlusBlock(this, ebnfRoot, alts);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Choice getLLStarEBNFBlock(GrammarAST ebnfRoot, List<CodeBlock> alts) {
|
||||||
|
int ebnf = 0;
|
||||||
|
if ( ebnfRoot!=null ) ebnf = ebnfRoot.getType();
|
||||||
|
Choice c = null;
|
||||||
|
switch ( ebnf ) {
|
||||||
|
case ANTLRParser.OPTIONAL :
|
||||||
|
c = new OptionalBlock(this, ebnfRoot, alts);
|
||||||
|
break;
|
||||||
|
case ANTLRParser.CLOSURE :
|
||||||
|
c = new StarBlock(this, ebnfRoot, alts);
|
||||||
|
break;
|
||||||
|
case ANTLRParser.POSITIVE_CLOSURE :
|
||||||
|
c = new PlusBlock(this, ebnfRoot, alts);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void defineBitSet(BitSetDecl b);
|
||||||
|
|
||||||
|
public SrcOp getLL1Test(IntervalSet look, GrammarAST blkAST) {
|
||||||
|
return new TestSetInline(this, blkAST, look);
|
||||||
|
// OutputModelObject expr;
|
||||||
|
// if ( look.size() < gen.target.getInlineTestsVsBitsetThreshold() ) {
|
||||||
|
// expr = new TestSetInline(this, blkAST, look);
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// expr = new TestSet(this, blkAST, look);
|
||||||
|
// }
|
||||||
|
// return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public DFADecl defineDFA(GrammarAST ast, DFA dfa) {
|
||||||
|
// return null;
|
||||||
|
//// DFADef d = new DFADef(name, dfa);
|
||||||
|
//// outputModel.dfaDefs.add(d);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
public BitSetDecl createFollowBitSet(GrammarAST ast, IntervalSet set) {
|
||||||
|
String inRuleName = ast.atnState.rule.name;
|
||||||
|
String elementName = ast.getText(); // assume rule ref
|
||||||
|
if ( ast.getType() == ANTLRParser.STRING_LITERAL ) {
|
||||||
|
elementName = gen.target.getTokenTypeAsTargetLabel(g, g.stringLiteralToTypeMap.get(elementName));
|
||||||
|
}
|
||||||
|
String name = "FOLLOW_"+elementName+"_in_"+inRuleName+"_"+ast.token.getTokenIndex();
|
||||||
|
BitSetDecl b = new BitSetDecl(this, name, set);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitSetDecl createExpectingBitSet(GrammarAST ast, int decision, IntervalSet set, String position) {
|
||||||
|
String inRuleName = ast.atnState.rule.name;
|
||||||
|
String name = "EXPECTING_in_"+inRuleName+"_"+position+"_"+decision;
|
||||||
|
//System.out.println("!!!!!!!! create "+name);
|
||||||
|
BitSetDecl b = new BitSetDecl(this, name, set);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitSetDecl createTestBitSet(GrammarAST ast, IntervalSet set) {
|
||||||
|
String inRuleName = ast.atnState.rule.name;
|
||||||
|
String name = "LOOK_in_"+inRuleName+"_"+ast.token.getTokenIndex();
|
||||||
|
BitSetDecl b = new BitSetDecl(this, name, set);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
package org.antlr.v4.codegen;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.model.*;
|
||||||
|
import org.antlr.v4.misc.Utils;
|
||||||
|
import org.antlr.v4.parse.ANTLRParser;
|
||||||
|
import org.antlr.v4.tool.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class ParserFactory extends OutputModelFactory {
|
||||||
|
// public static final Map<Class, String> modelToTemplateMap = new HashMap<Class, String>() {{
|
||||||
|
// put(ParserFile.class, "parserFile");
|
||||||
|
// put(Parser.class, "parser");
|
||||||
|
// put(RuleFunction.class, "parserFunction");
|
||||||
|
// put(DFADef.class, "DFA");
|
||||||
|
// put(CodeBlock.class, "codeBlock");
|
||||||
|
// put(LL1Choice.class, "switch");
|
||||||
|
// put(MatchToken.class, "matchToken");
|
||||||
|
// }};
|
||||||
|
|
||||||
|
public ParserFactory(CodeGenerator gen) {
|
||||||
|
super(gen);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OutputModelObject buildOutputModel() {
|
||||||
|
return new ParserFile(this, gen.getRecognizerFileName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<SrcOp> ruleRef(GrammarAST ID, GrammarAST label, GrammarAST args) {
|
||||||
|
InvokeRule r = new InvokeRule(this, ID, label);
|
||||||
|
AddToList a = null;
|
||||||
|
if ( label!=null && label.parent.getType()==ANTLRParser.PLUS_ASSIGN ) {
|
||||||
|
a = new AddToList(this, gen.target.getListLabel(label.getText()), r);
|
||||||
|
}
|
||||||
|
return Utils.list(r, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<SrcOp> tokenRef(GrammarAST ID, GrammarAST label, GrammarAST args) {
|
||||||
|
MatchToken m = new MatchToken(this, (TerminalAST) ID, label);
|
||||||
|
AddToList a = null;
|
||||||
|
if ( label!=null && label.parent.getType()==ANTLRParser.PLUS_ASSIGN ) {
|
||||||
|
a = new AddToList(this, gen.target.getListLabel(label.getText()), m);
|
||||||
|
}
|
||||||
|
return Utils.list(m, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<SrcOp> stringRef(GrammarAST ID, GrammarAST label) {
|
||||||
|
return tokenRef(ID, label, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void defineBitSet(BitSetDecl b) {
|
||||||
|
// ((ParserFile)file).defineBitSet(b);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package org.antlr.v4.codegen;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.model.Decl;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class RuleContextDecl extends Decl {
|
||||||
|
public String ctxName;
|
||||||
|
public RuleContextDecl(OutputModelFactory factory, String name, String ctxName) {
|
||||||
|
super(factory, name);
|
||||||
|
this.ctxName = ctxName;
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,29 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.*;
|
||||||
|
import org.antlr.v4.codegen.model.actions.ActionChunk;
|
||||||
|
import org.antlr.v4.tool.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class Action extends RuleElement {
|
||||||
|
public List<ActionChunk> chunks;
|
||||||
|
public Action(OutputModelFactory factory, GrammarAST ast) {
|
||||||
|
super(factory,ast);
|
||||||
|
RuleFunction rf = null;
|
||||||
|
if ( factory.currentRule.size()>0 ) rf = factory.currentRule.peek();
|
||||||
|
chunks = ActionTranslator.translateAction(factory, rf, ast.token, (ActionAST)ast);
|
||||||
|
//System.out.println("actions="+chunks);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
// public List<String> getChildren() {
|
||||||
|
// final List<String> sup = super.getChildren();
|
||||||
|
// return new ArrayList<String>() {{
|
||||||
|
// if ( sup!=null ) addAll(sup);
|
||||||
|
// add("chunks");
|
||||||
|
// }};
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class AddToList extends SrcOp {
|
||||||
|
public String listName;
|
||||||
|
public LabeledOp opWithResultToAdd;
|
||||||
|
|
||||||
|
public AddToList(OutputModelFactory factory, String listName, LabeledOp opWithResultToAdd) {
|
||||||
|
super(factory);
|
||||||
|
this.listName = listName;
|
||||||
|
this.opWithResultToAdd = opWithResultToAdd;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
import org.antlr.v4.runtime.atn.BlockStartState;
|
||||||
|
import org.antlr.v4.tool.GrammarAST;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class AltBlock extends Choice {
|
||||||
|
public ThrowNoViableAlt error;
|
||||||
|
|
||||||
|
public AltBlock(OutputModelFactory factory,
|
||||||
|
GrammarAST blkOrEbnfRootAST,
|
||||||
|
List<CodeBlock> alts)
|
||||||
|
{
|
||||||
|
super(factory, blkOrEbnfRootAST, alts);
|
||||||
|
decision = ((BlockStartState)blkOrEbnfRootAST.atnState).decision;
|
||||||
|
this.error = new ThrowNoViableAlt(factory, blkOrEbnfRootAST, null);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
public class CaptureNextToken extends SrcOp {
|
||||||
|
public String varName;
|
||||||
|
public CaptureNextToken(String varName) { this.varName = varName; }
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class CaptureNextTokenType extends SrcOp {
|
||||||
|
public String varName;
|
||||||
|
public CaptureNextTokenType(String varName) { this.varName = varName; }
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
import org.antlr.v4.misc.IntervalSet;
|
||||||
|
import org.antlr.v4.tool.GrammarAST;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/** The class hierarchy underneath SrcOp is pretty deep but makes sense that,
|
||||||
|
* for example LL1StarBlock is a kind of LL1Loop which is a kind of Choice.
|
||||||
|
* The problem is it's impossible to figure
|
||||||
|
* out how to construct one of these deeply nested objects because of the
|
||||||
|
* long super constructor call chain. Instead, I decided to in-line all of
|
||||||
|
* this and then look for opportunities to re-factor code into functions.
|
||||||
|
* It makes sense to use a class hierarchy to share data fields, but I don't
|
||||||
|
* think it makes sense to factor code using super constructors because
|
||||||
|
* it has too much work to do.
|
||||||
|
*/
|
||||||
|
public abstract class Choice extends RuleElement {
|
||||||
|
public int decision = -1;
|
||||||
|
public List<CodeBlock> alts;
|
||||||
|
public List<SrcOp> preamble;
|
||||||
|
|
||||||
|
public Choice(OutputModelFactory factory,
|
||||||
|
GrammarAST blkOrEbnfRootAST,
|
||||||
|
List<CodeBlock> alts)
|
||||||
|
{
|
||||||
|
super(factory, blkOrEbnfRootAST);
|
||||||
|
this.alts = alts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPreambleOp(SrcOp op) {
|
||||||
|
if ( preamble==null ) preamble = new ArrayList<SrcOp>();
|
||||||
|
preamble.add(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String[]> getAltLookaheadAsStringLists(IntervalSet[] altLookSets) {
|
||||||
|
List<String[]> altLook = new ArrayList<String[]>();
|
||||||
|
for (int a=1; a<altLookSets.length; a++) {
|
||||||
|
IntervalSet s = altLookSets[a];
|
||||||
|
altLook.add(factory.gen.target.getTokenTypesAsTargetLabels(factory.g, s.toArray()));
|
||||||
|
}
|
||||||
|
return altLook;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SrcOp addCodeForLookaheadTempVar(IntervalSet look) {
|
||||||
|
SrcOp expr = factory.getLL1Test(look, ast);
|
||||||
|
if ( expr instanceof TestSetInline) {
|
||||||
|
TestSetInline e = (TestSetInline)expr;
|
||||||
|
Decl d = new TokenTypeDecl(factory, e.varName);
|
||||||
|
factory.currentRule.peek().addDecl(d);
|
||||||
|
CaptureNextTokenType nextType = new CaptureNextTokenType(e.varName);
|
||||||
|
addPreambleOp(nextType);
|
||||||
|
}
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class CodeBlock extends SrcOp {
|
||||||
|
public List<SrcOp> ops;
|
||||||
|
|
||||||
|
public CodeBlock(OutputModelFactory factory) { this.factory = factory; }
|
||||||
|
|
||||||
|
public CodeBlock(OutputModelFactory factory, List<SrcOp> ops) {
|
||||||
|
super(factory);
|
||||||
|
this.ops = ops;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CodeBlock(OutputModelFactory factory, final SrcOp elem) {
|
||||||
|
this(factory, new ArrayList<SrcOp>() {{ add(elem); }});
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
// public List<String> getChildren() {
|
||||||
|
// final List<String> sup = super.getChildren();
|
||||||
|
// return new ArrayList<String>() {{ if ( sup!=null ) addAll(sup); add("ops"); }};
|
||||||
|
// }
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class Decl extends SrcOp {
|
||||||
|
public String name;
|
||||||
|
public String decl; // whole thing if copied from action
|
||||||
|
|
||||||
|
public Decl(OutputModelFactory factory, String name, String decl) {
|
||||||
|
this(factory, name);
|
||||||
|
this.decl = decl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Decl(OutputModelFactory factory, String name) {
|
||||||
|
super(factory);
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return name.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
return name.equals(((Decl)obj).name);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
import org.antlr.v4.tool.GrammarAST;
|
||||||
|
|
||||||
|
public class ForcedAction extends Action {
|
||||||
|
public ForcedAction(OutputModelFactory factory, GrammarAST ast) {
|
||||||
|
super(factory, ast);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
import org.antlr.v4.misc.IntervalSet;
|
||||||
|
import org.antlr.v4.runtime.atn.DecisionState;
|
||||||
|
import org.antlr.v4.tool.GrammarAST;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** (A | B | C) */
|
||||||
|
public class LL1AltBlock extends LL1Choice {
|
||||||
|
public LL1AltBlock(OutputModelFactory factory, GrammarAST blkAST, List<CodeBlock> alts) {
|
||||||
|
super(factory, blkAST, alts);
|
||||||
|
this.decision = ((DecisionState)blkAST.atnState).decision;
|
||||||
|
|
||||||
|
/** Lookahead for each alt 1..n */
|
||||||
|
// IntervalSet[] altLookSets = LinearApproximator.getLL1LookaheadSets(dfa);
|
||||||
|
IntervalSet[] altLookSets = factory.g.decisionLOOK.get(decision);
|
||||||
|
altLook = getAltLookaheadAsStringLists(altLookSets);
|
||||||
|
|
||||||
|
IntervalSet expecting = IntervalSet.or(altLookSets); // combine alt sets
|
||||||
|
this.error = new ThrowNoViableAlt(factory, blkAST, expecting);
|
||||||
|
System.out.println(blkAST.toStringTree()+" LL1AltBlock expecting="+expecting);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
import org.antlr.v4.misc.IntervalSet;
|
||||||
|
import org.antlr.v4.tool.GrammarAST;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public abstract class LL1Loop extends Choice {
|
||||||
|
public OutputModelObject loopExpr;
|
||||||
|
public List<SrcOp> iteration;
|
||||||
|
public Sync sync;
|
||||||
|
|
||||||
|
public LL1Loop(OutputModelFactory factory,
|
||||||
|
GrammarAST blkAST,
|
||||||
|
List<CodeBlock> alts)
|
||||||
|
{
|
||||||
|
super(factory, blkAST, alts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addIterationOp(SrcOp op) {
|
||||||
|
if ( iteration==null ) iteration = new ArrayList<SrcOp>();
|
||||||
|
iteration.add(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SrcOp addCodeForLoopLookaheadTempVar(IntervalSet look) {
|
||||||
|
SrcOp expr = addCodeForLookaheadTempVar(look);
|
||||||
|
if ( expr instanceof TestSetInline ) {
|
||||||
|
TestSetInline e = (TestSetInline)expr;
|
||||||
|
CaptureNextTokenType nextType = new CaptureNextTokenType(e.varName);
|
||||||
|
addIterationOp(nextType);
|
||||||
|
}
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
import org.antlr.v4.misc.IntervalSet;
|
||||||
|
import org.antlr.v4.runtime.atn.DecisionState;
|
||||||
|
import org.antlr.v4.tool.GrammarAST;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** (A B C)? */
|
||||||
|
public class LL1OptionalBlockSingleAlt extends LL1Choice {
|
||||||
|
public OutputModelObject expr;
|
||||||
|
public OutputModelObject followExpr;
|
||||||
|
|
||||||
|
public LL1OptionalBlockSingleAlt(OutputModelFactory factory,
|
||||||
|
GrammarAST blkAST,
|
||||||
|
List<CodeBlock> alts)
|
||||||
|
{
|
||||||
|
super(factory, blkAST, alts);
|
||||||
|
this.decision = ((DecisionState)blkAST.atnState).decision;
|
||||||
|
|
||||||
|
/** Lookahead for each alt 1..n */
|
||||||
|
// IntervalSet[] altLookSets = LinearApproximator.getLL1LookaheadSets(dfa);
|
||||||
|
IntervalSet[] altLookSets = factory.g.decisionLOOK.get(decision);
|
||||||
|
altLook = getAltLookaheadAsStringLists(altLookSets);
|
||||||
|
IntervalSet look = altLookSets[1];
|
||||||
|
IntervalSet followLook = altLookSets[2];
|
||||||
|
|
||||||
|
IntervalSet expecting = (IntervalSet)look.or(followLook);
|
||||||
|
this.error = new ThrowNoViableAlt(factory, blkAST, expecting);
|
||||||
|
System.out.println(blkAST.toStringTree()+" LL1OptionalBlockSingleAlt expecting="+expecting);
|
||||||
|
|
||||||
|
expr = addCodeForLookaheadTempVar(look);
|
||||||
|
followExpr = factory.getLL1Test(followLook, blkAST);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
import org.antlr.v4.misc.IntervalSet;
|
||||||
|
import org.antlr.v4.runtime.atn.StarBlockStartState;
|
||||||
|
import org.antlr.v4.tool.GrammarAST;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class LL1StarBlock extends LL1Loop {
|
||||||
|
/** Token names for each alt 0..n-1 */
|
||||||
|
public List<String[]> altLook;
|
||||||
|
public String loopLabel;
|
||||||
|
public String[] exitLook;
|
||||||
|
|
||||||
|
public LL1StarBlock(OutputModelFactory factory, GrammarAST blkAST, List<CodeBlock> alts) {
|
||||||
|
super(factory, blkAST, alts);
|
||||||
|
|
||||||
|
StarBlockStartState blkStart = (StarBlockStartState)blkAST.atnState;
|
||||||
|
this.decision = blkStart.decision;
|
||||||
|
|
||||||
|
/** Lookahead for each alt 1..n */
|
||||||
|
IntervalSet[] altLookSets = factory.g.decisionLOOK.get(decision);
|
||||||
|
IntervalSet lastLook = altLookSets[altLookSets.length-1];
|
||||||
|
IntervalSet[] copy = new IntervalSet[altLookSets.length-1];
|
||||||
|
System.arraycopy(altLookSets, 0, copy, 0, altLookSets.length-1); // remove last (exit) alt
|
||||||
|
altLookSets = copy;
|
||||||
|
altLook = getAltLookaheadAsStringLists(altLookSets);
|
||||||
|
loopLabel = factory.gen.target.getLoopLabel(blkAST);
|
||||||
|
|
||||||
|
this.exitLook =
|
||||||
|
factory.gen.target.getTokenTypesAsTargetLabels(factory.g, lastLook.toArray());
|
||||||
|
|
||||||
|
// this.sync = new Sync(factory, blkAST, expecting, decision, "iter");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
import org.antlr.v4.misc.IntervalSet;
|
||||||
|
import org.antlr.v4.runtime.atn.StarBlockStartState;
|
||||||
|
import org.antlr.v4.tool.GrammarAST;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class LL1StarBlockSingleAlt extends LL1Loop {
|
||||||
|
public LL1StarBlockSingleAlt(OutputModelFactory factory, GrammarAST starRoot, List<CodeBlock> alts) {
|
||||||
|
super(factory, starRoot, alts);
|
||||||
|
|
||||||
|
StarBlockStartState star = (StarBlockStartState)starRoot.atnState;
|
||||||
|
this.decision = star.decision;
|
||||||
|
// DFA dfa = factory.g.decisionDFAs.get(decision);
|
||||||
|
// IntervalSet[] altLookSets = LinearApproximator.getLL1LookaheadSets(dfa);
|
||||||
|
IntervalSet[] altLookSets = factory.g.decisionLOOK.get(decision);
|
||||||
|
IntervalSet enterLook = altLookSets[1];
|
||||||
|
IntervalSet exitLook = altLookSets[2];
|
||||||
|
loopExpr = addCodeForLoopLookaheadTempVar(enterLook);
|
||||||
|
|
||||||
|
IntervalSet enterExpecting = (IntervalSet)exitLook.or(enterLook);
|
||||||
|
this.sync = new Sync(factory, starRoot, enterExpecting, decision, "iter");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public interface LabeledOp {
|
||||||
|
public List<String> getLabels();
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
import org.antlr.v4.tool.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class Lexer extends OutputModelObject {
|
||||||
|
public String name;
|
||||||
|
public Map<String,Integer> tokens;
|
||||||
|
public LexerFile file;
|
||||||
|
public String[] tokenNames;
|
||||||
|
public Set<String> ruleNames;
|
||||||
|
public Collection<String> modes;
|
||||||
|
public LinkedHashMap<Integer, Action> actions;
|
||||||
|
public LinkedHashMap<Integer, Action> sempreds;
|
||||||
|
public SerializedATN atn;
|
||||||
|
|
||||||
|
public Lexer(OutputModelFactory factory, LexerFile file) {
|
||||||
|
this.factory = factory;
|
||||||
|
this.file = file; // who contains us?
|
||||||
|
name = factory.g.getRecognizerName();
|
||||||
|
tokens = new LinkedHashMap<String,Integer>();
|
||||||
|
LexerGrammar lg = (LexerGrammar)factory.g;
|
||||||
|
atn = new SerializedATN(factory, lg.atn);
|
||||||
|
modes = lg.modes.keySet();
|
||||||
|
|
||||||
|
for (String t : factory.g.tokenNameToTypeMap.keySet()) {
|
||||||
|
Integer ttype = factory.g.tokenNameToTypeMap.get(t);
|
||||||
|
if ( ttype>0 ) tokens.put(t, ttype);
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenNames = factory.g.getTokenDisplayNames();
|
||||||
|
ruleNames = factory.g.rules.keySet();
|
||||||
|
|
||||||
|
sempreds = new LinkedHashMap<Integer, Action>();
|
||||||
|
for (PredAST p : factory.g.sempreds.keySet()) {
|
||||||
|
sempreds.put(factory.g.sempreds.get(p), new Action(factory, p));
|
||||||
|
}
|
||||||
|
actions = new LinkedHashMap<Integer, Action>();
|
||||||
|
for (ActionAST a : factory.g.actions.keySet()) {
|
||||||
|
actions.put(factory.g.actions.get(a), new Action(factory, a));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
import org.antlr.v4.parse.ANTLRParser;
|
||||||
|
import org.antlr.v4.tool.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class MatchToken extends RuleElement implements LabeledOp {
|
||||||
|
public String name;
|
||||||
|
public BitSetDecl follow;
|
||||||
|
public List<String> labels = new ArrayList<String>();
|
||||||
|
|
||||||
|
public MatchToken(OutputModelFactory factory, TerminalAST ast, GrammarAST labelAST) {
|
||||||
|
super(factory, ast);
|
||||||
|
int ttype = factory.g.getTokenType(ast.getText());
|
||||||
|
name = factory.gen.target.getTokenTypeAsTargetLabel(factory.g, ttype);
|
||||||
|
if ( labelAST!=null ) {
|
||||||
|
String label = labelAST.getText();
|
||||||
|
labels.add(label);
|
||||||
|
TokenDecl d = new TokenDecl(factory, label);
|
||||||
|
factory.currentRule.peek().addDecl(d);
|
||||||
|
if ( labelAST.parent.getType() == ANTLRParser.PLUS_ASSIGN ) {
|
||||||
|
TokenListDecl l = new TokenListDecl(factory, factory.gen.target.getListLabel(label));
|
||||||
|
factory.currentRule.peek().addDecl(l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If action refs as token not label, we need to define implicit label
|
||||||
|
if ( factory.currentAlt.tokenRefsInActions.containsKey(ast.getText()) ) {
|
||||||
|
String label = factory.gen.target.getImplicitTokenLabel(ast.getText());
|
||||||
|
labels.add(label);
|
||||||
|
TokenDecl d = new TokenDecl(factory, label);
|
||||||
|
factory.currentRule.peek().addDecl(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
// LinearApproximator approx = new LinearApproximator(factory.g, ATN.INVALID_DECISION_NUMBER);
|
||||||
|
// IntervalSet fset = approx.FIRST(ast.ATNState.transition(0).target);
|
||||||
|
// System.out.println("follow match "+name+"="+fset);
|
||||||
|
// follow = factory.createFollowBitSet(ast, fset);
|
||||||
|
// factory.defineBitSet(follow);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getLabels() { return labels; }
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
import org.antlr.v4.tool.GrammarAST;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class OptionalBlock extends AltBlock {
|
||||||
|
public OptionalBlock(OutputModelFactory factory,
|
||||||
|
GrammarAST questionAST,
|
||||||
|
List<CodeBlock> alts)
|
||||||
|
{
|
||||||
|
super(factory, questionAST, alts);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
import org.antlr.v4.tool.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class Parser extends OutputModelObject {
|
||||||
|
public String name;
|
||||||
|
public Map<String,Integer> tokens;
|
||||||
|
public String[] tokenNames;
|
||||||
|
public Set<String> ruleNames;
|
||||||
|
public List<RuleFunction> funcs = new ArrayList<RuleFunction>();
|
||||||
|
public List<DynamicScopeStruct> scopes = new ArrayList<DynamicScopeStruct>();
|
||||||
|
public ParserFile file;
|
||||||
|
public SerializedATN atn;
|
||||||
|
public LinkedHashMap<Integer, ForcedAction> actions;
|
||||||
|
public LinkedHashMap<Integer, Action> sempreds;
|
||||||
|
|
||||||
|
public Parser(OutputModelFactory factory, ParserFile file) {
|
||||||
|
this.factory = factory;
|
||||||
|
this.file = file; // who contains us?
|
||||||
|
name = factory.g.getRecognizerName();
|
||||||
|
tokens = new LinkedHashMap<String,Integer>();
|
||||||
|
//tokens.putAll( factory.g.tokenNameToTypeMap );
|
||||||
|
for (String t : factory.g.tokenNameToTypeMap.keySet()) {
|
||||||
|
Integer ttype = factory.g.tokenNameToTypeMap.get(t);
|
||||||
|
if ( ttype>0 ) tokens.put(t, ttype);
|
||||||
|
}
|
||||||
|
// int numTokens = factory.g.getTokenTypes().size();
|
||||||
|
// for (int t=Token.MIN_TOKEN_TYPE; t<numTokens; t++) {
|
||||||
|
// String lit = factory.g.typeToStringLiteralList.get(t);
|
||||||
|
// if ( lit!=null ) tokens.put(lit, t);
|
||||||
|
// tokens.put(factory.g.typeToTokenList.get(t), t);
|
||||||
|
// }
|
||||||
|
tokenNames = factory.g.getTokenDisplayNames();
|
||||||
|
ruleNames = factory.g.rules.keySet();
|
||||||
|
for (AttributeDict d : factory.g.scopes.values()) {
|
||||||
|
scopes.add( new DynamicScopeStruct(factory, d.name, d.attributes.values()) );
|
||||||
|
}
|
||||||
|
for (Rule r : factory.g.rules.values()) {
|
||||||
|
//if ( r.isStartRule ) funcs.add( new StartRuleFunction(factory, r) );
|
||||||
|
funcs.add( new RuleFunction(factory, r) );
|
||||||
|
}
|
||||||
|
atn = new SerializedATN(factory, factory.g.atn);
|
||||||
|
|
||||||
|
sempreds = new LinkedHashMap<Integer, Action>();
|
||||||
|
for (PredAST p : factory.g.sempreds.keySet()) {
|
||||||
|
sempreds.put(factory.g.sempreds.get(p), new Action(factory, p));
|
||||||
|
}
|
||||||
|
actions = new LinkedHashMap<Integer, ForcedAction>();
|
||||||
|
for (ActionAST a : factory.g.actions.keySet()) {
|
||||||
|
actions.put(factory.g.actions.get(a), new ForcedAction(factory, a));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
// public List<String> getChildren() {
|
||||||
|
// final List<String> sup = super.getChildren();
|
||||||
|
// return new ArrayList<String>() {{ if ( sup!=null ) addAll(sup); add("funcs"); add("scopes"); }};
|
||||||
|
// }
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
import org.antlr.v4.tool.GrammarAST;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class ParserFile extends OutputModelObject {
|
||||||
|
public String fileName;
|
||||||
|
public Parser parser;
|
||||||
|
// public List<DFADecl> dfaDecls = new ArrayList<DFADecl>();
|
||||||
|
// public OrderedHashSet<BitSetDecl> bitSetDecls = new OrderedHashSet<BitSetDecl>();
|
||||||
|
public String TokenLabelType;
|
||||||
|
public String ASTLabelType;
|
||||||
|
public Map<String, Action> namedActions;
|
||||||
|
|
||||||
|
public ParserFile(OutputModelFactory factory, String fileName) {
|
||||||
|
super(factory);
|
||||||
|
this.fileName = fileName;
|
||||||
|
factory.file = this;
|
||||||
|
TokenLabelType = factory.gen.g.getOption("TokenLabelType");
|
||||||
|
ASTLabelType = factory.gen.g.getOption("ASTLabelType");
|
||||||
|
namedActions = new HashMap<String, Action>();
|
||||||
|
for (String name : factory.gen.g.namedActions.keySet()) {
|
||||||
|
GrammarAST ast = factory.gen.g.namedActions.get(name);
|
||||||
|
namedActions.put(name, new Action(factory, ast));
|
||||||
|
}
|
||||||
|
parser = new Parser(factory, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// public void defineBitSet(BitSetDecl b) {
|
||||||
|
// bitSetDecls.add(b);
|
||||||
|
// }
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
import org.antlr.v4.tool.GrammarAST;
|
||||||
|
|
||||||
|
public class RuleElement extends SrcOp {
|
||||||
|
/** Associated ATN state for this rule elements (action, token, ruleref, ...) */
|
||||||
|
public int stateNumber;
|
||||||
|
|
||||||
|
public RuleElement(OutputModelFactory factory, GrammarAST ast) {
|
||||||
|
super(factory, ast);
|
||||||
|
if ( ast.atnState!=null ) stateNumber = ast.atnState.stateNumber;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.runtime.tree.CommonTreeNodeStream;
|
||||||
|
import org.antlr.v4.codegen.*;
|
||||||
|
import org.antlr.v4.misc.*;
|
||||||
|
import org.antlr.v4.parse.*;
|
||||||
|
import org.antlr.v4.runtime.atn.ATNState;
|
||||||
|
import org.antlr.v4.tool.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class RuleFunction extends OutputModelObject {
|
||||||
|
public String name;
|
||||||
|
public List<String> modifiers;
|
||||||
|
public String ctxType;
|
||||||
|
public List<String> globalScopesUsed;
|
||||||
|
public Collection<String> ruleLabels;
|
||||||
|
public Collection<String> tokenLabels;
|
||||||
|
public List<String> elementsReferencedInRewrite;
|
||||||
|
public List<String> exceptions;
|
||||||
|
public Action finallyAction;
|
||||||
|
public Map<String, Action> namedActions;
|
||||||
|
|
||||||
|
public ATNState startState;
|
||||||
|
|
||||||
|
public StructDecl context;
|
||||||
|
public DynamicScopeStruct scope;
|
||||||
|
|
||||||
|
public int index;
|
||||||
|
|
||||||
|
public Collection<Attribute> args = null;
|
||||||
|
public OrderedHashSet<Decl> decls;
|
||||||
|
public SrcOp code;
|
||||||
|
|
||||||
|
public RuleFunction(OutputModelFactory factory) {
|
||||||
|
super(factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RuleFunction(OutputModelFactory factory, Rule r) {
|
||||||
|
super(factory);
|
||||||
|
this.name = r.name;
|
||||||
|
if ( r.modifiers!=null && r.modifiers.size()>0 ) {
|
||||||
|
this.modifiers = new ArrayList<String>();
|
||||||
|
for (GrammarAST t : r.modifiers) modifiers.add(t.getText());
|
||||||
|
}
|
||||||
|
modifiers = Utils.nodesToStrings(r.modifiers);
|
||||||
|
|
||||||
|
ctxType = factory.gen.target.getRuleFunctionContextStructName(r);
|
||||||
|
|
||||||
|
List<Attribute> argsAndReturnValues = new ArrayList<Attribute>();
|
||||||
|
List<Attribute> ctorAttrs = new ArrayList<Attribute>();
|
||||||
|
|
||||||
|
index = r.index;
|
||||||
|
|
||||||
|
if ( r.args!=null ) {
|
||||||
|
argsAndReturnValues.addAll(r.args.attributes.values());
|
||||||
|
args = r.args.attributes.values();
|
||||||
|
ctorAttrs.addAll(args);
|
||||||
|
}
|
||||||
|
if ( r.retvals!=null ) {
|
||||||
|
argsAndReturnValues.addAll(r.retvals.attributes.values());
|
||||||
|
}
|
||||||
|
if ( r.scope!=null ) {
|
||||||
|
scope = new DynamicScopeStruct(factory, factory.gen.target.getRuleDynamicScopeStructName(r.name),
|
||||||
|
r.scope.attributes.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
globalScopesUsed = Utils.apply(r.useScopes, "getText");
|
||||||
|
|
||||||
|
if ( argsAndReturnValues.size()>0 ) {
|
||||||
|
context = new StructDecl(factory, factory.gen.target.getRuleFunctionContextStructName(r),
|
||||||
|
argsAndReturnValues);
|
||||||
|
context.ctorAttrs = ctorAttrs;
|
||||||
|
}
|
||||||
|
|
||||||
|
ruleLabels = r.getLabelNames();
|
||||||
|
tokenLabels = r.getTokenRefs();
|
||||||
|
exceptions = Utils.nodesToStrings(r.exceptionActions);
|
||||||
|
if ( r.finallyAction!=null ) finallyAction = new Action(factory, r.finallyAction);
|
||||||
|
|
||||||
|
namedActions = new HashMap<String, Action>();
|
||||||
|
for (String name : r.namedActions.keySet()) {
|
||||||
|
GrammarAST ast = r.namedActions.get(name);
|
||||||
|
namedActions.put(name, new Action(factory, ast));
|
||||||
|
}
|
||||||
|
|
||||||
|
startState = factory.g.atn.ruleToStartState.get(r);
|
||||||
|
|
||||||
|
factory.currentRule.push(this);
|
||||||
|
GrammarASTAdaptor adaptor = new GrammarASTAdaptor(r.ast.token.getInputStream());
|
||||||
|
GrammarAST blk = (GrammarAST)r.ast.getFirstChildWithType(ANTLRParser.BLOCK);
|
||||||
|
CommonTreeNodeStream nodes = new CommonTreeNodeStream(adaptor,blk);
|
||||||
|
SourceGenTriggers genTriggers = new SourceGenTriggers(nodes, factory);
|
||||||
|
try {
|
||||||
|
code = genTriggers.block(null,null); // GEN Instr OBJECTS
|
||||||
|
}
|
||||||
|
catch (Exception e){
|
||||||
|
e.printStackTrace(System.err);
|
||||||
|
}
|
||||||
|
factory.currentRule.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addDecl(Decl d) {
|
||||||
|
if ( decls==null ) decls = new OrderedHashSet<Decl>();
|
||||||
|
decls.add(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
// public List<String> getChildren() {
|
||||||
|
// final List<String> sup = super.getChildren();
|
||||||
|
// return new ArrayList<String>() {{
|
||||||
|
// if ( sup!=null ) addAll(sup);
|
||||||
|
// add("context"); add("scope"); add("decls"); add("code");
|
||||||
|
// add("finallyAction"); add("namedActions");
|
||||||
|
// }};
|
||||||
|
// }
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
import org.antlr.v4.tool.GrammarAST;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class SemPred extends Action {
|
||||||
|
public SemPred(OutputModelFactory factory, GrammarAST ast) { super(factory,ast); }
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
import org.antlr.v4.tool.GrammarAST;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public abstract class SrcOp extends OutputModelObject {
|
||||||
|
/** Used to create unique var names etc... */
|
||||||
|
public int uniqueID;
|
||||||
|
|
||||||
|
public SrcOp() {;}
|
||||||
|
public SrcOp(OutputModelFactory factory) { super(factory); }
|
||||||
|
public SrcOp(OutputModelFactory factory, GrammarAST ast) {
|
||||||
|
super(factory,ast);
|
||||||
|
uniqueID = ast.token.getTokenIndex();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
import org.antlr.v4.runtime.atn.BlockStartState;
|
||||||
|
import org.antlr.v4.tool.GrammarAST;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class StarBlock extends Loop {
|
||||||
|
public String loopLabel;
|
||||||
|
|
||||||
|
public StarBlock(OutputModelFactory factory,
|
||||||
|
GrammarAST blkOrEbnfRootAST,
|
||||||
|
List<CodeBlock> alts)
|
||||||
|
{
|
||||||
|
super(factory, blkOrEbnfRootAST, alts);
|
||||||
|
loopLabel = factory.gen.target.getLoopLabel(blkOrEbnfRootAST);
|
||||||
|
BlockStartState star = (BlockStartState)blkOrEbnfRootAST.atnState;
|
||||||
|
decision = star.decision;
|
||||||
|
exitAlt = alts.size()+1;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
import org.antlr.v4.tool.Attribute;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class StructDecl extends Decl {
|
||||||
|
public List<Decl> attrs = new ArrayList<Decl>();
|
||||||
|
public Collection<Attribute> ctorAttrs;
|
||||||
|
|
||||||
|
public StructDecl(OutputModelFactory factory, String name, Collection<Attribute> attrList) {
|
||||||
|
super(factory, name);
|
||||||
|
for (Attribute a : attrList) {
|
||||||
|
attrs.add(new AttributeDecl(factory, a.name, a.decl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
// public List<String> getChildren() {
|
||||||
|
// final List<String> sup = super.getChildren();
|
||||||
|
// return new ArrayList<String>() {{
|
||||||
|
// if ( sup!=null ) addAll(sup); add("attrs");
|
||||||
|
// }};
|
||||||
|
// }
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
import org.antlr.v4.misc.IntervalSet;
|
||||||
|
import org.antlr.v4.tool.GrammarAST;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class Sync extends SrcOp {
|
||||||
|
public int decision;
|
||||||
|
public BitSetDecl expecting;
|
||||||
|
public Sync(OutputModelFactory factory,
|
||||||
|
GrammarAST blkOrEbnfRootAST,
|
||||||
|
IntervalSet expecting,
|
||||||
|
int decision,
|
||||||
|
String position)
|
||||||
|
{
|
||||||
|
super(factory, blkOrEbnfRootAST);
|
||||||
|
this.decision = decision;
|
||||||
|
this.expecting = factory.createExpectingBitSet(ast, decision, expecting, position);
|
||||||
|
factory.defineBitSet(this.expecting);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
import org.antlr.v4.misc.IntervalSet;
|
||||||
|
import org.antlr.v4.tool.GrammarAST;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class TestSet extends RuleElement {
|
||||||
|
public BitSetDecl set;
|
||||||
|
public TestSet(OutputModelFactory factory, GrammarAST blkAST, IntervalSet set) {
|
||||||
|
super(factory, blkAST);
|
||||||
|
this.set = factory.createTestBitSet(blkAST, set);
|
||||||
|
factory.defineBitSet(this.set);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
import org.antlr.v4.misc.IntervalSet;
|
||||||
|
import org.antlr.v4.tool.GrammarAST;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class TestSetInline extends SrcOp {
|
||||||
|
public String varName;
|
||||||
|
public String[] ttypes;
|
||||||
|
// public CaptureNextToken nextToken;
|
||||||
|
// public Choice choice;
|
||||||
|
public TestSetInline(OutputModelFactory factory, GrammarAST ast, IntervalSet set) {
|
||||||
|
super(factory, ast);
|
||||||
|
this.ttypes = factory.gen.target.getTokenTypesAsTargetLabels(factory.g, set.toArray());
|
||||||
|
this.varName = "_la";
|
||||||
|
// this.choice = choice;
|
||||||
|
// nextToken = new CaptureNextToken();
|
||||||
|
// choice.addPreambleOp(nextToken);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
import org.antlr.v4.misc.IntervalSet;
|
||||||
|
import org.antlr.v4.tool.GrammarAST;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class ThrowEarlyExitException extends ThrowRecognitionException {
|
||||||
|
public ThrowEarlyExitException(OutputModelFactory factory, GrammarAST ast, IntervalSet expecting) {
|
||||||
|
super(factory, ast, expecting);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
import org.antlr.v4.misc.IntervalSet;
|
||||||
|
import org.antlr.v4.tool.GrammarAST;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class ThrowNoViableAlt extends ThrowRecognitionException {
|
||||||
|
public ThrowNoViableAlt(OutputModelFactory factory, GrammarAST blkOrEbnfRootAST,
|
||||||
|
IntervalSet expecting)
|
||||||
|
{
|
||||||
|
super(factory, blkOrEbnfRootAST, expecting);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
import org.antlr.v4.misc.IntervalSet;
|
||||||
|
import org.antlr.v4.tool.GrammarAST;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class ThrowRecognitionException extends SrcOp {
|
||||||
|
public int decision;
|
||||||
|
public String grammarFile;
|
||||||
|
public int grammarLine;
|
||||||
|
public int grammarCharPosInLine;
|
||||||
|
public BitSetDecl expecting;
|
||||||
|
|
||||||
|
public ThrowRecognitionException(OutputModelFactory factory, GrammarAST ast, IntervalSet expecting) {
|
||||||
|
super(factory, ast);
|
||||||
|
//this.decision = ((BlockStartState)ast.ATNState).decision;
|
||||||
|
grammarLine = ast.getLine();
|
||||||
|
grammarLine = ast.getCharPositionInLine();
|
||||||
|
grammarFile = factory.g.fileName;
|
||||||
|
//this.expecting = factory.createExpectingBitSet(ast, decision, expecting, "error");
|
||||||
|
factory.defineBitSet(this.expecting);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class TokenDecl extends Decl {
|
||||||
|
public TokenDecl(OutputModelFactory factory, String varName) {
|
||||||
|
super(factory, varName);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class TokenListDecl extends Decl {
|
||||||
|
public TokenListDecl(OutputModelFactory factory, String varName) {
|
||||||
|
super(factory, varName);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package org.antlr.v4.codegen.model.actions;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class ActionText extends ActionChunk {
|
||||||
|
public String text;
|
||||||
|
|
||||||
|
public ActionText(String text) {
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package org.antlr.v4.codegen.model.actions;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class ArgRef extends ActionChunk {
|
||||||
|
public String name;
|
||||||
|
|
||||||
|
public ArgRef(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package org.antlr.v4.codegen.model.actions;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class DynScopeAttrRef extends ActionChunk {
|
||||||
|
public String scope;
|
||||||
|
public String attr;
|
||||||
|
|
||||||
|
public DynScopeAttrRef(String scope, String attr) {
|
||||||
|
this.attr = attr;
|
||||||
|
this.scope = scope;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package org.antlr.v4.codegen.model.actions;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class DynScopeRef extends ActionChunk {
|
||||||
|
public String scope;
|
||||||
|
|
||||||
|
public DynScopeRef(String scope) {
|
||||||
|
this.scope = scope;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package org.antlr.v4.codegen.model.actions;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class RetValueRef extends ActionChunk {
|
||||||
|
public String name;
|
||||||
|
|
||||||
|
public RetValueRef(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package org.antlr.v4.codegen.model.actions;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class RulePropertyRef extends ActionChunk {
|
||||||
|
public String label;
|
||||||
|
|
||||||
|
public RulePropertyRef(String label) {
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package org.antlr.v4.codegen.model.actions;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class RulePropertyRef_st extends RulePropertyRef {
|
||||||
|
public RulePropertyRef_st(String label) {
|
||||||
|
super(label);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package org.antlr.v4.codegen.model.actions;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class RulePropertyRef_start extends RulePropertyRef {
|
||||||
|
public RulePropertyRef_start(String label) {
|
||||||
|
super(label);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package org.antlr.v4.codegen.model.actions;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class RulePropertyRef_stop extends RulePropertyRef {
|
||||||
|
public RulePropertyRef_stop(String label) {
|
||||||
|
super(label);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package org.antlr.v4.codegen.model.actions;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class RulePropertyRef_text extends RulePropertyRef {
|
||||||
|
public RulePropertyRef_text(String label) {
|
||||||
|
super(label);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package org.antlr.v4.codegen.model.actions;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class RulePropertyRef_tree extends RulePropertyRef {
|
||||||
|
public RulePropertyRef_tree(String label) {
|
||||||
|
super(label);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package org.antlr.v4.codegen.model.actions;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class SetAttr extends ActionChunk {
|
||||||
|
public String name;
|
||||||
|
public List<ActionChunk> rhsChunks;
|
||||||
|
|
||||||
|
public SetAttr(String name, List<ActionChunk> rhsChunks) {
|
||||||
|
this.name = name;
|
||||||
|
this.rhsChunks = rhsChunks;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
// public List<String> getChildren() {
|
||||||
|
// final List<String> sup = super.getChildren();
|
||||||
|
// return new ArrayList<String>() {{
|
||||||
|
// if ( sup!=null ) addAll(sup);
|
||||||
|
// add("rhsChunks");
|
||||||
|
// }};
|
||||||
|
// }
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package org.antlr.v4.codegen.model.actions;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class SetDynScopeAttr extends ActionChunk {
|
||||||
|
public String scope;
|
||||||
|
public String attr;
|
||||||
|
public List<ActionChunk> rhsChunks;
|
||||||
|
|
||||||
|
public SetDynScopeAttr(String scope, String attr, List<ActionChunk> rhsChunks) {
|
||||||
|
this.scope = scope;
|
||||||
|
this.attr = attr;
|
||||||
|
this.rhsChunks = rhsChunks;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public List<String> getChildren() {
|
||||||
|
// final List<String> sup = super.getChildren();
|
||||||
|
// return new ArrayList<String>() {{
|
||||||
|
// if ( sup!=null ) addAll(sup);
|
||||||
|
// add("rhsChunks");
|
||||||
|
// }};
|
||||||
|
// }
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package org.antlr.v4.codegen.model.actions;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class SetDynScopeAttr_index extends SetDynScopeAttr {
|
||||||
|
public List<ActionChunk> indexChunks;
|
||||||
|
public SetDynScopeAttr_index(String scope, String attr, List<ActionChunk> indexChunks, List<ActionChunk> rhsChunks) {
|
||||||
|
super(scope, attr, rhsChunks);
|
||||||
|
this.indexChunks = indexChunks;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
// public List<String> getChildren() {
|
||||||
|
// final List<String> sup = super.getChildren();
|
||||||
|
// return new ArrayList<String>() {{
|
||||||
|
// if ( sup!=null ) addAll(sup);
|
||||||
|
// add("indexChunks");
|
||||||
|
// }};
|
||||||
|
// }
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package org.antlr.v4.codegen.model.actions;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class TokenPropertyRef extends ActionChunk {
|
||||||
|
public String label;
|
||||||
|
|
||||||
|
public TokenPropertyRef(String label) {
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package org.antlr.v4.codegen.model.actions;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class TokenPropertyRef_int extends TokenPropertyRef {
|
||||||
|
public TokenPropertyRef_int(String label) {
|
||||||
|
super(label);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package org.antlr.v4.codegen.model.actions;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class TokenPropertyRef_text extends TokenPropertyRef {
|
||||||
|
public TokenPropertyRef_text(String label) {
|
||||||
|
super(label);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package org.antlr.v4.codegen.model.actions;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class TokenPropertyRef_tree extends TokenPropertyRef {
|
||||||
|
public TokenPropertyRef_tree(String label) {
|
||||||
|
super(label);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package org.antlr.v4.codegen.model.actions;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class TokenPropertyRef_type extends TokenPropertyRef {
|
||||||
|
public TokenPropertyRef_type(String label) {
|
||||||
|
super(label);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package org.antlr.v4.codegen.model.ast;
|
||||||
|
|
||||||
|
import org.antlr.v4.codegen.model.SrcOp;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class MakeRoot extends SrcOp {
|
||||||
|
public SrcOp opWithResultToAdd;
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package org.antlr.v4.codegen.model;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public class dbg extends OutputModelObject {
|
||||||
|
}
|
|
@ -118,7 +118,7 @@ public class TokenVocabParser {
|
||||||
*/
|
*/
|
||||||
public File getImportedVocabFile() {
|
public File getImportedVocabFile() {
|
||||||
|
|
||||||
File f = new File(tool.getLibraryDirectory(),
|
File f = new File(tool.libDirectory,
|
||||||
File.separator +
|
File.separator +
|
||||||
vocabName +
|
vocabName +
|
||||||
CodeGenerator.VOCAB_FILE_EXTENSION);
|
CodeGenerator.VOCAB_FILE_EXTENSION);
|
||||||
|
@ -131,12 +131,7 @@ public class TokenVocabParser {
|
||||||
// files are generated (in the base, not relative to the input
|
// files are generated (in the base, not relative to the input
|
||||||
// location.)
|
// location.)
|
||||||
//
|
//
|
||||||
if (tool.haveOutputDir) {
|
f = new File(tool.outputDirectory, vocabName + CodeGenerator.VOCAB_FILE_EXTENSION);
|
||||||
f = new File(tool.getOutputDirectory(), vocabName + CodeGenerator.VOCAB_FILE_EXTENSION);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
f = new File(vocabName + CodeGenerator.VOCAB_FILE_EXTENSION);
|
|
||||||
}
|
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
package org.antlr.v4.semantics;
|
||||||
|
|
||||||
|
import org.antlr.runtime.*;
|
||||||
|
import org.antlr.v4.parse.ActionSplitter;
|
||||||
|
import org.antlr.v4.tool.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** Find token and rule refs, side-effect: update Alternatives */
|
||||||
|
public class ActionSniffer extends BlankActionSplitterListener {
|
||||||
|
public Grammar g;
|
||||||
|
public Rule r; // null if action outside of rule
|
||||||
|
public Alternative alt; // null if action outside of alt; could be in rule
|
||||||
|
public ActionAST node;
|
||||||
|
public Token actionToken; // token within action
|
||||||
|
public ErrorManager errMgr;
|
||||||
|
|
||||||
|
public ActionSniffer(Grammar g, Rule r, Alternative alt, ActionAST node, Token actionToken) {
|
||||||
|
this.g = g;
|
||||||
|
this.r = r;
|
||||||
|
this.alt = alt;
|
||||||
|
this.node = node;
|
||||||
|
this.actionToken = actionToken;
|
||||||
|
this.errMgr = g.tool.errMgr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void examineAction() {
|
||||||
|
//System.out.println("examine "+actionToken);
|
||||||
|
ANTLRStringStream in = new ANTLRStringStream(actionToken.getText());
|
||||||
|
in.setLine(actionToken.getLine());
|
||||||
|
in.setCharPositionInLine(actionToken.getCharPositionInLine());
|
||||||
|
ActionSplitter splitter = new ActionSplitter(in, this);
|
||||||
|
// forces eval, triggers listener methods
|
||||||
|
node.chunks = splitter.getActionTokens();
|
||||||
|
System.out.println(node.chunks);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void attr(String expr, Token x) {
|
||||||
|
List<TerminalAST> xRefs = alt.tokenRefs.get(x.getText());
|
||||||
|
if ( alt!=null && xRefs!=null ) {
|
||||||
|
alt.tokenRefsInActions.map(x.getText(), node);
|
||||||
|
}
|
||||||
|
List<GrammarAST> rRefs = alt.ruleRefs.get(x.getText());
|
||||||
|
if ( alt!=null && rRefs!=null ) {
|
||||||
|
alt.ruleRefsInActions.map(x.getText(), node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void qualifiedAttr(String expr, Token x, Token y) {
|
||||||
|
attr(expr, x);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,219 @@
|
||||||
|
package org.antlr.v4.semantics;
|
||||||
|
|
||||||
|
import org.antlr.runtime.*;
|
||||||
|
import org.antlr.v4.parse.*;
|
||||||
|
import org.antlr.v4.tool.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** Trigger checks for various kinds of attribute expressions.
|
||||||
|
* no side-effects.
|
||||||
|
*/
|
||||||
|
public class AttributeChecks implements ActionSplitterListener {
|
||||||
|
public Grammar g;
|
||||||
|
public Rule r; // null if action outside of rule
|
||||||
|
public Alternative alt; // null if action outside of alt; could be in rule
|
||||||
|
public ActionAST node;
|
||||||
|
public Token actionToken; // token within action
|
||||||
|
public ErrorManager errMgr;
|
||||||
|
|
||||||
|
public AttributeChecks(Grammar g, Rule r, Alternative alt, ActionAST node, Token actionToken) {
|
||||||
|
this.g = g;
|
||||||
|
this.r = r;
|
||||||
|
this.alt = alt;
|
||||||
|
this.node = node;
|
||||||
|
this.actionToken = actionToken;
|
||||||
|
this.errMgr = g.tool.errMgr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void checkAllAttributeExpressions(Grammar g) {
|
||||||
|
for (ActionAST act : g.namedActions.values()) {
|
||||||
|
AttributeChecks checker = new AttributeChecks(g, null, null, act, act.token);
|
||||||
|
checker.examineAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Rule r : g.rules.values()) {
|
||||||
|
for (ActionAST a : r.namedActions.values()) {
|
||||||
|
AttributeChecks checker = new AttributeChecks(g, r, null, a, a.token);
|
||||||
|
checker.examineAction();
|
||||||
|
}
|
||||||
|
for (int i=1; i<=r.numberOfAlts; i++) {
|
||||||
|
Alternative alt = r.alt[i];
|
||||||
|
for (ActionAST a : alt.actions) {
|
||||||
|
AttributeChecks checker =
|
||||||
|
new AttributeChecks(g, r, alt, a, a.token);
|
||||||
|
checker.examineAction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (ActionAST a : r.exceptionActions) {
|
||||||
|
AttributeChecks checker = new AttributeChecks(g, r, null, a, a.token);
|
||||||
|
checker.examineAction();
|
||||||
|
}
|
||||||
|
if ( r.finallyAction!=null ) {
|
||||||
|
AttributeChecks checker =
|
||||||
|
new AttributeChecks(g, r, null, r.finallyAction, r.finallyAction.token);
|
||||||
|
checker.examineAction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void examineAction() {
|
||||||
|
//System.out.println("examine "+actionToken);
|
||||||
|
ANTLRStringStream in = new ANTLRStringStream(actionToken.getText());
|
||||||
|
in.setLine(actionToken.getLine());
|
||||||
|
in.setCharPositionInLine(actionToken.getCharPositionInLine());
|
||||||
|
ActionSplitter splitter = new ActionSplitter(in, this);
|
||||||
|
// forces eval, triggers listener methods
|
||||||
|
node.chunks = splitter.getActionTokens();
|
||||||
|
System.out.println(node.chunks);
|
||||||
|
}
|
||||||
|
|
||||||
|
// LISTENER METHODS
|
||||||
|
|
||||||
|
public void setQualifiedAttr(String expr, Token x, Token y, Token rhs) {
|
||||||
|
qualifiedAttr(expr, x, y);
|
||||||
|
new AttributeChecks(g, r, alt, node, rhs).examineAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
// $x.y
|
||||||
|
public void qualifiedAttr(String expr, Token x, Token y) {
|
||||||
|
if ( node.resolver.resolveToAttribute(x.getText(), y.getText(), node)==null ) {
|
||||||
|
Rule rref = isolatedRuleRef(x.getText());
|
||||||
|
if ( rref!=null ) {
|
||||||
|
//if ( r.name.equals(x.getText()) ) return; // $a.x in rule a is ok
|
||||||
|
if ( rref.args!=null && rref.args.get(y.getText())!=null ) {
|
||||||
|
g.tool.errMgr.grammarError(ErrorType.INVALID_RULE_PARAMETER_REF,
|
||||||
|
g.fileName, y, y.getText(), expr);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
errMgr.grammarError(ErrorType.UNKNOWN_RULE_ATTRIBUTE,
|
||||||
|
g.fileName, y, y.getText(), rref.name, expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( !node.resolver.resolvesToAttributeDict(x.getText(), node) ) {
|
||||||
|
errMgr.grammarError(ErrorType.UNKNOWN_SIMPLE_ATTRIBUTE,
|
||||||
|
g.fileName, x, x.getText(), expr);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
errMgr.grammarError(ErrorType.UNKNOWN_ATTRIBUTE_IN_SCOPE,
|
||||||
|
g.fileName, y, y.getText(), expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttr(String expr, Token x, Token rhs) {
|
||||||
|
if ( node.resolver.resolveToAttribute(x.getText(), node)==null ) {
|
||||||
|
errMgr.grammarError(ErrorType.UNKNOWN_SIMPLE_ATTRIBUTE,
|
||||||
|
g.fileName, x, x.getText(), expr);
|
||||||
|
}
|
||||||
|
new AttributeChecks(g, r, alt, node, rhs).examineAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void attr(String expr, Token x) {
|
||||||
|
if ( node.resolver.resolveToAttribute(x.getText(), node)==null ) {
|
||||||
|
if ( node.resolver.resolveToDynamicScope(x.getText(), node)!=null ) {
|
||||||
|
return; // $S for scope S is ok
|
||||||
|
}
|
||||||
|
if ( node.resolver.resolvesToToken(x.getText(), node) ) {
|
||||||
|
return; // $ID for token ref or label of token
|
||||||
|
}
|
||||||
|
if ( node.resolver.resolvesToListLabel(x.getText(), node) ) {
|
||||||
|
return; // $ids for ids+=ID etc...
|
||||||
|
}
|
||||||
|
if ( isolatedRuleRef(x.getText())!=null ) {
|
||||||
|
errMgr.grammarError(ErrorType.ISOLATED_RULE_REF,
|
||||||
|
g.fileName, x, x.getText(), expr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
errMgr.grammarError(ErrorType.UNKNOWN_SIMPLE_ATTRIBUTE,
|
||||||
|
g.fileName, x, x.getText(), expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDynamicScopeAttr(String expr, Token x, Token y, Token rhs) {
|
||||||
|
//System.out.println("SET "+x+" :: "+y);
|
||||||
|
dynamicScopeAttr(expr, x, y);
|
||||||
|
new AttributeChecks(g, r, alt, node, rhs).examineAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dynamicScopeAttr(String expr, Token x, Token y) {
|
||||||
|
//System.out.println(x+" :: "+y);
|
||||||
|
AttributeDict s = node.resolver.resolveToDynamicScope(x.getText(), node);
|
||||||
|
if ( s==null ) {
|
||||||
|
errMgr.grammarError(ErrorType.UNKNOWN_DYNAMIC_SCOPE,
|
||||||
|
g.fileName, x, x.getText(), expr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Attribute a = s.get(y.getText());
|
||||||
|
if ( a==null ) {
|
||||||
|
errMgr.grammarError(ErrorType.UNKNOWN_DYNAMIC_SCOPE_ATTRIBUTE,
|
||||||
|
g.fileName, y, x.getText(), y.getText(), expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDynamicNegativeIndexedScopeAttr(String expr, Token x, Token y,
|
||||||
|
Token index, Token rhs) {
|
||||||
|
setDynamicScopeAttr(expr, x, y, rhs);
|
||||||
|
new AttributeChecks(g, r, alt, node, index).examineAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dynamicNegativeIndexedScopeAttr(String expr, Token x, Token y,
|
||||||
|
Token index) {
|
||||||
|
dynamicScopeAttr(expr, x, y);
|
||||||
|
new AttributeChecks(g, r, alt, node, index).examineAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDynamicAbsoluteIndexedScopeAttr(String expr, Token x, Token y,
|
||||||
|
Token index, Token rhs) {
|
||||||
|
setDynamicScopeAttr(expr, x, y, rhs);
|
||||||
|
new AttributeChecks(g, r, alt, node, index).examineAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dynamicAbsoluteIndexedScopeAttr(String expr, Token x, Token y,
|
||||||
|
Token index) {
|
||||||
|
dynamicScopeAttr(expr, x, y);
|
||||||
|
new AttributeChecks(g, r, alt, node, index).examineAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unknownSyntax(Token t) {
|
||||||
|
errMgr.grammarError(ErrorType.INVALID_TEMPLATE_ACTION,
|
||||||
|
g.fileName, t, t.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void text(String text) { }
|
||||||
|
|
||||||
|
// don't care
|
||||||
|
public void templateInstance(String expr) { }
|
||||||
|
public void indirectTemplateInstance(String expr) { }
|
||||||
|
public void setExprAttribute(String expr) { }
|
||||||
|
public void setSTAttribute(String expr) { }
|
||||||
|
public void templateExpr(String expr) { }
|
||||||
|
|
||||||
|
// SUPPORT
|
||||||
|
|
||||||
|
public Rule isolatedRuleRef(String x) {
|
||||||
|
if ( node.resolver instanceof Grammar ) return null;
|
||||||
|
|
||||||
|
if ( x.equals(r.name) ) return r;
|
||||||
|
List<LabelElementPair> labels = null;
|
||||||
|
if ( node.resolver instanceof Rule ) {
|
||||||
|
labels = r.getLabelDefs().get(x);
|
||||||
|
}
|
||||||
|
else if ( node.resolver instanceof Alternative ) {
|
||||||
|
labels = ((Alternative)node.resolver).labelDefs.get(x);
|
||||||
|
}
|
||||||
|
if ( labels!=null ) { // it's a label ref. is it a rule label?
|
||||||
|
LabelElementPair anyLabelDef = labels.get(0);
|
||||||
|
if ( anyLabelDef.type==LabelType.RULE_LABEL ) {
|
||||||
|
return g.getRule(anyLabelDef.element.getText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( node.resolver instanceof Alternative ) {
|
||||||
|
if ( ((Alternative)node.resolver).ruleRefs.get(x)!=null ) {
|
||||||
|
return g.getRule(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,442 @@
|
||||||
|
package org.antlr.v4.semantics;
|
||||||
|
|
||||||
|
import org.antlr.runtime.Token;
|
||||||
|
import org.antlr.v4.misc.Utils;
|
||||||
|
import org.antlr.v4.parse.ANTLRParser;
|
||||||
|
import org.antlr.v4.tool.*;
|
||||||
|
import org.stringtemplate.v4.misc.MultiMap;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/** No side-effects; BasicSemanticTriggers.g invokes check rules for these:
|
||||||
|
*
|
||||||
|
* FILE_AND_GRAMMAR_NAME_DIFFER
|
||||||
|
* LEXER_RULES_NOT_ALLOWED
|
||||||
|
* PARSER_RULES_NOT_ALLOWED
|
||||||
|
* CANNOT_ALIAS_TOKENS
|
||||||
|
* ARGS_ON_TOKEN_REF
|
||||||
|
* ILLEGAL_OPTION
|
||||||
|
* REWRITE_OR_OP_WITH_NO_OUTPUT_OPTION
|
||||||
|
* NO_RULES
|
||||||
|
* REWRITE_FOR_MULTI_ELEMENT_ALT
|
||||||
|
* HETERO_ILLEGAL_IN_REWRITE_ALT
|
||||||
|
* AST_OP_WITH_NON_AST_OUTPUT_OPTION
|
||||||
|
* AST_OP_IN_ALT_WITH_REWRITE
|
||||||
|
* CONFLICTING_OPTION_IN_TREE_FILTER
|
||||||
|
* WILDCARD_AS_ROOT
|
||||||
|
* INVALID_IMPORT
|
||||||
|
* TOKEN_VOCAB_IN_DELEGATE
|
||||||
|
* IMPORT_NAME_CLASH
|
||||||
|
* REPEATED_PREQUEL
|
||||||
|
* TOKEN_NAMES_MUST_START_UPPER
|
||||||
|
*
|
||||||
|
* TODO: 1 action per lex rule
|
||||||
|
*/
|
||||||
|
public class BasicSemanticChecks {
|
||||||
|
public static final Set legalLexerOptions =
|
||||||
|
new HashSet() {
|
||||||
|
{
|
||||||
|
add("language"); add("tokenVocab");
|
||||||
|
add("TokenLabelType");
|
||||||
|
add("superClass");
|
||||||
|
add("filter");
|
||||||
|
add("k");
|
||||||
|
add("backtrack");
|
||||||
|
add("memoize");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final Set legalParserOptions =
|
||||||
|
new HashSet() {
|
||||||
|
{
|
||||||
|
add("language"); add("tokenVocab");
|
||||||
|
add("output"); add("rewrite"); add("ASTLabelType");
|
||||||
|
add("TokenLabelType");
|
||||||
|
add("superClass");
|
||||||
|
add("k");
|
||||||
|
add("backtrack");
|
||||||
|
add("memoize");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final Set legalTreeParserOptions =
|
||||||
|
new HashSet() {
|
||||||
|
{
|
||||||
|
add("language"); add("tokenVocab");
|
||||||
|
add("output"); add("rewrite"); add("ASTLabelType");
|
||||||
|
add("TokenLabelType");
|
||||||
|
add("superClass");
|
||||||
|
add("k");
|
||||||
|
add("backtrack");
|
||||||
|
add("memoize");
|
||||||
|
add("filter");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final Set legalRuleOptions =
|
||||||
|
new HashSet() {
|
||||||
|
{
|
||||||
|
add("k"); add("greedy"); add("memoize");
|
||||||
|
add("backtrack"); add("strategy");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final Set legalBlockOptions =
|
||||||
|
new HashSet() {{add("k"); add("greedy"); add("backtrack"); add("memoize");}};
|
||||||
|
|
||||||
|
/** Legal options for terminal refs like ID<node=MyVarNode> */
|
||||||
|
public static final Set legalTokenOptions =
|
||||||
|
new HashSet() {
|
||||||
|
{
|
||||||
|
add(TerminalAST.defaultTokenOption);
|
||||||
|
add("associativity");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Set of valid imports. E.g., can only import a tree parser into
|
||||||
|
* another tree parser. Maps delegate to set of delegator grammar types.
|
||||||
|
* validDelegations.get(LEXER) gives list of the kinds of delegators
|
||||||
|
* that can import lexers.
|
||||||
|
*/
|
||||||
|
public static MultiMap<Integer,Integer> validImportTypes =
|
||||||
|
new MultiMap<Integer,Integer>() {
|
||||||
|
{
|
||||||
|
map(ANTLRParser.LEXER, ANTLRParser.LEXER);
|
||||||
|
map(ANTLRParser.LEXER, ANTLRParser.COMBINED);
|
||||||
|
|
||||||
|
map(ANTLRParser.PARSER, ANTLRParser.PARSER);
|
||||||
|
map(ANTLRParser.PARSER, ANTLRParser.COMBINED);
|
||||||
|
|
||||||
|
map(ANTLRParser.TREE, ANTLRParser.TREE);
|
||||||
|
|
||||||
|
map(ANTLRParser.COMBINED, ANTLRParser.COMBINED);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public Grammar g;
|
||||||
|
public ErrorManager errMgr;
|
||||||
|
|
||||||
|
public BasicSemanticChecks(Grammar g) {
|
||||||
|
this.g = g;
|
||||||
|
this.errMgr = g.tool.errMgr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkGrammarName(Token nameToken) {
|
||||||
|
if ( g.implicitLexer==null ) return;
|
||||||
|
String fullyQualifiedName = nameToken.getInputStream().getSourceName();
|
||||||
|
File f = new File(fullyQualifiedName);
|
||||||
|
String fileName = f.getName();
|
||||||
|
if ( !Utils.stripFileExtension(fileName).equals(nameToken.getText()) ) {
|
||||||
|
g.tool.errMgr.grammarError(ErrorType.FILE_AND_GRAMMAR_NAME_DIFFER,
|
||||||
|
fileName, nameToken, nameToken.getText(), fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkNumRules(GrammarAST rulesNode) {
|
||||||
|
if ( rulesNode.getChildCount()==0 ) {
|
||||||
|
GrammarAST root = (GrammarAST)rulesNode.getParent();
|
||||||
|
GrammarAST IDNode = (GrammarAST)root.getChild(0);
|
||||||
|
g.tool.errMgr.grammarError(ErrorType.NO_RULES, g.fileName,
|
||||||
|
null, IDNode.getText(), g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
List<GrammarAST> imports,
|
||||||
|
List<GrammarAST> tokens)
|
||||||
|
{
|
||||||
|
List<Token> secondOptionTokens = new ArrayList<Token>();
|
||||||
|
if ( options!=null && options.size()>1 ) {
|
||||||
|
secondOptionTokens.add(options.get(1).token);
|
||||||
|
}
|
||||||
|
if ( imports!=null && imports.size()>1 ) {
|
||||||
|
secondOptionTokens.add(imports.get(1).token);
|
||||||
|
}
|
||||||
|
if ( tokens!=null && tokens.size()>1 ) {
|
||||||
|
secondOptionTokens.add(tokens.get(1).token);
|
||||||
|
}
|
||||||
|
for (Token t : secondOptionTokens) {
|
||||||
|
String fileName = t.getInputStream().getSourceName();
|
||||||
|
g.tool.errMgr.grammarError(ErrorType.REPEATED_PREQUEL,
|
||||||
|
fileName, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkInvalidRuleDef(Token ruleID) {
|
||||||
|
String fileName = null;
|
||||||
|
if ( ruleID.getInputStream()!=null ) {
|
||||||
|
fileName = ruleID.getInputStream().getSourceName();
|
||||||
|
}
|
||||||
|
if ( g.isLexer() && Character.isLowerCase(ruleID.getText().charAt(0)) ) {
|
||||||
|
g.tool.errMgr.grammarError(ErrorType.PARSER_RULES_NOT_ALLOWED,
|
||||||
|
fileName, ruleID, ruleID.getText());
|
||||||
|
}
|
||||||
|
if ( (g.isParser()||g.isTreeGrammar()) &&
|
||||||
|
Character.isUpperCase(ruleID.getText().charAt(0)) )
|
||||||
|
{
|
||||||
|
g.tool.errMgr.grammarError(ErrorType.LEXER_RULES_NOT_ALLOWED,
|
||||||
|
fileName, ruleID, ruleID.getText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkInvalidRuleRef(Token ruleID) {
|
||||||
|
String fileName = ruleID.getInputStream().getSourceName();
|
||||||
|
if ( g.isLexer() && Character.isLowerCase(ruleID.getText().charAt(0)) ) {
|
||||||
|
g.tool.errMgr.grammarError(ErrorType.PARSER_RULES_NOT_ALLOWED,
|
||||||
|
fileName, ruleID, ruleID.getText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkTokenAlias(Token tokenID) {
|
||||||
|
String fileName = tokenID.getInputStream().getSourceName();
|
||||||
|
if ( Character.isLowerCase(tokenID.getText().charAt(0)) ) {
|
||||||
|
g.tool.errMgr.grammarError(ErrorType.TOKEN_NAMES_MUST_START_UPPER,
|
||||||
|
fileName,
|
||||||
|
tokenID,
|
||||||
|
tokenID.getText());
|
||||||
|
}
|
||||||
|
if ( !g.isCombined() ) {
|
||||||
|
g.tool.errMgr.grammarError(ErrorType.CANNOT_ALIAS_TOKENS,
|
||||||
|
fileName,
|
||||||
|
tokenID,
|
||||||
|
tokenID.getText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** At this point, we can only rule out obvious problems like ID[3]
|
||||||
|
* in parser. Might be illegal too in later stage when we see ID
|
||||||
|
* isn't a fragment.
|
||||||
|
*/
|
||||||
|
void checkTokenArgs(Token tokenID) {
|
||||||
|
String fileName = tokenID.getInputStream().getSourceName();
|
||||||
|
if ( !g.isLexer() ) {
|
||||||
|
g.tool.errMgr.grammarError(ErrorType.ARGS_ON_TOKEN_REF,
|
||||||
|
fileName, tokenID, tokenID.getText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check option is appropriate for grammar, rule, subrule */
|
||||||
|
boolean checkOptions(GrammarAST parent,
|
||||||
|
Token optionID, String value)
|
||||||
|
{
|
||||||
|
boolean ok = true;
|
||||||
|
if ( optionID.getText().equals("tokenVocab") &&
|
||||||
|
g.parent!=null ) // only allow tokenVocab option in root grammar
|
||||||
|
{
|
||||||
|
g.tool.errMgr.grammarWarning(ErrorType.TOKEN_VOCAB_IN_DELEGATE,
|
||||||
|
g.fileName,
|
||||||
|
optionID,
|
||||||
|
g.name);
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( parent.getType()==ANTLRParser.BLOCK ) {
|
||||||
|
if ( !legalBlockOptions.contains(optionID.getText()) ) { // block
|
||||||
|
g.tool.errMgr.grammarError(ErrorType.ILLEGAL_OPTION,
|
||||||
|
g.fileName,
|
||||||
|
optionID,
|
||||||
|
optionID.getText());
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( parent.getType()==ANTLRParser.RULE ) {
|
||||||
|
if ( !legalRuleOptions.contains(optionID.getText()) ) { // rule
|
||||||
|
g.tool.errMgr.grammarError(ErrorType.ILLEGAL_OPTION,
|
||||||
|
g.fileName,
|
||||||
|
optionID,
|
||||||
|
optionID.getText());
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( parent.getType()==ANTLRParser.GRAMMAR &&
|
||||||
|
!legalGrammarOption(optionID.getText()) ) { // grammar
|
||||||
|
g.tool.errMgr.grammarError(ErrorType.ILLEGAL_OPTION,
|
||||||
|
g.fileName,
|
||||||
|
optionID,
|
||||||
|
optionID.getText());
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check option is appropriate for token; parent is ELEMENT_OPTIONS */
|
||||||
|
boolean checkTokenOptions(GrammarAST parent,
|
||||||
|
Token optionID, String value)
|
||||||
|
{
|
||||||
|
String fileName = optionID.getInputStream().getSourceName();
|
||||||
|
// don't care about ID<ASTNodeName> options
|
||||||
|
if ( value!=null && !legalTokenOptions.contains(optionID.getText()) ) {
|
||||||
|
g.tool.errMgr.grammarError(ErrorType.ILLEGAL_OPTION,
|
||||||
|
fileName,
|
||||||
|
optionID,
|
||||||
|
optionID.getText());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// example (ALT_REWRITE (ALT (ID (ELEMENT_OPTIONS Foo))) (-> (ALT ID))
|
||||||
|
if ( parent.hasAncestor(ANTLRParser.ALT_REWRITE) ) {
|
||||||
|
g.tool.errMgr.grammarError(ErrorType.HETERO_ILLEGAL_IN_REWRITE_ALT,
|
||||||
|
fileName,
|
||||||
|
optionID);
|
||||||
|
|
||||||
|
}
|
||||||
|
// TODO: extra checks depending on terminal kind?
|
||||||
|
switch ( parent.getType() ) {
|
||||||
|
case ANTLRParser.TOKEN_REF :
|
||||||
|
case ANTLRParser.STRING_LITERAL :
|
||||||
|
case ANTLRParser.WILDCARD :
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean legalGrammarOption(String key) {
|
||||||
|
switch ( g.getType() ) {
|
||||||
|
case ANTLRParser.LEXER :
|
||||||
|
return legalLexerOptions.contains(key);
|
||||||
|
case ANTLRParser.PARSER :
|
||||||
|
return legalParserOptions.contains(key);
|
||||||
|
case ANTLRParser.TREE :
|
||||||
|
return legalTreeParserOptions.contains(key);
|
||||||
|
default :
|
||||||
|
return legalParserOptions.contains(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Rules in tree grammar that use -> rewrites and are spitting out
|
||||||
|
* templates via output=template and then use rewrite=true must only
|
||||||
|
* use -> on alts that are simple nodes or trees or single rule refs
|
||||||
|
* that match either nodes or trees.
|
||||||
|
*/
|
||||||
|
void checkRewriteForMultiRootAltInTreeGrammar(
|
||||||
|
Map<String, String> options,
|
||||||
|
Token altStart,
|
||||||
|
int alt)
|
||||||
|
{
|
||||||
|
if ( g.isTreeGrammar() &&
|
||||||
|
options!=null && options.get("output")!=null &&
|
||||||
|
options.get("output").equals("template") &&
|
||||||
|
options.get("rewrite")!=null &&
|
||||||
|
options.get("rewrite").equals("true") )
|
||||||
|
{
|
||||||
|
String fileName = altStart.getInputStream().getSourceName();
|
||||||
|
g.tool.errMgr.grammarWarning(ErrorType.REWRITE_FOR_MULTI_ELEMENT_ALT,
|
||||||
|
fileName,
|
||||||
|
altStart,
|
||||||
|
alt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkASTOps(Map<String, String> options,
|
||||||
|
GrammarAST op,
|
||||||
|
GrammarAST elementRoot)
|
||||||
|
{
|
||||||
|
RuleAST rule = (RuleAST)op.getAncestor(ANTLRParser.RULE);
|
||||||
|
String ruleName = rule.getChild(0).getText();
|
||||||
|
String fileName = elementRoot.token.getInputStream().getSourceName();
|
||||||
|
if ( options==null || !options.get("output").equals("AST") ) {
|
||||||
|
g.tool.errMgr.grammarWarning(ErrorType.AST_OP_WITH_NON_AST_OUTPUT_OPTION,
|
||||||
|
fileName,
|
||||||
|
elementRoot.token,
|
||||||
|
op.getText());
|
||||||
|
}
|
||||||
|
if ( options!=null && options.get("output")==null ) {
|
||||||
|
g.tool.errMgr.grammarWarning(ErrorType.REWRITE_OR_OP_WITH_NO_OUTPUT_OPTION,
|
||||||
|
fileName,
|
||||||
|
elementRoot.token,
|
||||||
|
ruleName);
|
||||||
|
}
|
||||||
|
if ( op.hasAncestor(ANTLRParser.ALT_REWRITE) ) {
|
||||||
|
GrammarAST rew = (GrammarAST)op.getAncestor(ANTLRParser.ALT_REWRITE);
|
||||||
|
int altNum = rew.getChildIndex() + 1; // alts are 1..n
|
||||||
|
g.tool.errMgr.grammarWarning(ErrorType.AST_OP_IN_ALT_WITH_REWRITE,
|
||||||
|
fileName,
|
||||||
|
elementRoot.token,
|
||||||
|
ruleName,
|
||||||
|
altNum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkRewriteOk(Map<String, String> options, GrammarAST elementRoot) {
|
||||||
|
RuleAST rule = (RuleAST)elementRoot.getAncestor(ANTLRParser.RULE);
|
||||||
|
String ruleName = rule.getChild(0).getText();
|
||||||
|
String fileName = elementRoot.token.getInputStream().getSourceName();
|
||||||
|
if ( options!=null && options.get("output")==null ) {
|
||||||
|
g.tool.errMgr.grammarWarning(ErrorType.REWRITE_OR_OP_WITH_NO_OUTPUT_OPTION,
|
||||||
|
fileName,
|
||||||
|
elementRoot.token,
|
||||||
|
ruleName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkTreeFilterOptions(GrammarRootAST root,
|
||||||
|
Map<String, String> options)
|
||||||
|
{
|
||||||
|
if ( options==null ) return;
|
||||||
|
String fileName = root.token.getInputStream().getSourceName();
|
||||||
|
String filter = options.get("filter");
|
||||||
|
if ( g.isTreeGrammar() && filter!=null && filter.equals("true") ) {
|
||||||
|
// check for conflicting options
|
||||||
|
// filter => backtrack=true (can't be false)
|
||||||
|
// filter&&output!=AST => error
|
||||||
|
// filter&&output=AST => rewrite=true
|
||||||
|
// any deviation from valid option set is an error
|
||||||
|
String backtrack = options.get("backtrack");
|
||||||
|
String output = options.get("output");
|
||||||
|
String rewrite = options.get("rewrite");
|
||||||
|
if ( backtrack!=null && !backtrack.toString().equals("true") ) {
|
||||||
|
g.tool.errMgr.grammarError(ErrorType.CONFLICTING_OPTION_IN_TREE_FILTER,
|
||||||
|
fileName,
|
||||||
|
root.token,
|
||||||
|
"backtrack", backtrack);
|
||||||
|
}
|
||||||
|
if ( output!=null && !output.equals("AST") ) {
|
||||||
|
g.tool.errMgr.grammarError(ErrorType.CONFLICTING_OPTION_IN_TREE_FILTER,
|
||||||
|
fileName,
|
||||||
|
root.token,
|
||||||
|
"output", output);
|
||||||
|
}
|
||||||
|
else if ( rewrite!=null && !rewrite.equals("true") ) { // && AST output
|
||||||
|
g.tool.errMgr.grammarError(ErrorType.CONFLICTING_OPTION_IN_TREE_FILTER,
|
||||||
|
fileName,
|
||||||
|
root.token,
|
||||||
|
"rewrite", rewrite);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkWildcardRoot(Token wild) {
|
||||||
|
String fileName = wild.getInputStream().getSourceName();
|
||||||
|
g.tool.errMgr.grammarError(ErrorType.WILDCARD_AS_ROOT,
|
||||||
|
fileName,
|
||||||
|
wild);
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkImport(Token importID) {
|
||||||
|
Grammar delegate = g.getImportedGrammar(importID.getText());
|
||||||
|
if ( delegate==null ) return;
|
||||||
|
List<Integer> validDelegators = validImportTypes.get(delegate.getType());
|
||||||
|
if ( validDelegators!=null && !validDelegators.contains(g.getType()) ) {
|
||||||
|
g.tool.errMgr.grammarError(ErrorType.INVALID_IMPORT,
|
||||||
|
g.fileName,
|
||||||
|
importID,
|
||||||
|
g, delegate);
|
||||||
|
}
|
||||||
|
if ( g.isCombined() &&
|
||||||
|
(delegate.name.equals(g.name+Grammar.getGrammarTypeToFileNameSuffix(ANTLRParser.LEXER))||
|
||||||
|
delegate.name.equals(g.name+Grammar.getGrammarTypeToFileNameSuffix(ANTLRParser.PARSER))) )
|
||||||
|
{
|
||||||
|
g.tool.errMgr.grammarError(ErrorType.IMPORT_NAME_CLASH,
|
||||||
|
g.fileName,
|
||||||
|
importID,
|
||||||
|
g, delegate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,240 @@
|
||||||
|
/*
|
||||||
|
[The "BSD license"]
|
||||||
|
Copyright (c) 2010 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Triggers for the basic semantics of the input. Side-effects:
|
||||||
|
* Set token, block, rule options in the tree. Load field option
|
||||||
|
* with grammar options. Only legal options are set.
|
||||||
|
*/
|
||||||
|
tree grammar BasicSemanticTriggers;
|
||||||
|
options {
|
||||||
|
language = Java;
|
||||||
|
tokenVocab = ANTLRParser;
|
||||||
|
ASTLabelType = GrammarAST;
|
||||||
|
filter = true;
|
||||||
|
//superClass = 'org.antlr.v4.runtime.tree.TreeFilter';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include the copyright in this source and also the generated source
|
||||||
|
@header {
|
||||||
|
/*
|
||||||
|
[The "BSD license"]
|
||||||
|
Copyright (c) 2010 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.semantics;
|
||||||
|
import org.antlr.v4.tool.*;
|
||||||
|
}
|
||||||
|
|
||||||
|
@members {
|
||||||
|
// TODO: SHOULD we fix up grammar AST to remove errors? Like kill refs to bad rules?
|
||||||
|
// that is, rewrite tree? maybe all passes are filters until code gen, which needs
|
||||||
|
// tree grammar. 'course we won't try codegen if errors.
|
||||||
|
public String name;
|
||||||
|
GrammarASTWithOptions root;
|
||||||
|
Grammar g; // which grammar are we checking
|
||||||
|
BasicSemanticChecks checker;
|
||||||
|
public BasicSemanticTriggers(TreeNodeStream input, Grammar g) {
|
||||||
|
this(input);
|
||||||
|
this.g = g;
|
||||||
|
checker = new BasicSemanticChecks(g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
topdown // do these on way down so options and such are set first
|
||||||
|
: grammarSpec
|
||||||
|
| rules
|
||||||
|
| mode
|
||||||
|
| option
|
||||||
|
| rule
|
||||||
|
| tokenAlias
|
||||||
|
| rewrite
|
||||||
|
;
|
||||||
|
|
||||||
|
bottomup // do these "inside to outside" of expressions.
|
||||||
|
: multiElementAltInTreeGrammar
|
||||||
|
| astOps
|
||||||
|
| ruleref
|
||||||
|
| tokenRefWithArgs
|
||||||
|
| elementOption
|
||||||
|
| checkGrammarOptions // do after we see everything
|
||||||
|
| wildcardRoot
|
||||||
|
;
|
||||||
|
|
||||||
|
grammarSpec
|
||||||
|
: ^( GRAMMAR ID DOC_COMMENT?
|
||||||
|
{
|
||||||
|
name = $ID.text;
|
||||||
|
checker.checkGrammarName($ID.token);
|
||||||
|
root = (GrammarRootAST)$start;
|
||||||
|
}
|
||||||
|
prequelConstructs ^(RULES .*)
|
||||||
|
)
|
||||||
|
;
|
||||||
|
|
||||||
|
checkGrammarOptions // when we get back to root
|
||||||
|
: GRAMMAR
|
||||||
|
{checker.checkTreeFilterOptions((GrammarRootAST)$GRAMMAR,
|
||||||
|
root.getOptions());}
|
||||||
|
;
|
||||||
|
|
||||||
|
/*
|
||||||
|
grammarType
|
||||||
|
@init {gtype = $start.getType(); root = (GrammarASTWithOptions)$start;}
|
||||||
|
: LEXER_GRAMMAR | PARSER_GRAMMAR | TREE_GRAMMAR | COMBINED_GRAMMAR
|
||||||
|
;
|
||||||
|
*/
|
||||||
|
|
||||||
|
prequelConstructs
|
||||||
|
: ( ^(o+=OPTIONS .+)
|
||||||
|
| ^(i+=IMPORT delegateGrammar+)
|
||||||
|
| ^(t+=TOKENS .+)
|
||||||
|
)*
|
||||||
|
{checker.checkNumPrequels($o, $i, $t);}
|
||||||
|
;
|
||||||
|
|
||||||
|
delegateGrammar
|
||||||
|
: ( ^(ASSIGN ID id=ID)
|
||||||
|
| id=ID
|
||||||
|
)
|
||||||
|
{checker.checkImport($id.token);}
|
||||||
|
;
|
||||||
|
|
||||||
|
rules : RULES {checker.checkNumRules($RULES);} ;
|
||||||
|
|
||||||
|
mode : ^(MODE ID .*) {checker.checkMode($ID.token);} ;
|
||||||
|
|
||||||
|
option // TODO: put in grammar, or rule, or block
|
||||||
|
: {inContext("OPTIONS")}? ^(ASSIGN o=ID optionValue)
|
||||||
|
{
|
||||||
|
GrammarAST parent = (GrammarAST)$start.getParent(); // OPTION
|
||||||
|
GrammarAST parentWithOptionKind = (GrammarAST)parent.getParent();
|
||||||
|
boolean ok = checker.checkOptions(parentWithOptionKind,
|
||||||
|
$ID.token, $optionValue.v);
|
||||||
|
// store options into XXX_GRAMMAR, RULE, BLOCK nodes
|
||||||
|
if ( ok ) {
|
||||||
|
((GrammarASTWithOptions)parentWithOptionKind).setOption($o.text, $optionValue.v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
optionValue returns [String v]
|
||||||
|
@init {$v = $start.token.getText();}
|
||||||
|
: ID
|
||||||
|
| STRING_LITERAL
|
||||||
|
| INT
|
||||||
|
| STAR
|
||||||
|
;
|
||||||
|
|
||||||
|
rule: ^( RULE r=ID .*) {checker.checkInvalidRuleDef($r.token);}
|
||||||
|
;
|
||||||
|
|
||||||
|
ruleref
|
||||||
|
: RULE_REF {checker.checkInvalidRuleRef($RULE_REF.token);}
|
||||||
|
;
|
||||||
|
|
||||||
|
tokenAlias
|
||||||
|
: {inContext("TOKENS")}? ^(ASSIGN ID STRING_LITERAL)
|
||||||
|
{checker.checkTokenAlias($ID.token);}
|
||||||
|
;
|
||||||
|
|
||||||
|
tokenRefWithArgs
|
||||||
|
: {!inContext("RESULT ...")}? // if not on right side of ->
|
||||||
|
^(TOKEN_REF ARG_ACTION)
|
||||||
|
{checker.checkTokenArgs($TOKEN_REF.token);}
|
||||||
|
;
|
||||||
|
|
||||||
|
elementOption
|
||||||
|
: {!inContext("RESULT ...")}? // not on right side of ->
|
||||||
|
^( ELEMENT_OPTIONS
|
||||||
|
( ^(ASSIGN o=ID value=ID)
|
||||||
|
| ^(ASSIGN o=ID value=STRING_LITERAL)
|
||||||
|
| o=ID
|
||||||
|
)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
boolean ok = checker.checkTokenOptions((GrammarAST)$o.getParent(),
|
||||||
|
$o.token, $value.text);
|
||||||
|
if ( ok ) {
|
||||||
|
if ( value!=null ) {
|
||||||
|
TerminalAST terminal = (TerminalAST)$start.getParent();
|
||||||
|
terminal.setOption($o.text, $value.text);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
TerminalAST terminal = (TerminalAST)$start.getParent();
|
||||||
|
terminal.setOption(TerminalAST.defaultTokenOption, $o.text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
// (ALT_REWRITE (ALT A B) ^( ALT ^( A B ) ) or ( ALT A )
|
||||||
|
multiElementAltInTreeGrammar
|
||||||
|
: {inContext("ALT_REWRITE")}?
|
||||||
|
^( ALT ~(SEMPRED|ACTION) ~(SEMPRED|ACTION)+ ) // > 1 element at outer level
|
||||||
|
{
|
||||||
|
int altNum = $start.getParent().getChildIndex() + 1; // alts are 1..n
|
||||||
|
GrammarAST firstNode = (GrammarAST)$start.getChild(0);
|
||||||
|
checker.checkRewriteForMultiRootAltInTreeGrammar(root.getOptions(),
|
||||||
|
firstNode.token,
|
||||||
|
altNum);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
// Check stuff like (^ A) (! r)
|
||||||
|
astOps
|
||||||
|
: ^(ROOT el=.) {checker.checkASTOps(root.getOptions(), $start, $el);}
|
||||||
|
| ^(BANG el=.) {checker.checkASTOps(root.getOptions(), $start, $el);}
|
||||||
|
;
|
||||||
|
|
||||||
|
rewrite
|
||||||
|
: (RESULT|ST_RESULT)
|
||||||
|
{checker.checkRewriteOk(root.getOptions(),$start);}
|
||||||
|
;
|
||||||
|
|
||||||
|
wildcardRoot
|
||||||
|
: ^(TREE_BEGIN WILDCARD .*)
|
||||||
|
{checker.checkWildcardRoot($WILDCARD.token);}
|
||||||
|
;
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,99 @@
|
||||||
|
SEMPRED=4
|
||||||
|
FORCED_ACTION=5
|
||||||
|
DOC_COMMENT=6
|
||||||
|
SRC=7
|
||||||
|
NLCHARS=8
|
||||||
|
COMMENT=9
|
||||||
|
DOUBLE_QUOTE_STRING_LITERAL=10
|
||||||
|
DOUBLE_ANGLE_STRING_LITERAL=11
|
||||||
|
ACTION_STRING_LITERAL=12
|
||||||
|
ACTION_CHAR_LITERAL=13
|
||||||
|
ARG_ACTION=14
|
||||||
|
NESTED_ACTION=15
|
||||||
|
ACTION=16
|
||||||
|
ACTION_ESC=17
|
||||||
|
WSNLCHARS=18
|
||||||
|
OPTIONS=19
|
||||||
|
TOKENS=20
|
||||||
|
SCOPE=21
|
||||||
|
IMPORT=22
|
||||||
|
FRAGMENT=23
|
||||||
|
LEXER=24
|
||||||
|
PARSER=25
|
||||||
|
TREE=26
|
||||||
|
GRAMMAR=27
|
||||||
|
PROTECTED=28
|
||||||
|
PUBLIC=29
|
||||||
|
PRIVATE=30
|
||||||
|
RETURNS=31
|
||||||
|
THROWS=32
|
||||||
|
CATCH=33
|
||||||
|
FINALLY=34
|
||||||
|
TEMPLATE=35
|
||||||
|
MODE=36
|
||||||
|
COLON=37
|
||||||
|
COLONCOLON=38
|
||||||
|
COMMA=39
|
||||||
|
SEMI=40
|
||||||
|
LPAREN=41
|
||||||
|
RPAREN=42
|
||||||
|
IMPLIES=43
|
||||||
|
LT=44
|
||||||
|
GT=45
|
||||||
|
ASSIGN=46
|
||||||
|
QUESTION=47
|
||||||
|
BANG=48
|
||||||
|
STAR=49
|
||||||
|
PLUS=50
|
||||||
|
PLUS_ASSIGN=51
|
||||||
|
OR=52
|
||||||
|
ROOT=53
|
||||||
|
DOLLAR=54
|
||||||
|
DOT=55
|
||||||
|
RANGE=56
|
||||||
|
ETC=57
|
||||||
|
RARROW=58
|
||||||
|
TREE_BEGIN=59
|
||||||
|
AT=60
|
||||||
|
NOT=61
|
||||||
|
RBRACE=62
|
||||||
|
TOKEN_REF=63
|
||||||
|
RULE_REF=64
|
||||||
|
INT=65
|
||||||
|
WSCHARS=66
|
||||||
|
ESC_SEQ=67
|
||||||
|
STRING_LITERAL=68
|
||||||
|
HEX_DIGIT=69
|
||||||
|
UNICODE_ESC=70
|
||||||
|
WS=71
|
||||||
|
ERRCHAR=72
|
||||||
|
RULE=73
|
||||||
|
RULES=74
|
||||||
|
RULEMODIFIERS=75
|
||||||
|
RULEACTIONS=76
|
||||||
|
BLOCK=77
|
||||||
|
REWRITE_BLOCK=78
|
||||||
|
OPTIONAL=79
|
||||||
|
CLOSURE=80
|
||||||
|
POSITIVE_CLOSURE=81
|
||||||
|
SYNPRED=82
|
||||||
|
CHAR_RANGE=83
|
||||||
|
EPSILON=84
|
||||||
|
ALT=85
|
||||||
|
ALTLIST=86
|
||||||
|
ID=87
|
||||||
|
ARG=88
|
||||||
|
ARGLIST=89
|
||||||
|
RET=90
|
||||||
|
COMBINED=91
|
||||||
|
INITACTION=92
|
||||||
|
LABEL=93
|
||||||
|
GATED_SEMPRED=94
|
||||||
|
SYN_SEMPRED=95
|
||||||
|
BACKTRACK_SEMPRED=96
|
||||||
|
WILDCARD=97
|
||||||
|
LIST=98
|
||||||
|
ELEMENT_OPTIONS=99
|
||||||
|
ST_RESULT=100
|
||||||
|
RESULT=101
|
||||||
|
ALT_REWRITE=102
|
|
@ -0,0 +1,57 @@
|
||||||
|
package org.antlr.v4.semantics;
|
||||||
|
|
||||||
|
import org.antlr.runtime.Token;
|
||||||
|
import org.antlr.v4.parse.ActionSplitterListener;
|
||||||
|
|
||||||
|
public class BlankActionSplitterListener implements ActionSplitterListener {
|
||||||
|
public void setQualifiedAttr(String expr, Token x, Token y, Token rhs) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void qualifiedAttr(String expr, Token x, Token y) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttr(String expr, Token x, Token rhs) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void attr(String expr, Token x) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDynamicScopeAttr(String expr, Token x, Token y, Token rhs) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dynamicScopeAttr(String expr, Token x, Token y) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDynamicNegativeIndexedScopeAttr(String expr, Token x, Token y, Token index, Token rhs) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dynamicNegativeIndexedScopeAttr(String expr, Token x, Token y, Token index) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDynamicAbsoluteIndexedScopeAttr(String expr, Token x, Token y, Token index, Token rhs) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dynamicAbsoluteIndexedScopeAttr(String expr, Token x, Token y, Token index) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void templateInstance(String expr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void indirectTemplateInstance(String expr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExprAttribute(String expr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSTAttribute(String expr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void templateExpr(String expr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unknownSyntax(Token t) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void text(String text) {
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,310 @@
|
||||||
|
/*
|
||||||
|
[The "BSD license"]
|
||||||
|
Copyright (c) 2010 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Collects rules, terminals, strings, actions, scopes etc... from AST
|
||||||
|
* No side-effects
|
||||||
|
*/
|
||||||
|
tree grammar CollectSymbols;
|
||||||
|
options {
|
||||||
|
language = Java;
|
||||||
|
tokenVocab = ANTLRParser;
|
||||||
|
ASTLabelType = GrammarAST;
|
||||||
|
filter = true;
|
||||||
|
//superClass = 'org.antlr.v4.runtime.tree.TreeFilter';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include the copyright in this source and also the generated source
|
||||||
|
@header {
|
||||||
|
/*
|
||||||
|
[The "BSD license"]
|
||||||
|
Copyright (c) 2010 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.semantics;
|
||||||
|
import org.antlr.v4.tool.*;
|
||||||
|
import org.antlr.v4.parse.*;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import org.stringtemplate.v4.misc.MultiMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@members {
|
||||||
|
Rule currentRule;
|
||||||
|
String currentMode = LexerGrammar.DEFAULT_MODE_NAME;
|
||||||
|
int currentAlt = 1; // 1..n
|
||||||
|
public List<Rule> rules = new ArrayList<Rule>();
|
||||||
|
public List<GrammarAST> rulerefs = new ArrayList<GrammarAST>();
|
||||||
|
public List<GrammarAST> qualifiedRulerefs = new ArrayList<GrammarAST>();
|
||||||
|
public List<GrammarAST> terminals = new ArrayList<GrammarAST>();
|
||||||
|
public List<GrammarAST> tokenIDRefs = new ArrayList<GrammarAST>();
|
||||||
|
public Set<String> strings = new HashSet<String>();
|
||||||
|
public List<GrammarAST> tokensDefs = new ArrayList<GrammarAST>();
|
||||||
|
public List<AttributeDict> scopes = new ArrayList<AttributeDict>();
|
||||||
|
public List<GrammarAST> actions = new ArrayList<GrammarAST>();
|
||||||
|
Grammar g; // which grammar are we checking
|
||||||
|
public CollectSymbols(TreeNodeStream input, Grammar g) {
|
||||||
|
this(input);
|
||||||
|
this.g = g;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
topdown
|
||||||
|
//@init {System.out.println("topdown: "+((Tree)input.LT(1)).getText());}
|
||||||
|
: globalScope
|
||||||
|
| globalNamedAction
|
||||||
|
| tokensSection
|
||||||
|
| mode
|
||||||
|
| rule
|
||||||
|
| ruleArg
|
||||||
|
| ruleReturns
|
||||||
|
| ruleNamedAction
|
||||||
|
| ruleScopeSpec
|
||||||
|
| ruleref
|
||||||
|
| rewriteElement // make sure we check this before terminal etc...
|
||||||
|
// want to match rewrite stuff all here
|
||||||
|
| terminal
|
||||||
|
| labeledElement
|
||||||
|
| setAlt
|
||||||
|
| ruleAction
|
||||||
|
| sempred
|
||||||
|
| finallyClause
|
||||||
|
| exceptionHandler
|
||||||
|
;
|
||||||
|
|
||||||
|
bottomup
|
||||||
|
: finishRule
|
||||||
|
;
|
||||||
|
|
||||||
|
globalScope
|
||||||
|
: {inContext("GRAMMAR")}? ^(SCOPE ID ACTION)
|
||||||
|
{
|
||||||
|
AttributeDict s = ScopeParser.parseDynamicScope($ACTION.text);
|
||||||
|
s.type = AttributeDict.DictType.GLOBAL_SCOPE;
|
||||||
|
s.name = $ID.text;
|
||||||
|
s.ast = $ACTION;
|
||||||
|
scopes.add(s);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
globalNamedAction
|
||||||
|
: {inContext("GRAMMAR")}? ^(AT ID? ID ACTION)
|
||||||
|
{actions.add($AT); ((ActionAST)$ACTION).resolver = g;}
|
||||||
|
;
|
||||||
|
|
||||||
|
tokensSection
|
||||||
|
: {inContext("TOKENS")}?
|
||||||
|
( ^(ASSIGN t=ID STRING_LITERAL)
|
||||||
|
{terminals.add($t); tokenIDRefs.add($t);
|
||||||
|
tokensDefs.add($ASSIGN); strings.add($STRING_LITERAL.text);}
|
||||||
|
| t=ID
|
||||||
|
{terminals.add($t); tokenIDRefs.add($t); tokensDefs.add($t);}
|
||||||
|
)
|
||||||
|
;
|
||||||
|
|
||||||
|
mode: ^(MODE ID .+) {currentMode = $ID.text;} ;
|
||||||
|
|
||||||
|
rule
|
||||||
|
@init {List<GrammarAST> modifiers = new ArrayList<GrammarAST>();}
|
||||||
|
: ^( RULE
|
||||||
|
name=ID (options {greedy=false;}:.)*
|
||||||
|
(^(RULEMODIFIERS (m=. {modifiers.add($m);})+))?
|
||||||
|
(^(AT ID ACTION))*
|
||||||
|
^(BLOCK .+)
|
||||||
|
.*
|
||||||
|
)
|
||||||
|
{
|
||||||
|
int numAlts = $RULE.getFirstChildWithType(BLOCK).getChildCount();
|
||||||
|
Rule r = new Rule(g, $name.text, (RuleAST)$RULE, numAlts);
|
||||||
|
if ( g.isLexer() ) r.mode = currentMode;
|
||||||
|
if ( modifiers.size()>0 ) r.modifiers = modifiers;
|
||||||
|
rules.add(r);
|
||||||
|
currentRule = r;
|
||||||
|
currentAlt = 1;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
setAlt
|
||||||
|
: {inContext("RULE BLOCK")}? ( ALT | ALT_REWRITE )
|
||||||
|
{
|
||||||
|
currentAlt = $start.getChildIndex()+1;
|
||||||
|
currentRule.alt[currentAlt].ast = (AltAST)$start;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
finishRule
|
||||||
|
: RULE {currentRule = null;}
|
||||||
|
;
|
||||||
|
|
||||||
|
ruleNamedAction
|
||||||
|
: {inContext("RULE")}? ^(AT ID ACTION)
|
||||||
|
{
|
||||||
|
currentRule.namedActions.put($ID.text,(ActionAST)$ACTION);
|
||||||
|
((ActionAST)$ACTION).resolver = currentRule;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
ruleAction
|
||||||
|
: {inContext("RULE ...")&&!inContext("SCOPE")&&
|
||||||
|
!inContext("CATCH")&&!inContext("FINALLY")&&!inContext("AT")}?
|
||||||
|
ACTION
|
||||||
|
{
|
||||||
|
currentRule.defineActionInAlt(currentAlt, (ActionAST)$ACTION);
|
||||||
|
((ActionAST)$ACTION).resolver = currentRule.alt[currentAlt];
|
||||||
|
}
|
||||||
|
| FORCED_ACTION
|
||||||
|
{
|
||||||
|
currentRule.defineActionInAlt(currentAlt, (ActionAST)$FORCED_ACTION);
|
||||||
|
((ActionAST)$FORCED_ACTION).resolver = currentRule.alt[currentAlt];
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
sempred
|
||||||
|
: {inContext("RULE ...")}?
|
||||||
|
SEMPRED
|
||||||
|
{
|
||||||
|
currentRule.definePredicateInAlt(currentAlt, (PredAST)$SEMPRED);
|
||||||
|
((PredAST)$SEMPRED).resolver = currentRule.alt[currentAlt];
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
exceptionHandler
|
||||||
|
: ^(CATCH ARG_ACTION ACTION)
|
||||||
|
{
|
||||||
|
currentRule.exceptionActions.add((ActionAST)$ACTION);
|
||||||
|
((ActionAST)$ACTION).resolver = currentRule;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
finallyClause
|
||||||
|
: ^(FINALLY ACTION)
|
||||||
|
{
|
||||||
|
currentRule.finallyAction = (ActionAST)$ACTION;
|
||||||
|
((ActionAST)$ACTION).resolver = currentRule;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
ruleArg
|
||||||
|
: {inContext("RULE")}? ARG_ACTION
|
||||||
|
{
|
||||||
|
currentRule.args = ScopeParser.parseTypeList($ARG_ACTION.text);
|
||||||
|
currentRule.args.type = AttributeDict.DictType.ARG;
|
||||||
|
currentRule.args.ast = $ARG_ACTION;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
ruleReturns
|
||||||
|
: ^(RETURNS ARG_ACTION)
|
||||||
|
{
|
||||||
|
currentRule.retvals = ScopeParser.parseTypeList($ARG_ACTION.text);
|
||||||
|
currentRule.retvals.type = AttributeDict.DictType.RET;
|
||||||
|
currentRule.retvals.ast = $ARG_ACTION;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
ruleScopeSpec
|
||||||
|
: {inContext("RULE")}?
|
||||||
|
( ^(SCOPE ACTION)
|
||||||
|
{
|
||||||
|
currentRule.scope = ScopeParser.parseDynamicScope($ACTION.text);
|
||||||
|
currentRule.scope.type = AttributeDict.DictType.RULE_SCOPE;
|
||||||
|
currentRule.scope.name = currentRule.name;
|
||||||
|
currentRule.scope.ast = $ACTION;
|
||||||
|
}
|
||||||
|
| ^(SCOPE ids+=ID+) {currentRule.useScopes = $ids;}
|
||||||
|
)
|
||||||
|
;
|
||||||
|
|
||||||
|
rewriteElement
|
||||||
|
//@init {System.out.println("rewriteElement: "+((Tree)input.LT(1)).getText());}
|
||||||
|
:
|
||||||
|
{inContext("RESULT ...")}? (TOKEN_REF|RULE_REF|STRING_LITERAL|LABEL)
|
||||||
|
{currentRule.alt[currentAlt].rewriteElements.add($start);}
|
||||||
|
;
|
||||||
|
|
||||||
|
labeledElement
|
||||||
|
@after {
|
||||||
|
LabelElementPair lp = new LabelElementPair(g, $id, $e, $start.getType());
|
||||||
|
//currentRule.labelDefs.map($id.text, lp);
|
||||||
|
currentRule.alt[currentAlt].labelDefs.map($id.text, lp);
|
||||||
|
}
|
||||||
|
: {inContext("RULE ...")}?
|
||||||
|
( ^(ASSIGN id=ID e=.)
|
||||||
|
| ^(PLUS_ASSIGN id=ID e=.)
|
||||||
|
)
|
||||||
|
;
|
||||||
|
|
||||||
|
terminal
|
||||||
|
: {!inContext("TOKENS ASSIGN")}? STRING_LITERAL
|
||||||
|
{
|
||||||
|
terminals.add($start);
|
||||||
|
strings.add($STRING_LITERAL.text);
|
||||||
|
if ( currentRule!=null ) {
|
||||||
|
currentRule.alt[currentAlt].tokenRefs.map($STRING_LITERAL.text, (TerminalAST)$STRING_LITERAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
| TOKEN_REF
|
||||||
|
{
|
||||||
|
terminals.add($TOKEN_REF);
|
||||||
|
tokenIDRefs.add($TOKEN_REF);
|
||||||
|
if ( currentRule!=null ) {
|
||||||
|
currentRule.alt[currentAlt].tokenRefs.map($TOKEN_REF.text, (TerminalAST)$TOKEN_REF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
ruleref
|
||||||
|
//@init {System.out.println("ruleref: "+((Tree)input.LT(1)).getText());}
|
||||||
|
: ( {inContext("DOT ...")}?
|
||||||
|
r=RULE_REF {qualifiedRulerefs.add((GrammarAST)$r.getParent());}
|
||||||
|
| r=RULE_REF
|
||||||
|
)
|
||||||
|
{
|
||||||
|
rulerefs.add($r);
|
||||||
|
if ( currentRule!=null ) {
|
||||||
|
currentRule.alt[currentAlt].ruleRefs.map($r.text, $r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
;
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,99 @@
|
||||||
|
SEMPRED=4
|
||||||
|
FORCED_ACTION=5
|
||||||
|
DOC_COMMENT=6
|
||||||
|
SRC=7
|
||||||
|
NLCHARS=8
|
||||||
|
COMMENT=9
|
||||||
|
DOUBLE_QUOTE_STRING_LITERAL=10
|
||||||
|
DOUBLE_ANGLE_STRING_LITERAL=11
|
||||||
|
ACTION_STRING_LITERAL=12
|
||||||
|
ACTION_CHAR_LITERAL=13
|
||||||
|
ARG_ACTION=14
|
||||||
|
NESTED_ACTION=15
|
||||||
|
ACTION=16
|
||||||
|
ACTION_ESC=17
|
||||||
|
WSNLCHARS=18
|
||||||
|
OPTIONS=19
|
||||||
|
TOKENS=20
|
||||||
|
SCOPE=21
|
||||||
|
IMPORT=22
|
||||||
|
FRAGMENT=23
|
||||||
|
LEXER=24
|
||||||
|
PARSER=25
|
||||||
|
TREE=26
|
||||||
|
GRAMMAR=27
|
||||||
|
PROTECTED=28
|
||||||
|
PUBLIC=29
|
||||||
|
PRIVATE=30
|
||||||
|
RETURNS=31
|
||||||
|
THROWS=32
|
||||||
|
CATCH=33
|
||||||
|
FINALLY=34
|
||||||
|
TEMPLATE=35
|
||||||
|
MODE=36
|
||||||
|
COLON=37
|
||||||
|
COLONCOLON=38
|
||||||
|
COMMA=39
|
||||||
|
SEMI=40
|
||||||
|
LPAREN=41
|
||||||
|
RPAREN=42
|
||||||
|
IMPLIES=43
|
||||||
|
LT=44
|
||||||
|
GT=45
|
||||||
|
ASSIGN=46
|
||||||
|
QUESTION=47
|
||||||
|
BANG=48
|
||||||
|
STAR=49
|
||||||
|
PLUS=50
|
||||||
|
PLUS_ASSIGN=51
|
||||||
|
OR=52
|
||||||
|
ROOT=53
|
||||||
|
DOLLAR=54
|
||||||
|
DOT=55
|
||||||
|
RANGE=56
|
||||||
|
ETC=57
|
||||||
|
RARROW=58
|
||||||
|
TREE_BEGIN=59
|
||||||
|
AT=60
|
||||||
|
NOT=61
|
||||||
|
RBRACE=62
|
||||||
|
TOKEN_REF=63
|
||||||
|
RULE_REF=64
|
||||||
|
INT=65
|
||||||
|
WSCHARS=66
|
||||||
|
ESC_SEQ=67
|
||||||
|
STRING_LITERAL=68
|
||||||
|
HEX_DIGIT=69
|
||||||
|
UNICODE_ESC=70
|
||||||
|
WS=71
|
||||||
|
ERRCHAR=72
|
||||||
|
RULE=73
|
||||||
|
RULES=74
|
||||||
|
RULEMODIFIERS=75
|
||||||
|
RULEACTIONS=76
|
||||||
|
BLOCK=77
|
||||||
|
REWRITE_BLOCK=78
|
||||||
|
OPTIONAL=79
|
||||||
|
CLOSURE=80
|
||||||
|
POSITIVE_CLOSURE=81
|
||||||
|
SYNPRED=82
|
||||||
|
CHAR_RANGE=83
|
||||||
|
EPSILON=84
|
||||||
|
ALT=85
|
||||||
|
ALTLIST=86
|
||||||
|
ID=87
|
||||||
|
ARG=88
|
||||||
|
ARGLIST=89
|
||||||
|
RET=90
|
||||||
|
COMBINED=91
|
||||||
|
INITACTION=92
|
||||||
|
LABEL=93
|
||||||
|
GATED_SEMPRED=94
|
||||||
|
SYN_SEMPRED=95
|
||||||
|
BACKTRACK_SEMPRED=96
|
||||||
|
WILDCARD=97
|
||||||
|
LIST=98
|
||||||
|
ELEMENT_OPTIONS=99
|
||||||
|
ST_RESULT=100
|
||||||
|
RESULT=101
|
||||||
|
ALT_REWRITE=102
|
|
@ -0,0 +1,175 @@
|
||||||
|
package org.antlr.v4.semantics;
|
||||||
|
|
||||||
|
import org.antlr.runtime.RecognitionException;
|
||||||
|
import org.antlr.runtime.tree.BufferedTreeNodeStream;
|
||||||
|
import org.antlr.v4.parse.*;
|
||||||
|
import org.antlr.v4.tool.*;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/** Do as much semantic checking as we can and fill in grammar
|
||||||
|
* with rules, dynamic scopes, actions, and token definitions.
|
||||||
|
* The only side effects are in the grammar pass to process().
|
||||||
|
* We consume a bunch of memory here while we build up data structures
|
||||||
|
* to perform checking, but all of it goes away after this pipeline object
|
||||||
|
* gets garbage collected.
|
||||||
|
*
|
||||||
|
* After this pipeline finishes, we can be sure that the grammar
|
||||||
|
* is syntactically correct and that it's semantically correct enough for us
|
||||||
|
* to attempt grammar analysis. We have assigned all token types.
|
||||||
|
* Note that imported grammars bring in token and rule definitions
|
||||||
|
* but only the root grammar and any implicitly created lexer grammar
|
||||||
|
* get their token definitions filled up. We are treating the
|
||||||
|
* imported grammars like includes (the generated code treats them
|
||||||
|
* as separate objects, however).
|
||||||
|
*/
|
||||||
|
public class SemanticPipeline {
|
||||||
|
public Grammar g;
|
||||||
|
|
||||||
|
public SemanticPipeline(Grammar g) {
|
||||||
|
this.g = g;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void process() {
|
||||||
|
if ( g.ast==null ) return;
|
||||||
|
|
||||||
|
// VALIDATE AST STRUCTURE
|
||||||
|
GrammarASTAdaptor adaptor = new GrammarASTAdaptor();
|
||||||
|
// use buffered node stream as we will look around in stream
|
||||||
|
// to give good error messages.
|
||||||
|
BufferedTreeNodeStream nodes =
|
||||||
|
new BufferedTreeNodeStream(adaptor,g.ast);
|
||||||
|
ASTVerifier walker = new ASTVerifier(nodes);
|
||||||
|
try {walker.grammarSpec();}
|
||||||
|
catch (RecognitionException re) {
|
||||||
|
ErrorManager.fatalInternalError("bad grammar AST structure", re);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DO BASIC / EASY SEMANTIC CHECKS
|
||||||
|
nodes.reset();
|
||||||
|
BasicSemanticTriggers basics = new BasicSemanticTriggers(nodes,g);
|
||||||
|
basics.downup(g.ast);
|
||||||
|
|
||||||
|
// don't continue if we get errors in this basic check
|
||||||
|
if ( false ) return;
|
||||||
|
|
||||||
|
// COLLECT SYMBOLS: RULES, ACTIONS, TERMINALS, ...
|
||||||
|
nodes.reset();
|
||||||
|
CollectSymbols collector = new CollectSymbols(nodes,g);
|
||||||
|
collector.downup(g.ast); // no side-effects; compute lists
|
||||||
|
|
||||||
|
// CHECK FOR SYMBOL COLLISIONS
|
||||||
|
SymbolChecks symcheck = new SymbolChecks(g, collector);
|
||||||
|
symcheck.examine(); // side-effect: strip away redef'd rules.
|
||||||
|
|
||||||
|
// don't continue if we get symbol errors
|
||||||
|
//if ( ErrorManager.getNumErrors()>0 ) return;
|
||||||
|
// hmm...we don't get missing arg errors and such if we bail out here
|
||||||
|
|
||||||
|
// STORE RULES/ACTIONS/SCOPES IN GRAMMAR
|
||||||
|
for (Rule r : collector.rules) g.defineRule(r);
|
||||||
|
for (AttributeDict s : collector.scopes) g.defineScope(s);
|
||||||
|
for (GrammarAST a : collector.actions) g.defineAction(a);
|
||||||
|
|
||||||
|
// LINK ALT NODES WITH Alternatives
|
||||||
|
for (Rule r : g.rules.values()) {
|
||||||
|
for (int i=1; i<=r.numberOfAlts; i++) {
|
||||||
|
r.alt[i].ast.alt = r.alt[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK RULE REFS NOW (that we've defined rules in grammar)
|
||||||
|
symcheck.checkRuleArgs(g, collector.rulerefs);
|
||||||
|
identifyStartRules(collector);
|
||||||
|
symcheck.checkForQualifiedRuleIssues(g, collector.qualifiedRulerefs);
|
||||||
|
|
||||||
|
// don't continue if we got symbol errors
|
||||||
|
if ( g.tool.getNumErrors()>0 ) return;
|
||||||
|
|
||||||
|
// CHECK ATTRIBUTE EXPRESSIONS FOR SEMANTIC VALIDITY
|
||||||
|
AttributeChecks.checkAllAttributeExpressions(g);
|
||||||
|
|
||||||
|
// ASSIGN TOKEN TYPES
|
||||||
|
String vocab = g.getOption("tokenVocab");
|
||||||
|
if ( vocab!=null ) {
|
||||||
|
TokenVocabParser vparser = new TokenVocabParser(g.tool, vocab);
|
||||||
|
Map<String,Integer> tokens = vparser.load();
|
||||||
|
System.out.println("tokens="+tokens);
|
||||||
|
for (String t : tokens.keySet()) {
|
||||||
|
if ( t.charAt(0)=='\'' ) g.defineStringLiteral(t, tokens.get(t));
|
||||||
|
else g.defineTokenName(t, tokens.get(t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( g.isLexer() ) assignLexerTokenTypes(g, collector);
|
||||||
|
else assignTokenTypes(g, collector, symcheck);
|
||||||
|
|
||||||
|
UseDefAnalyzer.checkRewriteElementsPresentOnLeftSide(g);
|
||||||
|
UseDefAnalyzer.trackTokenRuleRefsInActions(g);
|
||||||
|
}
|
||||||
|
|
||||||
|
void identifyStartRules(CollectSymbols collector) {
|
||||||
|
for (GrammarAST ref : collector.rulerefs) {
|
||||||
|
String ruleName = ref.getText();
|
||||||
|
Rule r = g.getRule(ruleName);
|
||||||
|
if ( r!=null ) r.isStartRule = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void assignLexerTokenTypes(Grammar g, CollectSymbols collector) {
|
||||||
|
Grammar G = g.getOutermostGrammar(); // put in root, even if imported
|
||||||
|
for (GrammarAST def : collector.tokensDefs) {
|
||||||
|
if ( def.getType()== ANTLRParser.ID ) G.defineTokenName(def.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEFINE TOKEN TYPES FOR NONFRAGMENT RULES
|
||||||
|
for (Rule r : g.rules.values()) {
|
||||||
|
if ( !r.isFragment() ) G.defineTokenName(r.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOR ALL X : 'xxx'; RULES, DEFINE 'xxx' AS TYPE X
|
||||||
|
Map<String,String> litAliases = Grammar.getStringLiteralAliasesFromLexerRules(g.ast);
|
||||||
|
if ( litAliases!=null ) {
|
||||||
|
for (String lit : litAliases.keySet()) {
|
||||||
|
G.defineTokenAlias(litAliases.get(lit), lit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void assignTokenTypes(Grammar g, CollectSymbols collector, SymbolChecks symcheck) {
|
||||||
|
if ( g.implicitLexerOwner!=null ) {
|
||||||
|
// copy vocab from combined to implicit lexer
|
||||||
|
g.importVocab(g.implicitLexerOwner);
|
||||||
|
System.out.println("tokens="+g.tokenNameToTypeMap);
|
||||||
|
System.out.println("strings="+g.stringLiteralToTypeMap);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Grammar G = g.getOutermostGrammar(); // put in root, even if imported
|
||||||
|
|
||||||
|
// DEFINE tokens { X='x'; } ALIASES
|
||||||
|
for (GrammarAST alias : collector.tokensDefs) {
|
||||||
|
if ( alias.getType()== ANTLRParser.ASSIGN ) {
|
||||||
|
String name = alias.getChild(0).getText();
|
||||||
|
String lit = alias.getChild(1).getText();
|
||||||
|
G.defineTokenAlias(name, lit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEFINE TOKEN TYPES FOR X : 'x' ; RULES
|
||||||
|
Map<String,String> litAliases = Grammar.getStringLiteralAliasesFromLexerRules(g.ast);
|
||||||
|
if ( litAliases!=null ) {
|
||||||
|
for (String lit : litAliases.keySet()) {
|
||||||
|
G.defineTokenAlias(litAliases.get(lit), lit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEFINE TOKEN TYPES FOR TOKEN REFS LIKE ID, INT
|
||||||
|
for (String id : symcheck.tokenIDs) { G.defineTokenName(id); }
|
||||||
|
|
||||||
|
// DEFINE TOKEN TYPES FOR STRING LITERAL REFS LIKE 'while', ';'
|
||||||
|
for (String s : collector.strings) { G.defineStringLiteral(s); }
|
||||||
|
// System.out.println("tokens="+G.tokenNameToTypeMap);
|
||||||
|
// System.out.println("strings="+G.stringLiteralToTypeMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,316 @@
|
||||||
|
package org.antlr.v4.semantics;
|
||||||
|
|
||||||
|
import org.antlr.runtime.Token;
|
||||||
|
import org.antlr.v4.parse.ANTLRParser;
|
||||||
|
import org.antlr.v4.tool.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/** Check for symbol problems; no side-effects. Inefficient to walk rules
|
||||||
|
* and such multiple times, but I like isolating all error checking outside
|
||||||
|
* of code that actually defines symbols etc...
|
||||||
|
*
|
||||||
|
* Side-effect: strip away redef'd rules.
|
||||||
|
*/
|
||||||
|
public class SymbolChecks {
|
||||||
|
Grammar g;
|
||||||
|
CollectSymbols collector;
|
||||||
|
Map<String, Rule> nameToRuleMap = new HashMap<String, Rule>();
|
||||||
|
Set<String> tokenIDs = new HashSet<String>();
|
||||||
|
Set<String> globalScopeNames = new HashSet<String>();
|
||||||
|
Map<String, Set<String>> actionScopeToActionNames = new HashMap<String, Set<String>>();
|
||||||
|
public ErrorManager errMgr;
|
||||||
|
|
||||||
|
public SymbolChecks(Grammar g, CollectSymbols collector) {
|
||||||
|
this.g = g;
|
||||||
|
this.collector = collector;
|
||||||
|
this.errMgr = g.tool.errMgr;
|
||||||
|
/*
|
||||||
|
System.out.println("rules="+collector.rules);
|
||||||
|
System.out.println("rulerefs="+collector.rulerefs);
|
||||||
|
System.out.println("tokenIDRefs="+collector.tokenIDRefs);
|
||||||
|
System.out.println("terminals="+collector.terminals);
|
||||||
|
System.out.println("strings="+collector.strings);
|
||||||
|
System.out.println("tokensDef="+collector.tokensDefs);
|
||||||
|
System.out.println("actions="+collector.actions);
|
||||||
|
System.out.println("scopes="+collector.scopes);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
public void examine() {
|
||||||
|
// methods affect fields, but no side-effects outside this object
|
||||||
|
// So, call order sensitive
|
||||||
|
checkScopeRedefinitions(collector.scopes); // sets globalScopeNames
|
||||||
|
//checkForImportedRuleIssues(collector.qualifiedRulerefs);
|
||||||
|
checkForRuleConflicts(collector.rules); // sets nameToRuleMap
|
||||||
|
checkActionRedefinitions(collector.actions); // sets actionScopeToActionNames
|
||||||
|
checkTokenAliasRedefinitions(collector.tokensDefs);
|
||||||
|
//checkRuleArgs(collector.rulerefs);
|
||||||
|
checkForTokenConflicts(collector.tokenIDRefs); // sets tokenIDs
|
||||||
|
checkForLabelConflicts(collector.rules);
|
||||||
|
//checkRewriteElementsPresentOnLeftSide(collector.rules); // move to after token type assignment
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkForRuleConflicts(List<Rule> rules) {
|
||||||
|
if ( rules==null ) return;
|
||||||
|
for (Rule r : collector.rules) {
|
||||||
|
if ( nameToRuleMap.get(r.name)==null ) {
|
||||||
|
nameToRuleMap.put(r.name, r);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
GrammarAST idNode = (GrammarAST)r.ast.getChild(0);
|
||||||
|
errMgr.grammarError(ErrorType.RULE_REDEFINITION,
|
||||||
|
g.fileName, idNode.token, r.name);
|
||||||
|
}
|
||||||
|
if ( globalScopeNames.contains(r.name) ) {
|
||||||
|
GrammarAST idNode = (GrammarAST)r.ast.getChild(0);
|
||||||
|
errMgr.grammarError(ErrorType.SYMBOL_CONFLICTS_WITH_GLOBAL_SCOPE,
|
||||||
|
g.fileName, idNode.token, r.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkScopeRedefinitions(List<AttributeDict> dicts) {
|
||||||
|
if ( dicts ==null ) return;
|
||||||
|
for (int i=0; i< dicts.size(); i++) {
|
||||||
|
AttributeDict s = dicts.get(i);
|
||||||
|
//GrammarAST idNode = (GrammarAST)s.getChild(0);
|
||||||
|
if ( !globalScopeNames.contains(s.getName()) ) {
|
||||||
|
globalScopeNames.add(s.getName());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Token idNode = ((GrammarAST) s.ast.getParent().getChild(0)).token;
|
||||||
|
errMgr.grammarError(ErrorType.SCOPE_REDEFINITION,
|
||||||
|
g.fileName, idNode, s.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkTokenAliasRedefinitions(List<GrammarAST> aliases) {
|
||||||
|
if ( aliases==null ) return;
|
||||||
|
Map<String, GrammarAST> aliasTokenNames = new HashMap<String, GrammarAST>();
|
||||||
|
for (int i=0; i< aliases.size(); i++) {
|
||||||
|
GrammarAST a = aliases.get(i);
|
||||||
|
GrammarAST idNode = a;
|
||||||
|
if ( a.getType()== ANTLRParser.ASSIGN ) {
|
||||||
|
idNode = (GrammarAST)a.getChild(0);
|
||||||
|
if ( g!=g.getOutermostGrammar() ) {
|
||||||
|
errMgr.grammarError(ErrorType.TOKEN_ALIAS_IN_DELEGATE,
|
||||||
|
g.fileName, idNode.token, idNode.getText(), g.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GrammarAST prev = aliasTokenNames.get(idNode.getText());
|
||||||
|
if ( prev==null ) {
|
||||||
|
aliasTokenNames.put(idNode.getText(), a);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
GrammarAST value = (GrammarAST)prev.getChild(1);
|
||||||
|
String valueText = null;
|
||||||
|
if ( value!=null ) valueText = value.getText();
|
||||||
|
errMgr.grammarError(ErrorType.TOKEN_ALIAS_REASSIGNMENT,
|
||||||
|
g.fileName, idNode.token, idNode.getText(), valueText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkForTokenConflicts(List<GrammarAST> tokenIDRefs) {
|
||||||
|
for (GrammarAST a : tokenIDRefs) {
|
||||||
|
Token t = a.token;
|
||||||
|
String ID = t.getText();
|
||||||
|
tokenIDs.add(ID);
|
||||||
|
if ( globalScopeNames.contains(t.getText()) ) {
|
||||||
|
errMgr.grammarError(ErrorType.SYMBOL_CONFLICTS_WITH_GLOBAL_SCOPE,
|
||||||
|
g.fileName, t, ID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkActionRedefinitions(List<GrammarAST> actions) {
|
||||||
|
if ( actions==null ) return;
|
||||||
|
String scope = g.getDefaultActionScope();
|
||||||
|
String name = null;
|
||||||
|
GrammarAST nameNode = null;
|
||||||
|
for (GrammarAST ampersandAST : actions) {
|
||||||
|
nameNode = (GrammarAST)ampersandAST.getChild(0);
|
||||||
|
if ( ampersandAST.getChildCount()==2 ) {
|
||||||
|
name = nameNode.getText();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
scope = nameNode.getText();
|
||||||
|
name = ampersandAST.getChild(1).getText();
|
||||||
|
}
|
||||||
|
Set<String> scopeActions = actionScopeToActionNames.get(scope);
|
||||||
|
if ( scopeActions==null ) { // init scope
|
||||||
|
scopeActions = new HashSet<String>();
|
||||||
|
actionScopeToActionNames.put(scope, scopeActions);
|
||||||
|
}
|
||||||
|
if ( !scopeActions.contains(name) ) {
|
||||||
|
scopeActions.add(name);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
errMgr.grammarError(ErrorType.ACTION_REDEFINITION,
|
||||||
|
g.fileName, nameNode.token, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Make sure a label doesn't conflict with another symbol.
|
||||||
|
* Labels must not conflict with: rules, tokens, scope names,
|
||||||
|
* return values, parameters, and rule-scope dynamic attributes
|
||||||
|
* defined in surrounding rule. Also they must have same type
|
||||||
|
* for repeated defs.
|
||||||
|
*/
|
||||||
|
public void checkForLabelConflicts(List<Rule> rules) {
|
||||||
|
for (Rule r : rules) {
|
||||||
|
checkForRuleArgumentAndReturnValueConflicts(r);
|
||||||
|
checkForRuleScopeAttributeConflict(r);
|
||||||
|
Map<String, LabelElementPair> labelNameSpace =
|
||||||
|
new HashMap<String, LabelElementPair>();
|
||||||
|
for (int i=1; i<=r.numberOfAlts; i++) {
|
||||||
|
Alternative a = r.alt[i];
|
||||||
|
for (List<LabelElementPair> pairs : a.labelDefs.values() ) {
|
||||||
|
for (LabelElementPair p : pairs) {
|
||||||
|
checkForLabelConflict(r, p.label);
|
||||||
|
String name = p.label.getText();
|
||||||
|
LabelElementPair prev = labelNameSpace.get(name);
|
||||||
|
if ( prev==null ) labelNameSpace.put(name, p);
|
||||||
|
else checkForTypeMismatch(prev, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkForTypeMismatch(LabelElementPair prevLabelPair,
|
||||||
|
LabelElementPair labelPair)
|
||||||
|
{
|
||||||
|
// label already defined; if same type, no problem
|
||||||
|
if ( prevLabelPair.type != labelPair.type ) {
|
||||||
|
String typeMismatchExpr = labelPair.type+"!="+prevLabelPair.type;
|
||||||
|
errMgr.grammarError(
|
||||||
|
ErrorType.LABEL_TYPE_CONFLICT,
|
||||||
|
g.fileName,
|
||||||
|
labelPair.label.token,
|
||||||
|
labelPair.label.getText(),
|
||||||
|
typeMismatchExpr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkForLabelConflict(Rule r, GrammarAST labelID) {
|
||||||
|
ErrorType etype = ErrorType.INVALID;
|
||||||
|
Object arg2 = null;
|
||||||
|
String name = labelID.getText();
|
||||||
|
if ( globalScopeNames.contains(name) ) {
|
||||||
|
etype = ErrorType.SYMBOL_CONFLICTS_WITH_GLOBAL_SCOPE;
|
||||||
|
}
|
||||||
|
else if ( nameToRuleMap.containsKey(name) ) {
|
||||||
|
etype = ErrorType.LABEL_CONFLICTS_WITH_RULE;
|
||||||
|
}
|
||||||
|
else if ( tokenIDs.contains(name) ) {
|
||||||
|
etype = ErrorType.LABEL_CONFLICTS_WITH_TOKEN;
|
||||||
|
}
|
||||||
|
else if ( r.scope !=null && r.scope.get(name)!=null ) {
|
||||||
|
etype = ErrorType.LABEL_CONFLICTS_WITH_RULE_SCOPE_ATTRIBUTE;
|
||||||
|
arg2 = r.name;
|
||||||
|
}
|
||||||
|
else if ( (r.retvals!=null&&r.retvals.get(name)!=null) ||
|
||||||
|
(r.args!=null&&r.args.get(name)!=null) )
|
||||||
|
{
|
||||||
|
etype = ErrorType.LABEL_CONFLICTS_WITH_RULE_ARG_RETVAL;
|
||||||
|
arg2 = r.name;
|
||||||
|
}
|
||||||
|
if ( etype!=ErrorType.INVALID ) {
|
||||||
|
errMgr.grammarError(etype,g.fileName,labelID.token,name,arg2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkForRuleArgumentAndReturnValueConflicts(Rule r) {
|
||||||
|
if ( r.retvals!=null ) {
|
||||||
|
Set conflictingKeys = r.retvals.intersection(r.args);
|
||||||
|
if (conflictingKeys!=null) {
|
||||||
|
for (Iterator it = conflictingKeys.iterator(); it.hasNext();) {
|
||||||
|
String key = (String) it.next();
|
||||||
|
errMgr.grammarError(
|
||||||
|
ErrorType.ARG_RETVAL_CONFLICT,
|
||||||
|
g.fileName,
|
||||||
|
((GrammarAST)r.ast.getChild(0)).token,
|
||||||
|
key,
|
||||||
|
r.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check for collision of a rule-scope dynamic attribute with:
|
||||||
|
* arg, return value, rule name itself. Labels are checked elsewhere.
|
||||||
|
*/
|
||||||
|
public void checkForRuleScopeAttributeConflict(Rule r) {
|
||||||
|
if ( r.scope ==null ) return;
|
||||||
|
for (Attribute a : r.scope.attributes.values()) {
|
||||||
|
ErrorType msgID = ErrorType.INVALID;
|
||||||
|
Object arg2 = null;
|
||||||
|
String attrName = a.name;
|
||||||
|
if ( r.name.equals(attrName) ) {
|
||||||
|
msgID = ErrorType.ATTRIBUTE_CONFLICTS_WITH_RULE;
|
||||||
|
arg2 = r.name;
|
||||||
|
}
|
||||||
|
else if ( (r.retvals!=null&&r.retvals.get(attrName)!=null) ||
|
||||||
|
(r.args!=null&&r.args.get(attrName)!=null) )
|
||||||
|
{
|
||||||
|
msgID = ErrorType.ATTRIBUTE_CONFLICTS_WITH_RULE_ARG_RETVAL;
|
||||||
|
arg2 = r.name;
|
||||||
|
}
|
||||||
|
if ( msgID!=ErrorType.INVALID ) {
|
||||||
|
errMgr.grammarError(msgID,g.fileName,
|
||||||
|
r.scope.ast.token,
|
||||||
|
attrName,arg2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAN ONLY CALL THE TWO NEXT METHODS AFTER GRAMMAR HAS RULE DEFS (see semanticpipeline)
|
||||||
|
|
||||||
|
public void checkRuleArgs(Grammar g, List<GrammarAST> rulerefs) {
|
||||||
|
if ( rulerefs==null ) return;
|
||||||
|
for (GrammarAST ref : rulerefs) {
|
||||||
|
String ruleName = ref.getText();
|
||||||
|
Rule r = g.getRule(ruleName);
|
||||||
|
if ( r==null && !ref.hasAncestor(ANTLRParser.DOT)) {
|
||||||
|
// only give error for unqualified rule refs now
|
||||||
|
errMgr.grammarError(ErrorType.UNDEFINED_RULE_REF,
|
||||||
|
g.fileName, ref.token, ruleName);
|
||||||
|
}
|
||||||
|
GrammarAST arg = (GrammarAST)ref.getChild(0);
|
||||||
|
if ( arg!=null && r.args==null ) {
|
||||||
|
errMgr.grammarError(ErrorType.RULE_HAS_NO_ARGS,
|
||||||
|
g.fileName, ref.token, ruleName);
|
||||||
|
|
||||||
|
}
|
||||||
|
else if ( arg==null && (r!=null&&r.args!=null) ) {
|
||||||
|
errMgr.grammarError(ErrorType.MISSING_RULE_ARGS,
|
||||||
|
g.fileName, ref.token, ruleName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkForQualifiedRuleIssues(Grammar g, List<GrammarAST> qualifiedRuleRefs) {
|
||||||
|
for (GrammarAST dot : qualifiedRuleRefs) {
|
||||||
|
GrammarAST grammar = (GrammarAST)dot.getChild(0);
|
||||||
|
GrammarAST rule = (GrammarAST)dot.getChild(1);
|
||||||
|
System.out.println(grammar.getText()+"."+rule.getText());
|
||||||
|
Grammar delegate = g.getImportedGrammar(grammar.getText());
|
||||||
|
if ( delegate==null ) {
|
||||||
|
errMgr.grammarError(ErrorType.NO_SUCH_GRAMMAR_SCOPE,
|
||||||
|
g.fileName, grammar.token, grammar.getText(),
|
||||||
|
rule.getText());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ( g.getRule(grammar.getText(), rule.getText())==null ) {
|
||||||
|
errMgr.grammarError(ErrorType.NO_SUCH_RULE_IN_SCOPE,
|
||||||
|
g.fileName, rule.token, grammar.getText(),
|
||||||
|
rule.getText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
package org.antlr.v4.semantics;
|
||||||
|
|
||||||
|
import org.antlr.v4.parse.ANTLRParser;
|
||||||
|
import org.antlr.v4.runtime.Token;
|
||||||
|
import org.antlr.v4.tool.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/** Look for errors and deadcode stuff */
|
||||||
|
public class UseDefAnalyzer {
|
||||||
|
public static void checkRewriteElementsPresentOnLeftSide(Grammar g) {
|
||||||
|
for (Rule r : g.rules.values()) {
|
||||||
|
for (int a=1; a<=r.numberOfAlts; a++) {
|
||||||
|
Alternative alt = r.alt[a];
|
||||||
|
for (GrammarAST e : alt.rewriteElements) {
|
||||||
|
if ( !(alt.ruleRefs.containsKey(e.getText()) ||
|
||||||
|
g.getTokenType(e.getText())!= Token.INVALID_TYPE ||
|
||||||
|
alt.labelDefs.containsKey(e.getText()) ||
|
||||||
|
e.getText().equals(r.name)) ) // $r ok in rule r
|
||||||
|
{
|
||||||
|
g.tool.errMgr.grammarError(ErrorType.REWRITE_ELEMENT_NOT_PRESENT_ON_LHS,
|
||||||
|
g.fileName, e.token, e.getText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// side-effect: updates Alternative with refs in actions
|
||||||
|
public static void trackTokenRuleRefsInActions(Grammar g) {
|
||||||
|
for (Rule r : g.rules.values()) {
|
||||||
|
for (int i=1; i<=r.numberOfAlts; i++) {
|
||||||
|
Alternative alt = r.alt[i];
|
||||||
|
for (ActionAST a : alt.actions) {
|
||||||
|
ActionSniffer sniffer = new ActionSniffer(g, r, alt, a, a.token);
|
||||||
|
sniffer.examineAction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Find all rules reachable from r directly or indirectly for all r in g */
|
||||||
|
public static Map<Rule, Set<Rule>> getRuleDependencies(Grammar g) {
|
||||||
|
return getRuleDependencies(g, g.rules.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<Rule, Set<Rule>> getRuleDependencies(LexerGrammar g, String modeName) {
|
||||||
|
return getRuleDependencies(g, g.modes.get(modeName));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<Rule, Set<Rule>> getRuleDependencies(Grammar g, Collection<Rule> rules) {
|
||||||
|
Map<Rule, Set<Rule>> dependencies = new HashMap<Rule, Set<Rule>>();
|
||||||
|
|
||||||
|
for (Rule r : rules) {
|
||||||
|
List<GrammarAST> tokenRefs = r.ast.getNodesWithType(ANTLRParser.TOKEN_REF);
|
||||||
|
for (GrammarAST tref : tokenRefs) {
|
||||||
|
Set<Rule> calls = dependencies.get(r);
|
||||||
|
if ( calls==null ) {
|
||||||
|
calls = new HashSet<Rule>();
|
||||||
|
dependencies.put(r, calls);
|
||||||
|
}
|
||||||
|
calls.add(g.getRule(tref.getText()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,9 +1,8 @@
|
||||||
package org.antlr.v4.tool;
|
package org.antlr.v4.tool;
|
||||||
|
|
||||||
import org.antlr.misc.MultiMap;
|
|
||||||
import org.antlr.runtime.RecognitionException;
|
import org.antlr.runtime.RecognitionException;
|
||||||
import org.antlr.tool.Rule;
|
|
||||||
import org.antlr.v4.Tool;
|
import org.antlr.v4.Tool;
|
||||||
|
import org.stringtemplate.v4.misc.MultiMap;
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
public class LexerGrammar extends Grammar {
|
public class LexerGrammar extends Grammar {
|
||||||
|
|
Loading…
Reference in New Issue