/* * [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.analysis; import org.antlr.codegen.CodeGenerator; import org.antlr.grammar.v3.ANTLRParser; import org.antlr.tool.Grammar; import org.antlr.tool.GrammarAST; import org.stringtemplate.v4.ST; import org.stringtemplate.v4.STGroup; import java.util.*; /** A binary tree structure used to record the semantic context in which * an NFA configuration is valid. It's either a single predicate or * a tree representing an operation tree such as: p1&&p2 or p1||p2. * * For NFA o-p1->o-p2->o, create tree AND(p1,p2). * For NFA (1)-p1->(2) * | ^ * | | * (3)-p2---- * we will have to combine p1 and p2 into DFA state as we will be * adding NFA configurations for state 2 with two predicates p1,p2. * So, set context for combined NFA config for state 2: OR(p1,p2). * * I have scoped the AND, NOT, OR, and Predicate subclasses of * SemanticContext within the scope of this outer class. * * July 7, 2006: TJP altered OR to be set of operands. the Binary tree * made it really hard to reduce complicated || sequences to their minimum. * Got huge repeated || conditions. */ public abstract class SemanticContext { /** Create a default value for the semantic context shared among all * NFAConfigurations that do not have an actual semantic context. * This prevents lots of if!=null type checks all over; it represents * just an empty set of predicates. */ public static final SemanticContext EMPTY_SEMANTIC_CONTEXT = new Predicate(Predicate.INVALID_PRED_VALUE); /** Given a semantic context expression tree, return a tree with all * nongated predicates set to true and then reduced. So p&&(q||r) would * return p&&r if q is nongated but p and r are gated. */ public abstract SemanticContext getGatedPredicateContext(); /** Generate an expression that will evaluate the semantic context, * given a set of output templates. */ public abstract ST genExpr(CodeGenerator generator, STGroup templates, DFA dfa); public abstract boolean hasUserSemanticPredicate(); // user-specified sempred {}? or {}?=> public abstract boolean isSyntacticPredicate(); /** Notify the indicated grammar of any syn preds used within this context */ public void trackUseOfSyntacticPredicates(Grammar g) { } public static class Predicate extends SemanticContext { /** The AST node in tree created from the grammar holding the predicate */ public GrammarAST predicateAST; /** Is this a {...}?=> gating predicate or a normal disambiguating {..}? * If any predicate in expression is gated, then expression is considered * gated. * * The simple Predicate object's predicate AST's type is used to set * gated to true if type==GATED_SEMPRED. */ protected boolean gated = false; /** syntactic predicates are converted to semantic predicates * but synpreds are generated slightly differently. */ protected boolean synpred = false; public static final int INVALID_PRED_VALUE = -2; public static final int FALSE_PRED = 0; public static final int TRUE_PRED = ~0; /** sometimes predicates are known to be true or false; we need * a way to represent this without resorting to a target language * value like true or TRUE. */ protected int constantValue = INVALID_PRED_VALUE; public Predicate(int constantValue) { predicateAST = new GrammarAST(); this.constantValue=constantValue; } public Predicate(GrammarAST predicate) { this.predicateAST = predicate; this.gated = predicate.getType()==ANTLRParser.GATED_SEMPRED || predicate.getType()==ANTLRParser.SYN_SEMPRED ; this.synpred = predicate.getType()==ANTLRParser.SYN_SEMPRED || predicate.getType()==ANTLRParser.BACKTRACK_SEMPRED; } public Predicate(Predicate p) { this.predicateAST = p.predicateAST; this.gated = p.gated; this.synpred = p.synpred; this.constantValue = p.constantValue; } /** Two predicates are the same if they are literally the same * text rather than same node in the grammar's AST. * Or, if they have the same constant value, return equal. * As of July 2006 I'm not sure these are needed. */ public boolean equals(Object o) { if ( !(o instanceof Predicate) ) { return false; } Predicate other = (Predicate)o; if (this.constantValue != other.constantValue){ return false; } if (this.constantValue != INVALID_PRED_VALUE){ return true; } return predicateAST.getText().equals(other.predicateAST.getText()); } public int hashCode() { if (constantValue != INVALID_PRED_VALUE){ return constantValue; } if ( predicateAST ==null ) { return 0; } return predicateAST.getText().hashCode(); } public ST genExpr(CodeGenerator generator, STGroup templates, DFA dfa) { ST eST = null; if ( templates!=null ) { if ( synpred ) { eST = templates.getInstanceOf("evalSynPredicate"); } else { eST = templates.getInstanceOf("evalPredicate"); generator.grammar.decisionsWhoseDFAsUsesSemPreds.add(dfa); } String predEnclosingRuleName = predicateAST.enclosingRuleName; /* String decisionEnclosingRuleName = dfa.getNFADecisionStartState().getEnclosingRule(); // if these rulenames are diff, then pred was hoisted out of rule // Currently I don't warn you about this as it could be annoying. // I do the translation anyway. */ //eST.add("pred", this.toString()); if ( generator!=null ) { eST.add("pred", generator.translateAction(predEnclosingRuleName,predicateAST)); } } else { eST = new ST(""); eST.add("pred", this.toString()); return eST; } if ( generator!=null ) { String description = generator.target.getTargetStringLiteralFromString(this.toString()); eST.add("description", description); } return eST; } @Override public SemanticContext getGatedPredicateContext() { if ( gated ) { return this; } return null; } @Override public boolean hasUserSemanticPredicate() { // user-specified sempred return predicateAST !=null && ( predicateAST.getType()==ANTLRParser.GATED_SEMPRED || predicateAST.getType()==ANTLRParser.SEMPRED ); } @Override public boolean isSyntacticPredicate() { return predicateAST !=null && ( predicateAST.getType()==ANTLRParser.SYN_SEMPRED || predicateAST.getType()==ANTLRParser.BACKTRACK_SEMPRED ); } @Override public void trackUseOfSyntacticPredicates(Grammar g) { if ( synpred ) { g.synPredNamesUsedInDFA.add(predicateAST.getText()); } } @Override public String toString() { if ( predicateAST ==null ) { return ""; } return predicateAST.getText(); } } public static class TruePredicate extends Predicate { public TruePredicate() { super(TRUE_PRED); } @Override public ST genExpr(CodeGenerator generator, STGroup templates, DFA dfa) { if ( templates!=null ) { return templates.getInstanceOf("true_value"); } return new ST("true"); } @Override public boolean hasUserSemanticPredicate() { return false; // not user specified. } @Override public String toString() { return "true"; // not used for code gen, just DOT and print outs } } public static class FalsePredicate extends Predicate { public FalsePredicate() { super(FALSE_PRED); } @Override public ST genExpr(CodeGenerator generator, STGroup templates, DFA dfa) { if ( templates!=null ) { return templates.getInstanceOf("false"); } return new ST("false"); } @Override public boolean hasUserSemanticPredicate() { return false; // not user specified. } @Override public String toString() { return "false"; // not used for code gen, just DOT and print outs } } public static abstract class CommutativePredicate extends SemanticContext { protected final Set operands = new HashSet(); protected int hashcode; public CommutativePredicate(SemanticContext a, SemanticContext b) { if (a.getClass() == this.getClass()){ CommutativePredicate predicate = (CommutativePredicate)a; operands.addAll(predicate.operands); } else { operands.add(a); } if (b.getClass() == this.getClass()){ CommutativePredicate predicate = (CommutativePredicate)b; operands.addAll(predicate.operands); } else { operands.add(b); } hashcode = calculateHashCode(); } public CommutativePredicate(HashSet contexts){ for (SemanticContext context : contexts){ if (context.getClass() == this.getClass()){ CommutativePredicate predicate = (CommutativePredicate)context; operands.addAll(predicate.operands); } else { operands.add(context); } } hashcode = calculateHashCode(); } @Override public SemanticContext getGatedPredicateContext() { SemanticContext result = null; for (SemanticContext semctx : operands) { SemanticContext gatedPred = semctx.getGatedPredicateContext(); if ( gatedPred!=null ) { result = combinePredicates(result, gatedPred); } } return result; } @Override public boolean hasUserSemanticPredicate() { for (SemanticContext semctx : operands) { if ( semctx.hasUserSemanticPredicate() ) { return true; } } return false; } @Override public boolean isSyntacticPredicate() { for (SemanticContext semctx : operands) { if ( semctx.isSyntacticPredicate() ) { return true; } } return false; } @Override public void trackUseOfSyntacticPredicates(Grammar g) { for (SemanticContext semctx : operands) { semctx.trackUseOfSyntacticPredicates(g); } } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj.getClass() == this.getClass()) { CommutativePredicate commutative = (CommutativePredicate)obj; Set otherOperands = commutative.operands; if (operands.size() != otherOperands.size()) return false; return operands.containsAll(otherOperands); } if (obj instanceof NOT) { NOT not = (NOT)obj; if (not.ctx instanceof CommutativePredicate && not.ctx.getClass() != this.getClass()) { Set otherOperands = ((CommutativePredicate)not.ctx).operands; if (operands.size() != otherOperands.size()) return false; ArrayList temp = new ArrayList(operands.size()); for (SemanticContext context : otherOperands) { temp.add(not(context)); } return operands.containsAll(temp); } } return false; } @Override public int hashCode(){ return hashcode; } @Override public String toString() { StringBuffer buf = new StringBuffer(); buf.append("("); int i = 0; for (SemanticContext semctx : operands) { if ( i>0 ) { buf.append(getOperandString()); } buf.append(semctx.toString()); i++; } buf.append(")"); return buf.toString(); } public abstract String getOperandString(); public abstract SemanticContext combinePredicates(SemanticContext left, SemanticContext right); public abstract int calculateHashCode(); } public static class AND extends CommutativePredicate { public AND(SemanticContext a, SemanticContext b) { super(a,b); } public AND(HashSet contexts) { super(contexts); } @Override public ST genExpr(CodeGenerator generator, STGroup templates, DFA dfa) { ST result = null; for (SemanticContext operand : operands) { if (result == null) result = operand.genExpr(generator, templates, dfa); ST eST = null; if ( templates!=null ) { eST = templates.getInstanceOf("andPredicates"); } else { eST = new ST("(&&)"); } eST.add("left", result); eST.add("right", operand.genExpr(generator,templates,dfa)); result = eST; } return result; } @Override public String getOperandString() { return "&&"; } @Override public SemanticContext combinePredicates(SemanticContext left, SemanticContext right) { return SemanticContext.and(left, right); } @Override public int calculateHashCode() { int hashcode = 0; for (SemanticContext context : operands) { hashcode = hashcode ^ context.hashCode(); } return hashcode; } } public static class OR extends CommutativePredicate { public OR(SemanticContext a, SemanticContext b) { super(a,b); } public OR(HashSet contexts) { super(contexts); } @Override public ST genExpr(CodeGenerator generator, STGroup templates, DFA dfa) { ST eST = null; if ( templates!=null ) { eST = templates.getInstanceOf("orPredicates"); } else { eST = new ST("(}>)"); } for (SemanticContext semctx : operands) { eST.add("operands", semctx.genExpr(generator,templates,dfa)); } return eST; } @Override public String getOperandString() { return "||"; } @Override public SemanticContext combinePredicates(SemanticContext left, SemanticContext right) { return SemanticContext.or(left, right); } @Override public int calculateHashCode() { int hashcode = 0; for (SemanticContext context : operands) { hashcode = ~hashcode ^ context.hashCode(); } return hashcode; } } public static class NOT extends SemanticContext { protected SemanticContext ctx; public NOT(SemanticContext ctx) { this.ctx = ctx; } @Override public ST genExpr(CodeGenerator generator, STGroup templates, DFA dfa) { ST eST = null; if ( templates!=null ) { eST = templates.getInstanceOf("notPredicate"); } else { eST = new ST("!()"); } eST.add("pred", ctx.genExpr(generator,templates,dfa)); return eST; } @Override public SemanticContext getGatedPredicateContext() { SemanticContext p = ctx.getGatedPredicateContext(); if ( p==null ) { return null; } return new NOT(p); } @Override public boolean hasUserSemanticPredicate() { return ctx.hasUserSemanticPredicate(); } @Override public boolean isSyntacticPredicate() { return ctx.isSyntacticPredicate(); } @Override public void trackUseOfSyntacticPredicates(Grammar g) { ctx.trackUseOfSyntacticPredicates(g); } @Override public boolean equals(Object object) { if ( !(object instanceof NOT) ) { return false; } return this.ctx.equals(((NOT)object).ctx); } @Override public int hashCode() { return ~ctx.hashCode(); } @Override public String toString() { return "!("+ctx+")"; } } public static SemanticContext and(SemanticContext a, SemanticContext b) { //System.out.println("AND: "+a+"&&"+b); SemanticContext[] terms = factorOr(a, b); SemanticContext commonTerms = terms[0]; a = terms[1]; b = terms[2]; boolean factored = commonTerms != null && commonTerms != EMPTY_SEMANTIC_CONTEXT && !(commonTerms instanceof TruePredicate); if (factored) { return or(commonTerms, and(a, b)); } //System.Console.Out.WriteLine( "AND: " + a + "&&" + b ); if (a instanceof FalsePredicate || b instanceof FalsePredicate) return new FalsePredicate(); if ( a==EMPTY_SEMANTIC_CONTEXT || a==null ) { return b; } if ( b==EMPTY_SEMANTIC_CONTEXT || b==null ) { return a; } if (a instanceof TruePredicate) return b; if (b instanceof TruePredicate) return a; //// Factoring takes care of this case //if (a.Equals(b)) // return a; //System.out.println("## have to AND"); return new AND(a,b); } public static SemanticContext or(SemanticContext a, SemanticContext b) { //System.out.println("OR: "+a+"||"+b); SemanticContext[] terms = factorAnd(a, b); SemanticContext commonTerms = terms[0]; a = terms[1]; b = terms[2]; boolean factored = commonTerms != null && commonTerms != EMPTY_SEMANTIC_CONTEXT && !(commonTerms instanceof FalsePredicate); if (factored) { return and(commonTerms, or(a, b)); } if ( a==EMPTY_SEMANTIC_CONTEXT || a==null || a instanceof FalsePredicate ) { return b; } if ( b==EMPTY_SEMANTIC_CONTEXT || b==null || b instanceof FalsePredicate ) { return a; } if ( a instanceof TruePredicate || b instanceof TruePredicate || commonTerms instanceof TruePredicate ) { return new TruePredicate(); } //// Factoring takes care of this case //if (a.equals(b)) // return a; if ( a instanceof NOT ) { NOT n = (NOT)a; // check for !p||p if ( n.ctx.equals(b) ) { return new TruePredicate(); } } else if ( b instanceof NOT ) { NOT n = (NOT)b; // check for p||!p if ( n.ctx.equals(a) ) { return new TruePredicate(); } } //System.out.println("## have to OR"); OR result = new OR(a,b); if (result.operands.size() == 1) return result.operands.iterator().next(); return result; } public static SemanticContext not(SemanticContext a) { if (a instanceof NOT) { return ((NOT)a).ctx; } if (a instanceof TruePredicate) return new FalsePredicate(); else if (a instanceof FalsePredicate) return new TruePredicate(); return new NOT(a); } // Factor so (a && b) == (result && a && b) public static SemanticContext[] factorAnd(SemanticContext a, SemanticContext b) { if (a == EMPTY_SEMANTIC_CONTEXT || a == null || a instanceof FalsePredicate) return new SemanticContext[] { EMPTY_SEMANTIC_CONTEXT, a, b }; if (b == EMPTY_SEMANTIC_CONTEXT || b == null || b instanceof FalsePredicate) return new SemanticContext[] { EMPTY_SEMANTIC_CONTEXT, a, b }; if (a instanceof TruePredicate || b instanceof TruePredicate) { return new SemanticContext[] { new TruePredicate(), EMPTY_SEMANTIC_CONTEXT, EMPTY_SEMANTIC_CONTEXT }; } HashSet opsA = new HashSet(getAndOperands(a)); HashSet opsB = new HashSet(getAndOperands(b)); HashSet result = new HashSet(opsA); result.retainAll(opsB); if (result.size() == 0) return new SemanticContext[] { EMPTY_SEMANTIC_CONTEXT, a, b }; opsA.removeAll(result); if (opsA.size() == 0) a = new TruePredicate(); else if (opsA.size() == 1) a = opsA.iterator().next(); else a = new AND(opsA); opsB.removeAll(result); if (opsB.size() == 0) b = new TruePredicate(); else if (opsB.size() == 1) b = opsB.iterator().next(); else b = new AND(opsB); if (result.size() == 1) return new SemanticContext[] { result.iterator().next(), a, b }; return new SemanticContext[] { new AND(result), a, b }; } // Factor so (a || b) == (result || a || b) public static SemanticContext[] factorOr(SemanticContext a, SemanticContext b) { HashSet opsA = new HashSet(getOrOperands(a)); HashSet opsB = new HashSet(getOrOperands(b)); HashSet result = new HashSet(opsA); result.retainAll(opsB); if (result.size() == 0) return new SemanticContext[] { EMPTY_SEMANTIC_CONTEXT, a, b }; opsA.removeAll(result); if (opsA.size() == 0) a = new FalsePredicate(); else if (opsA.size() == 1) a = opsA.iterator().next(); else a = new OR(opsA); opsB.removeAll(result); if (opsB.size() == 0) b = new FalsePredicate(); else if (opsB.size() == 1) b = opsB.iterator().next(); else b = new OR(opsB); if (result.size() == 1) return new SemanticContext[] { result.iterator().next(), a, b }; return new SemanticContext[] { new OR(result), a, b }; } public static Collection getAndOperands(SemanticContext context) { if (context instanceof AND) return ((AND)context).operands; if (context instanceof NOT) { Collection operands = getOrOperands(((NOT)context).ctx); List result = new ArrayList(operands.size()); for (SemanticContext operand : operands) { result.add(not(operand)); } return result; } ArrayList result = new ArrayList(); result.add(context); return result; } public static Collection getOrOperands(SemanticContext context) { if (context instanceof OR) return ((OR)context).operands; if (context instanceof NOT) { Collection operands = getAndOperands(((NOT)context).ctx); List result = new ArrayList(operands.size()); for (SemanticContext operand : operands) { result.add(not(operand)); } return result; } ArrayList result = new ArrayList(); result.add(context); return result; } }