/*
* [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.
*/
@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;
}
}