Merge pull request #1576 from sharwell/optional-getters
Implement support for optional getters
This commit is contained in:
commit
a17b299cd3
|
@ -21,12 +21,30 @@ import java.util.Deque;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class ElementFrequenciesVisitor extends GrammarTreeVisitor {
|
public class ElementFrequenciesVisitor extends GrammarTreeVisitor {
|
||||||
|
/**
|
||||||
|
* This special value means "no set", and is used by {@link #minFrequencies}
|
||||||
|
* to ensure that {@link #combineMin} doesn't merge an empty set (all zeros)
|
||||||
|
* with the results of the first alternative.
|
||||||
|
*/
|
||||||
|
private static final FrequencySet<String> SENTINEL = new FrequencySet<String>();
|
||||||
|
|
||||||
final Deque<FrequencySet<String>> frequencies;
|
final Deque<FrequencySet<String>> frequencies;
|
||||||
|
private final Deque<FrequencySet<String>> minFrequencies;
|
||||||
|
|
||||||
public ElementFrequenciesVisitor(TreeNodeStream input) {
|
public ElementFrequenciesVisitor(TreeNodeStream input) {
|
||||||
super(input);
|
super(input);
|
||||||
frequencies = new ArrayDeque<FrequencySet<String>>();
|
frequencies = new ArrayDeque<FrequencySet<String>>();
|
||||||
frequencies.push(new FrequencySet<String>());
|
frequencies.push(new FrequencySet<String>());
|
||||||
|
minFrequencies = new ArrayDeque<FrequencySet<String>>();
|
||||||
|
minFrequencies.push(SENTINEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
FrequencySet<String> getMinFrequencies() {
|
||||||
|
assert minFrequencies.size() == 1;
|
||||||
|
assert minFrequencies.peek() != SENTINEL;
|
||||||
|
assert SENTINEL.isEmpty();
|
||||||
|
|
||||||
|
return minFrequencies.peek();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** During code gen, we can assume tree is in good shape */
|
/** During code gen, we can assume tree is in good shape */
|
||||||
|
@ -61,6 +79,31 @@ public class ElementFrequenciesVisitor extends GrammarTreeVisitor {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a frequency set as the union of two input sets. If an
|
||||||
|
* element is contained in both sets, the value for the output will be
|
||||||
|
* the minimum of the two input values.
|
||||||
|
*
|
||||||
|
* @param a The first set.
|
||||||
|
* @param b The second set. If this set is {@link #SENTINEL}, it is treated
|
||||||
|
* as though no second set were provided.
|
||||||
|
* @return The union of the two sets, with the minimum value chosen
|
||||||
|
* whenever both sets contain the same key.
|
||||||
|
*/
|
||||||
|
protected static FrequencySet<String> combineMin(FrequencySet<String> a, FrequencySet<String> b) {
|
||||||
|
if (b == SENTINEL) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert a != SENTINEL;
|
||||||
|
FrequencySet<String> result = combineAndClip(a, b, 1);
|
||||||
|
for (Map.Entry<String, MutableInt> entry : result.entrySet()) {
|
||||||
|
entry.getValue().v = Math.min(a.count(entry.getKey()), b.count(entry.getKey()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a frequency set as the union of two input sets, with the
|
* Generate a frequency set as the union of two input sets, with the
|
||||||
* values clipped to a specified maximum value. If an element is
|
* values clipped to a specified maximum value. If an element is
|
||||||
|
@ -97,11 +140,13 @@ public class ElementFrequenciesVisitor extends GrammarTreeVisitor {
|
||||||
@Override
|
@Override
|
||||||
public void tokenRef(TerminalAST ref) {
|
public void tokenRef(TerminalAST ref) {
|
||||||
frequencies.peek().add(ref.getText());
|
frequencies.peek().add(ref.getText());
|
||||||
|
minFrequencies.peek().add(ref.getText());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void ruleRef(GrammarAST ref, ActionAST arg) {
|
public void ruleRef(GrammarAST ref, ActionAST arg) {
|
||||||
frequencies.peek().add(ref.getText());
|
frequencies.peek().add(ref.getText());
|
||||||
|
minFrequencies.peek().add(ref.getText());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -111,21 +156,25 @@ public class ElementFrequenciesVisitor extends GrammarTreeVisitor {
|
||||||
@Override
|
@Override
|
||||||
protected void enterAlternative(AltAST tree) {
|
protected void enterAlternative(AltAST tree) {
|
||||||
frequencies.push(new FrequencySet<String>());
|
frequencies.push(new FrequencySet<String>());
|
||||||
|
minFrequencies.push(new FrequencySet<String>());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void exitAlternative(AltAST tree) {
|
protected void exitAlternative(AltAST tree) {
|
||||||
frequencies.push(combineMax(frequencies.pop(), frequencies.pop()));
|
frequencies.push(combineMax(frequencies.pop(), frequencies.pop()));
|
||||||
|
minFrequencies.push(combineMin(minFrequencies.pop(), minFrequencies.pop()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void enterElement(GrammarAST tree) {
|
protected void enterElement(GrammarAST tree) {
|
||||||
frequencies.push(new FrequencySet<String>());
|
frequencies.push(new FrequencySet<String>());
|
||||||
|
minFrequencies.push(new FrequencySet<String>());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void exitElement(GrammarAST tree) {
|
protected void exitElement(GrammarAST tree) {
|
||||||
frequencies.push(combineAndClip(frequencies.pop(), frequencies.pop(), 2));
|
frequencies.push(combineAndClip(frequencies.pop(), frequencies.pop(), 2));
|
||||||
|
minFrequencies.push(combineAndClip(minFrequencies.pop(), minFrequencies.pop(), 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -135,6 +184,12 @@ public class ElementFrequenciesVisitor extends GrammarTreeVisitor {
|
||||||
entry.getValue().v = 2;
|
entry.getValue().v = 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tree.getType() == CLOSURE) {
|
||||||
|
// Everything inside a closure is optional, so the minimum
|
||||||
|
// number of occurrences for all elements is 0.
|
||||||
|
minFrequencies.peek().clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -144,21 +199,25 @@ public class ElementFrequenciesVisitor extends GrammarTreeVisitor {
|
||||||
@Override
|
@Override
|
||||||
protected void enterLexerAlternative(GrammarAST tree) {
|
protected void enterLexerAlternative(GrammarAST tree) {
|
||||||
frequencies.push(new FrequencySet<String>());
|
frequencies.push(new FrequencySet<String>());
|
||||||
|
minFrequencies.push(new FrequencySet<String>());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void exitLexerAlternative(GrammarAST tree) {
|
protected void exitLexerAlternative(GrammarAST tree) {
|
||||||
frequencies.push(combineMax(frequencies.pop(), frequencies.pop()));
|
frequencies.push(combineMax(frequencies.pop(), frequencies.pop()));
|
||||||
|
minFrequencies.push(combineMin(minFrequencies.pop(), minFrequencies.pop()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void enterLexerElement(GrammarAST tree) {
|
protected void enterLexerElement(GrammarAST tree) {
|
||||||
frequencies.push(new FrequencySet<String>());
|
frequencies.push(new FrequencySet<String>());
|
||||||
|
minFrequencies.push(new FrequencySet<String>());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void exitLexerElement(GrammarAST tree) {
|
protected void exitLexerElement(GrammarAST tree) {
|
||||||
frequencies.push(combineAndClip(frequencies.pop(), frequencies.pop(), 2));
|
frequencies.push(combineAndClip(frequencies.pop(), frequencies.pop(), 2));
|
||||||
|
minFrequencies.push(combineAndClip(minFrequencies.pop(), minFrequencies.pop(), 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -168,5 +227,11 @@ public class ElementFrequenciesVisitor extends GrammarTreeVisitor {
|
||||||
entry.getValue().v = 2;
|
entry.getValue().v = 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tree.getType() == CLOSURE) {
|
||||||
|
// Everything inside a closure is optional, so the minimum
|
||||||
|
// number of occurrences for all elements is 0.
|
||||||
|
minFrequencies.peek().clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -171,14 +171,20 @@ public class RuleFunction extends OutputModelObject {
|
||||||
*/
|
*/
|
||||||
public Set<Decl> getDeclsForAllElements(List<AltAST> altASTs) {
|
public Set<Decl> getDeclsForAllElements(List<AltAST> altASTs) {
|
||||||
Set<String> needsList = new HashSet<String>();
|
Set<String> needsList = new HashSet<String>();
|
||||||
|
Set<String> optional = new HashSet<String>();
|
||||||
List<GrammarAST> allRefs = new ArrayList<GrammarAST>();
|
List<GrammarAST> allRefs = new ArrayList<GrammarAST>();
|
||||||
for (AltAST ast : altASTs) {
|
for (AltAST ast : altASTs) {
|
||||||
IntervalSet reftypes = new IntervalSet(RULE_REF, TOKEN_REF);
|
IntervalSet reftypes = new IntervalSet(RULE_REF, TOKEN_REF);
|
||||||
List<GrammarAST> refs = ast.getNodesWithType(reftypes);
|
List<GrammarAST> refs = ast.getNodesWithType(reftypes);
|
||||||
allRefs.addAll(refs);
|
allRefs.addAll(refs);
|
||||||
FrequencySet<String> altFreq = getElementFrequenciesForAlt(ast);
|
Pair<FrequencySet<String>, FrequencySet<String>> minAndAltFreq = getElementFrequenciesForAlt(ast);
|
||||||
|
FrequencySet<String> minFreq = minAndAltFreq.a;
|
||||||
|
FrequencySet<String> altFreq = minAndAltFreq.b;
|
||||||
for (GrammarAST t : refs) {
|
for (GrammarAST t : refs) {
|
||||||
String refLabelName = t.getText();
|
String refLabelName = t.getText();
|
||||||
|
if (minFreq.count(refLabelName) == 0) {
|
||||||
|
optional.add(refLabelName);
|
||||||
|
}
|
||||||
if ( altFreq.count(refLabelName)>1 ) {
|
if ( altFreq.count(refLabelName)>1 ) {
|
||||||
needsList.add(refLabelName);
|
needsList.add(refLabelName);
|
||||||
}
|
}
|
||||||
|
@ -189,31 +195,32 @@ public class RuleFunction extends OutputModelObject {
|
||||||
String refLabelName = t.getText();
|
String refLabelName = t.getText();
|
||||||
List<Decl> d = getDeclForAltElement(t,
|
List<Decl> d = getDeclForAltElement(t,
|
||||||
refLabelName,
|
refLabelName,
|
||||||
needsList.contains(refLabelName));
|
needsList.contains(refLabelName),
|
||||||
|
optional.contains(refLabelName));
|
||||||
decls.addAll(d);
|
decls.addAll(d);
|
||||||
}
|
}
|
||||||
return decls;
|
return decls;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Given list of X and r refs in alt, compute how many of each there are */
|
/** Given list of X and r refs in alt, compute how many of each there are */
|
||||||
protected FrequencySet<String> getElementFrequenciesForAlt(AltAST ast) {
|
protected Pair<FrequencySet<String>, FrequencySet<String>> getElementFrequenciesForAlt(AltAST ast) {
|
||||||
try {
|
try {
|
||||||
ElementFrequenciesVisitor visitor = new ElementFrequenciesVisitor(new CommonTreeNodeStream(new GrammarASTAdaptor(), ast));
|
ElementFrequenciesVisitor visitor = new ElementFrequenciesVisitor(new CommonTreeNodeStream(new GrammarASTAdaptor(), ast));
|
||||||
visitor.outerAlternative();
|
visitor.outerAlternative();
|
||||||
if (visitor.frequencies.size() != 1) {
|
if (visitor.frequencies.size() != 1) {
|
||||||
factory.getGrammar().tool.errMgr.toolError(ErrorType.INTERNAL_ERROR);
|
factory.getGrammar().tool.errMgr.toolError(ErrorType.INTERNAL_ERROR);
|
||||||
return new FrequencySet<String>();
|
return new Pair<>(new FrequencySet<String>(), new FrequencySet<String>());
|
||||||
}
|
}
|
||||||
|
|
||||||
return visitor.frequencies.peek();
|
return new Pair<>(visitor.getMinFrequencies(), visitor.frequencies.peek());
|
||||||
}
|
}
|
||||||
catch (RecognitionException ex) {
|
catch (RecognitionException ex) {
|
||||||
factory.getGrammar().tool.errMgr.toolError(ErrorType.INTERNAL_ERROR, ex);
|
factory.getGrammar().tool.errMgr.toolError(ErrorType.INTERNAL_ERROR, ex);
|
||||||
return new FrequencySet<String>();
|
return new Pair<>(new FrequencySet<String>(), new FrequencySet<String>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Decl> getDeclForAltElement(GrammarAST t, String refLabelName, boolean needList) {
|
public List<Decl> getDeclForAltElement(GrammarAST t, String refLabelName, boolean needList, boolean optional) {
|
||||||
List<Decl> decls = new ArrayList<Decl>();
|
List<Decl> decls = new ArrayList<Decl>();
|
||||||
if ( t.getType()==RULE_REF ) {
|
if ( t.getType()==RULE_REF ) {
|
||||||
Rule rref = factory.getGrammar().getRule(t.getText());
|
Rule rref = factory.getGrammar().getRule(t.getText());
|
||||||
|
@ -225,7 +232,7 @@ public class RuleFunction extends OutputModelObject {
|
||||||
decls.add( new ContextRuleListIndexedGetterDecl(factory, refLabelName, ctxName) );
|
decls.add( new ContextRuleListIndexedGetterDecl(factory, refLabelName, ctxName) );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
decls.add( new ContextRuleGetterDecl(factory, refLabelName, ctxName) );
|
decls.add( new ContextRuleGetterDecl(factory, refLabelName, ctxName, optional) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -235,7 +242,7 @@ public class RuleFunction extends OutputModelObject {
|
||||||
decls.add( new ContextTokenListIndexedGetterDecl(factory, refLabelName) );
|
decls.add( new ContextTokenListIndexedGetterDecl(factory, refLabelName) );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
decls.add( new ContextTokenGetterDecl(factory, refLabelName) );
|
decls.add( new ContextTokenGetterDecl(factory, refLabelName, optional) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return decls;
|
return decls;
|
||||||
|
|
|
@ -11,8 +11,11 @@ import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
/** {@code public XContext X() { }} */
|
/** {@code public XContext X() { }} */
|
||||||
public class ContextRuleGetterDecl extends ContextGetterDecl {
|
public class ContextRuleGetterDecl extends ContextGetterDecl {
|
||||||
public String ctxName;
|
public String ctxName;
|
||||||
public ContextRuleGetterDecl(OutputModelFactory factory, String name, String ctxName) {
|
public boolean optional;
|
||||||
|
|
||||||
|
public ContextRuleGetterDecl(OutputModelFactory factory, String name, String ctxName, boolean optional) {
|
||||||
super(factory, name);
|
super(factory, name);
|
||||||
this.ctxName = ctxName;
|
this.ctxName = ctxName;
|
||||||
|
this.optional = optional;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,10 @@ import org.antlr.v4.codegen.OutputModelFactory;
|
||||||
|
|
||||||
/** {@code public Token X() { }} */
|
/** {@code public Token X() { }} */
|
||||||
public class ContextTokenGetterDecl extends ContextGetterDecl {
|
public class ContextTokenGetterDecl extends ContextGetterDecl {
|
||||||
public ContextTokenGetterDecl(OutputModelFactory factory, String name) {
|
public boolean optional;
|
||||||
|
|
||||||
|
public ContextTokenGetterDecl(OutputModelFactory factory, String name, boolean optional) {
|
||||||
super(factory, name);
|
super(factory, name);
|
||||||
|
this.optional = optional;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue