diff --git a/tool/src/org/antlr/v4/codegen/model/RuleFunction.java b/tool/src/org/antlr/v4/codegen/model/RuleFunction.java index 6cddce32a..39aaa1150 100644 --- a/tool/src/org/antlr/v4/codegen/model/RuleFunction.java +++ b/tool/src/org/antlr/v4/codegen/model/RuleFunction.java @@ -29,6 +29,9 @@ package org.antlr.v4.codegen.model; +import org.antlr.runtime.RecognitionException; +import org.antlr.runtime.tree.CommonTreeNodeStream; +import org.antlr.runtime.tree.TreeNodeStream; import org.antlr.v4.codegen.OutputModelFactory; import org.antlr.v4.codegen.model.decl.AltLabelStructDecl; import org.antlr.v4.codegen.model.decl.ContextRuleGetterDecl; @@ -40,27 +43,32 @@ import org.antlr.v4.codegen.model.decl.ContextTokenListIndexedGetterDecl; import org.antlr.v4.codegen.model.decl.Decl; import org.antlr.v4.codegen.model.decl.StructDecl; import org.antlr.v4.misc.FrequencySet; +import org.antlr.v4.misc.MutableInt; import org.antlr.v4.misc.Utils; +import org.antlr.v4.parse.GrammarASTAdaptor; +import org.antlr.v4.parse.GrammarTreeVisitor; import org.antlr.v4.runtime.atn.ATNState; import org.antlr.v4.runtime.misc.IntervalSet; import org.antlr.v4.runtime.misc.OrderedHashSet; import org.antlr.v4.runtime.misc.Triple; import org.antlr.v4.tool.Attribute; +import org.antlr.v4.tool.ErrorType; import org.antlr.v4.tool.Rule; import org.antlr.v4.tool.ast.ActionAST; import org.antlr.v4.tool.ast.AltAST; import org.antlr.v4.tool.ast.GrammarAST; +import org.antlr.v4.tool.ast.TerminalAST; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; +import java.util.Deque; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import static org.antlr.v4.parse.ANTLRParser.CLOSURE; -import static org.antlr.v4.parse.ANTLRParser.POSITIVE_CLOSURE; import static org.antlr.v4.parse.ANTLRParser.RULE_REF; import static org.antlr.v4.parse.ANTLRParser.TOKEN_REF; @@ -181,19 +189,9 @@ public class RuleFunction extends OutputModelObject { FrequencySet altFreq = getElementFrequenciesForAlt(ast); for (GrammarAST t : refs) { String refLabelName = t.getText(); - if (needsList.contains(refLabelName)) { - continue; - } - - if ( altFreq.count(t.getText())>1 ) { + if ( altFreq.count(refLabelName)>1 ) { needsList.add(refLabelName); } - else { - boolean inLoop = t.hasAncestor(CLOSURE) || t.hasAncestor(POSITIVE_CLOSURE); - if (inLoop) { - needsList.add(refLabelName); - } - } } } Set decls = new HashSet(); @@ -209,14 +207,19 @@ public class RuleFunction extends OutputModelObject { /** Given list of X and r refs in alt, compute how many of each there are */ protected FrequencySet getElementFrequenciesForAlt(AltAST ast) { - IntervalSet reftypes = new IntervalSet(RULE_REF, TOKEN_REF); - List refs = ast.getNodesWithType(reftypes); - FrequencySet altFreq = new FrequencySet(); - for (GrammarAST t : refs) { - String refLabelName = t.getText(); - altFreq.add(refLabelName); + 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(); + } + + return visitor.frequencies.peek(); + } catch (RecognitionException ex) { + factory.getGrammar().tool.errMgr.toolError(ErrorType.INTERNAL_ERROR, ex); + return new FrequencySet(); } - return altFreq; } /** Get list of decls for token/rule refs. @@ -231,14 +234,10 @@ public class RuleFunction extends OutputModelObject { TOKEN_REF); List refs = altAST.getNodesWithType(reftypes); Set decls = new HashSet(); - FrequencySet freq = new FrequencySet(); - for (GrammarAST t : refs) freq.add(t.getText()); + FrequencySet freq = getElementFrequenciesForAlt(altAST); for (GrammarAST t : refs) { String refLabelName = t.getText(); - boolean inLoop = t.hasAncestor(CLOSURE) || t.hasAncestor(POSITIVE_CLOSURE); - boolean multipleRefs = freq.count(refLabelName)>1; - boolean needList = inLoop || multipleRefs; -// System.out.println(altAST.toStringTree()+" "+t+" inLoop? "+inLoop); + boolean needList = freq.count(refLabelName)>1; List d = getDeclForAltElement(t, refLabelName, needList); decls.addAll(d); } @@ -293,4 +292,129 @@ public class RuleFunction extends OutputModelObject { } ruleCtx.addDecl(d); // stick in overall rule's ctx } + + protected static class ElementFrequenciesVisitor extends GrammarTreeVisitor { + final Deque> frequencies; + + public ElementFrequenciesVisitor(TreeNodeStream input) { + super(input); + frequencies = new ArrayDeque>(); + frequencies.push(new FrequencySet()); + } + + /* + * Common + */ + + protected static FrequencySet combineMax(FrequencySet a, FrequencySet b) { + FrequencySet result = combineAndClip(a, b, 1); + for (Map.Entry entry : a.entrySet()) { + result.get(entry.getKey()).v = entry.getValue().v; + } + + for (Map.Entry entry : a.entrySet()) { + MutableInt slot = result.get(entry.getKey()); + slot.v = Math.max(slot.v, entry.getValue().v); + } + + return result; + } + + protected static FrequencySet combineAndClip(FrequencySet a, FrequencySet b, int clip) { + FrequencySet result = new FrequencySet(); + for (Map.Entry entry : a.entrySet()) { + for (int i = 0; i < entry.getValue().v; i++) { + result.add(entry.getKey()); + } + } + + for (Map.Entry entry : b.entrySet()) { + for (int i = 0; i < entry.getValue().v; i++) { + result.add(entry.getKey()); + } + } + + for (Map.Entry entry : result.entrySet()) { + entry.getValue().v = Math.min(entry.getValue().v, clip); + } + + return result; + } + + @Override + public void tokenRef(TerminalAST ref) { + frequencies.peek().add(ref.getText()); + } + + @Override + public void ruleRef(GrammarAST ref, ActionAST arg) { + frequencies.peek().add(ref.getText()); + } + + /* + * Parser rules + */ + + @Override + protected void enterAlternative(AltAST tree) { + frequencies.push(new FrequencySet()); + } + + @Override + protected void exitAlternative(AltAST tree) { + frequencies.push(combineMax(frequencies.pop(), frequencies.pop())); + } + + @Override + protected void enterElement(GrammarAST tree) { + frequencies.push(new FrequencySet()); + } + + @Override + protected void exitElement(GrammarAST tree) { + frequencies.push(combineAndClip(frequencies.pop(), frequencies.pop(), 2)); + } + + @Override + protected void exitSubrule(GrammarAST tree) { + if (tree.getType() == CLOSURE || tree.getType() == POSITIVE_CLOSURE) { + for (Map.Entry entry : frequencies.peek().entrySet()) { + entry.getValue().v = 2; + } + } + } + + /* + * Lexer rules + */ + + @Override + protected void enterLexerAlternative(GrammarAST tree) { + frequencies.push(new FrequencySet()); + } + + @Override + protected void exitLexerAlternative(GrammarAST tree) { + frequencies.push(combineMax(frequencies.pop(), frequencies.pop())); + } + + @Override + protected void enterLexerElement(GrammarAST tree) { + frequencies.push(new FrequencySet()); + } + + @Override + protected void exitLexerElement(GrammarAST tree) { + frequencies.push(combineAndClip(frequencies.pop(), frequencies.pop(), 2)); + } + + @Override + protected void exitLexerSubrule(GrammarAST tree) { + if (tree.getType() == CLOSURE || tree.getType() == POSITIVE_CLOSURE) { + for (Map.Entry entry : frequencies.peek().entrySet()) { + entry.getValue().v = 2; + } + } + } + } }