/*
 * [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.
/** 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.
/** 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.
/** 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.
/** 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
/** 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
/** 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.
/** 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.
/** 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.
/** 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.
/** 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. */
@Override 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()); } @Override public int hashCode() { if (constantValue != INVALID_PRED_VALUE){ return constantValue; } if ( predicateAST ==null ) { return 0; } return predicateAST.getText().hashCode(); } @Override public ST genExpr(CodeGenerator generator, STGroup templates, DFA dfa) { ST eST; 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("<pred>"); 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 "<nopred>"; } 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<SemanticContext> operands = new HashSet<SemanticContext>(); 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<SemanticContext> 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<SemanticContext> 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<SemanticContext> otherOperands = ((CommutativePredicate)not.ctx).operands; if (operands.size() != otherOperands.size()) return false; ArrayList<SemanticContext> temp = new ArrayList<SemanticContext>(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() { StringBuilder buf = new StringBuilder(); 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<SemanticContext> 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); continue; } ST eST; if ( templates!=null ) { eST = templates.getInstanceOf("andPredicates"); } else { eST = new ST("(<left>&&<right>)"); } 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<SemanticContext> contexts) { super(contexts); } @Override public ST genExpr(CodeGenerator generator, STGroup templates, DFA dfa) { ST eST; if ( templates!=null ) { eST = templates.getInstanceOf("orPredicates"); } else { eST = new ST("(<operands; separator=\"||\">)"); } 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; if ( templates!=null ) { eST = templates.getInstanceOf("notPredicate"); } else { eST = new ST("!(<pred>)"); } 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); if (a instanceof FalsePredicate || b instanceof FalsePredicate) return new FalsePredicate(); 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"); AND result = new AND(a,b); if (result.operands.size() == 1) { return result.operands.iterator().next(); } return result; } public static SemanticContext or(SemanticContext a, SemanticContext b) { //System.out.println("OR: "+a+"||"+b); if (a instanceof TruePredicate || b instanceof TruePredicate) return new TruePredicate(); 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<SemanticContext> opsA = new HashSet<SemanticContext>(getAndOperands(a)); HashSet<SemanticContext> opsB = new HashSet<SemanticContext>(getAndOperands(b)); HashSet<SemanticContext> result = new HashSet<SemanticContext>(opsA); result.retainAll(opsB); if (result.isEmpty()) return new SemanticContext[] { EMPTY_SEMANTIC_CONTEXT, a, b }; opsA.removeAll(result); if (opsA.isEmpty()) a = new TruePredicate(); else if (opsA.size() == 1) a = opsA.iterator().next(); else a = new AND(opsA); opsB.removeAll(result); if (opsB.isEmpty()) 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<SemanticContext> opsA = new HashSet<SemanticContext>(getOrOperands(a)); HashSet<SemanticContext> opsB = new HashSet<SemanticContext>(getOrOperands(b)); HashSet<SemanticContext> result = new HashSet<SemanticContext>(opsA); result.retainAll(opsB); if (result.isEmpty()) return new SemanticContext[] { EMPTY_SEMANTIC_CONTEXT, a, b }; opsA.removeAll(result); if (opsA.isEmpty()) a = new FalsePredicate(); else if (opsA.size() == 1) a = opsA.iterator().next(); else a = new OR(opsA); opsB.removeAll(result); if (opsB.isEmpty()) 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<SemanticContext> getAndOperands(SemanticContext context) { if (context instanceof AND) return ((AND)context).operands; if (context instanceof NOT) { Collection<SemanticContext> operands = getOrOperands(((NOT)context).ctx); List<SemanticContext> result = new ArrayList<SemanticContext>(operands.size()); for (SemanticContext operand : operands) { result.add(not(operand)); } return result; } ArrayList<SemanticContext> result = new ArrayList<SemanticContext>(); result.add(context); return result; } public static Collection<SemanticContext> getOrOperands(SemanticContext context) { if (context instanceof OR) return ((OR)context).operands; if (context instanceof NOT) { Collection<SemanticContext> operands = getAndOperands(((NOT)context).ctx); List<SemanticContext> result = new ArrayList<SemanticContext>(operands.size()); for (SemanticContext operand : operands) { result.add(not(operand)); } return result; } ArrayList<SemanticContext> result = new ArrayList<SemanticContext>(); result.add(context); return result; } }