Merge pull request #1576 from sharwell/optional-getters

Implement support for optional getters
This commit is contained in:
Terence Parr 2017-01-04 09:50:36 -08:00 committed by GitHub
commit a17b299cd3
4 changed files with 89 additions and 11 deletions

View File

@ -21,12 +21,30 @@ import java.util.Deque;
import java.util.Map;
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;
private final Deque<FrequencySet<String>> minFrequencies;
public ElementFrequenciesVisitor(TreeNodeStream input) {
super(input);
frequencies = new ArrayDeque<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 */
@ -61,6 +79,31 @@ public class ElementFrequenciesVisitor extends GrammarTreeVisitor {
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
* values clipped to a specified maximum value. If an element is
@ -97,11 +140,13 @@ public class ElementFrequenciesVisitor extends GrammarTreeVisitor {
@Override
public void tokenRef(TerminalAST ref) {
frequencies.peek().add(ref.getText());
minFrequencies.peek().add(ref.getText());
}
@Override
public void ruleRef(GrammarAST ref, ActionAST arg) {
frequencies.peek().add(ref.getText());
minFrequencies.peek().add(ref.getText());
}
/*
@ -111,21 +156,25 @@ public class ElementFrequenciesVisitor extends GrammarTreeVisitor {
@Override
protected void enterAlternative(AltAST tree) {
frequencies.push(new FrequencySet<String>());
minFrequencies.push(new FrequencySet<String>());
}
@Override
protected void exitAlternative(AltAST tree) {
frequencies.push(combineMax(frequencies.pop(), frequencies.pop()));
minFrequencies.push(combineMin(minFrequencies.pop(), minFrequencies.pop()));
}
@Override
protected void enterElement(GrammarAST tree) {
frequencies.push(new FrequencySet<String>());
minFrequencies.push(new FrequencySet<String>());
}
@Override
protected void exitElement(GrammarAST tree) {
frequencies.push(combineAndClip(frequencies.pop(), frequencies.pop(), 2));
minFrequencies.push(combineAndClip(minFrequencies.pop(), minFrequencies.pop(), 2));
}
@Override
@ -135,6 +184,12 @@ public class ElementFrequenciesVisitor extends GrammarTreeVisitor {
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
protected void enterLexerAlternative(GrammarAST tree) {
frequencies.push(new FrequencySet<String>());
minFrequencies.push(new FrequencySet<String>());
}
@Override
protected void exitLexerAlternative(GrammarAST tree) {
frequencies.push(combineMax(frequencies.pop(), frequencies.pop()));
minFrequencies.push(combineMin(minFrequencies.pop(), minFrequencies.pop()));
}
@Override
protected void enterLexerElement(GrammarAST tree) {
frequencies.push(new FrequencySet<String>());
minFrequencies.push(new FrequencySet<String>());
}
@Override
protected void exitLexerElement(GrammarAST tree) {
frequencies.push(combineAndClip(frequencies.pop(), frequencies.pop(), 2));
minFrequencies.push(combineAndClip(minFrequencies.pop(), minFrequencies.pop(), 2));
}
@Override
@ -168,5 +227,11 @@ public class ElementFrequenciesVisitor extends GrammarTreeVisitor {
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();
}
}
}

View File

@ -171,14 +171,20 @@ public class RuleFunction extends OutputModelObject {
*/
public Set<Decl> getDeclsForAllElements(List<AltAST> altASTs) {
Set<String> needsList = new HashSet<String>();
Set<String> optional = new HashSet<String>();
List<GrammarAST> allRefs = new ArrayList<GrammarAST>();
for (AltAST ast : altASTs) {
IntervalSet reftypes = new IntervalSet(RULE_REF, TOKEN_REF);
List<GrammarAST> refs = ast.getNodesWithType(reftypes);
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) {
String refLabelName = t.getText();
if (minFreq.count(refLabelName) == 0) {
optional.add(refLabelName);
}
if ( altFreq.count(refLabelName)>1 ) {
needsList.add(refLabelName);
}
@ -189,31 +195,32 @@ public class RuleFunction extends OutputModelObject {
String refLabelName = t.getText();
List<Decl> d = getDeclForAltElement(t,
refLabelName,
needsList.contains(refLabelName));
needsList.contains(refLabelName),
optional.contains(refLabelName));
decls.addAll(d);
}
return decls;
}
/** 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 {
ElementFrequenciesVisitor visitor = new ElementFrequenciesVisitor(new CommonTreeNodeStream(new GrammarASTAdaptor(), ast));
visitor.outerAlternative();
if (visitor.frequencies.size() != 1) {
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) {
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>();
if ( t.getType()==RULE_REF ) {
Rule rref = factory.getGrammar().getRule(t.getText());
@ -225,7 +232,7 @@ public class RuleFunction extends OutputModelObject {
decls.add( new ContextRuleListIndexedGetterDecl(factory, refLabelName, ctxName) );
}
else {
decls.add( new ContextRuleGetterDecl(factory, refLabelName, ctxName) );
decls.add( new ContextRuleGetterDecl(factory, refLabelName, ctxName, optional) );
}
}
else {
@ -235,7 +242,7 @@ public class RuleFunction extends OutputModelObject {
decls.add( new ContextTokenListIndexedGetterDecl(factory, refLabelName) );
}
else {
decls.add( new ContextTokenGetterDecl(factory, refLabelName) );
decls.add( new ContextTokenGetterDecl(factory, refLabelName, optional) );
}
}
return decls;

View File

@ -11,8 +11,11 @@ import org.antlr.v4.codegen.OutputModelFactory;
/** {@code public XContext X() { }} */
public class ContextRuleGetterDecl extends ContextGetterDecl {
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);
this.ctxName = ctxName;
this.optional = optional;
}
}

View File

@ -10,7 +10,10 @@ import org.antlr.v4.codegen.OutputModelFactory;
/** {@code public Token X() { }} */
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);
this.optional = optional;
}
}