/*
* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
* Use of this file is governed by the BSD 3-clause license that
* can be found in the LICENSE.txt file in the project root.
*/
package org.antlr.v4.runtime.atn;
import org.antlr.v4.runtime.Recognizer;
import org.antlr.v4.runtime.RuleContext;
import org.antlr.v4.runtime.misc.MurmurHash;
import org.antlr.v4.runtime.misc.Utils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
A tree structure used to record the semantic context in which an ATN configuration is valid. It's either a single predicate, a conjunction p1&&p2
, or a sum of products p1||p2
. I have scoped the AND
, OR
, and Predicate
subclasses of SemanticContext
within the scope of this outer class.
/** A tree structure used to record the semantic context in which
* an ATN configuration is valid. It's either a single predicate,
* a conjunction {@code p1&&p2}, or a sum of products {@code p1||p2}.
*
* <p>I have scoped the {@link AND}, {@link OR}, and {@link Predicate} subclasses of
* {@link SemanticContext} within the scope of this outer class.</p>
*/
public abstract class SemanticContext {
The default SemanticContext
, which is semantically equivalent to a predicate of the form {true}?
. /**
* The default {@link SemanticContext}, which is semantically equivalent to
* a predicate of the form {@code {true}?}.
*/
public static final SemanticContext NONE = new Predicate();
For context independent predicates, we evaluate them without a local
context (i.e., null context). That way, we can evaluate them without
having to create proper rule-specific context during prediction (as
opposed to the parser, which creates them naturally). In a practical
sense, this avoids a cast exception from RuleContext to myruleContext.
For context dependent predicates, we must pass in a local context so that
references such as $arg evaluate properly as _localctx.arg. We only
capture context dependent predicates in the context in which we begin
prediction, so we passed in the outer context here in case of context
dependent predicate evaluation.
/**
* For context independent predicates, we evaluate them without a local
* context (i.e., null context). That way, we can evaluate them without
* having to create proper rule-specific context during prediction (as
* opposed to the parser, which creates them naturally). In a practical
* sense, this avoids a cast exception from RuleContext to myruleContext.
*
* <p>For context dependent predicates, we must pass in a local context so that
* references such as $arg evaluate properly as _localctx.arg. We only
* capture context dependent predicates in the context in which we begin
* prediction, so we passed in the outer context here in case of context
* dependent predicate evaluation.</p>
*/
public abstract boolean eval(Recognizer<?,?> parser, RuleContext parserCallStack);
Evaluate the precedence predicates for the context and reduce the result.
Params: - parser – The parser instance.
- parserCallStack –
Returns: The simplified semantic context after precedence predicates are
evaluated, which will be one of the following values.
NONE
: if the predicate simplifies to true
after precedence predicates are evaluated.
null
: if the predicate simplifies to false
after precedence predicates are evaluated.
this
: if the semantic context is not changed as a result of precedence predicate evaluation.
- A non-
null
SemanticContext
: the new simplified semantic context after precedence predicates are evaluated.
/**
* Evaluate the precedence predicates for the context and reduce the result.
*
* @param parser The parser instance.
* @param parserCallStack
* @return The simplified semantic context after precedence predicates are
* evaluated, which will be one of the following values.
* <ul>
* <li>{@link #NONE}: if the predicate simplifies to {@code true} after
* precedence predicates are evaluated.</li>
* <li>{@code null}: if the predicate simplifies to {@code false} after
* precedence predicates are evaluated.</li>
* <li>{@code this}: if the semantic context is not changed as a result of
* precedence predicate evaluation.</li>
* <li>A non-{@code null} {@link SemanticContext}: the new simplified
* semantic context after precedence predicates are evaluated.</li>
* </ul>
*/
public SemanticContext evalPrecedence(Recognizer<?,?> parser, RuleContext parserCallStack) {
return this;
}
public static class Predicate extends SemanticContext {
public final int ruleIndex;
public final int predIndex;
public final boolean isCtxDependent; // e.g., $i ref in pred
protected Predicate() {
this.ruleIndex = -1;
this.predIndex = -1;
this.isCtxDependent = false;
}
public Predicate(int ruleIndex, int predIndex, boolean isCtxDependent) {
this.ruleIndex = ruleIndex;
this.predIndex = predIndex;
this.isCtxDependent = isCtxDependent;
}
@Override
public boolean eval(Recognizer<?,?> parser, RuleContext parserCallStack) {
RuleContext localctx = isCtxDependent ? parserCallStack : null;
return parser.sempred(localctx, ruleIndex, predIndex);
}
@Override
public int hashCode() {
int hashCode = MurmurHash.initialize();
hashCode = MurmurHash.update(hashCode, ruleIndex);
hashCode = MurmurHash.update(hashCode, predIndex);
hashCode = MurmurHash.update(hashCode, isCtxDependent ? 1 : 0);
hashCode = MurmurHash.finish(hashCode, 3);
return hashCode;
}
@Override
public boolean equals(Object obj) {
if ( !(obj instanceof Predicate) ) return false;
if ( this == obj ) return true;
Predicate p = (Predicate)obj;
return this.ruleIndex == p.ruleIndex &&
this.predIndex == p.predIndex &&
this.isCtxDependent == p.isCtxDependent;
}
@Override
public String toString() {
return "{"+ruleIndex+":"+predIndex+"}?";
}
}
public static class PrecedencePredicate extends SemanticContext implements Comparable<PrecedencePredicate> {
public final int precedence;
protected PrecedencePredicate() {
this.precedence = 0;
}
public PrecedencePredicate(int precedence) {
this.precedence = precedence;
}
@Override
public boolean eval(Recognizer<?, ?> parser, RuleContext parserCallStack) {
return parser.precpred(parserCallStack, precedence);
}
@Override
public SemanticContext evalPrecedence(Recognizer<?, ?> parser, RuleContext parserCallStack) {
if (parser.precpred(parserCallStack, precedence)) {
return SemanticContext.NONE;
}
else {
return null;
}
}
@Override
public int compareTo(PrecedencePredicate o) {
return precedence - o.precedence;
}
@Override
public int hashCode() {
int hashCode = 1;
hashCode = 31 * hashCode + precedence;
return hashCode;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof PrecedencePredicate)) {
return false;
}
if (this == obj) {
return true;
}
PrecedencePredicate other = (PrecedencePredicate)obj;
return this.precedence == other.precedence;
}
@Override
// precedence >= _precedenceStack.peek()
public String toString() {
return "{"+precedence+">=prec}?";
}
}
This is the base class for semantic context "operators", which operate on
a collection of semantic context "operands".
Since: 4.3
/**
* This is the base class for semantic context "operators", which operate on
* a collection of semantic context "operands".
*
* @since 4.3
*/
public static abstract class Operator extends SemanticContext {
Gets the operands for the semantic context operator.
Returns: a collection of SemanticContext
operands for the operator. Since: 4.3
/**
* Gets the operands for the semantic context operator.
*
* @return a collection of {@link SemanticContext} operands for the
* operator.
*
* @since 4.3
*/
public abstract Collection<SemanticContext> getOperands();
}
A semantic context which is true whenever none of the contained contexts
is false.
/**
* A semantic context which is true whenever none of the contained contexts
* is false.
*/
public static class AND extends Operator {
public final SemanticContext[] opnds;
public AND(SemanticContext a, SemanticContext b) {
Set<SemanticContext> operands = new HashSet<SemanticContext>();
if ( a instanceof AND ) operands.addAll(Arrays.asList(((AND)a).opnds));
else operands.add(a);
if ( b instanceof AND ) operands.addAll(Arrays.asList(((AND)b).opnds));
else operands.add(b);
List<PrecedencePredicate> precedencePredicates = filterPrecedencePredicates(operands);
if (!precedencePredicates.isEmpty()) {
// interested in the transition with the lowest precedence
PrecedencePredicate reduced = Collections.min(precedencePredicates);
operands.add(reduced);
}
opnds = operands.toArray(new SemanticContext[operands.size()]);
}
@Override
public Collection<SemanticContext> getOperands() {
return Arrays.asList(opnds);
}
@Override
public boolean equals(Object obj) {
if ( this==obj ) return true;
if ( !(obj instanceof AND) ) return false;
AND other = (AND)obj;
return Arrays.equals(this.opnds, other.opnds);
}
@Override
public int hashCode() {
return MurmurHash.hashCode(opnds, AND.class.hashCode());
}
{@inheritDoc}
The evaluation of predicates by this context is short-circuiting, but
unordered.
/**
* {@inheritDoc}
*
* <p>
* The evaluation of predicates by this context is short-circuiting, but
* unordered.</p>
*/
@Override
public boolean eval(Recognizer<?,?> parser, RuleContext parserCallStack) {
for (SemanticContext opnd : opnds) {
if ( !opnd.eval(parser, parserCallStack) ) return false;
}
return true;
}
@Override
public SemanticContext evalPrecedence(Recognizer<?, ?> parser, RuleContext parserCallStack) {
boolean differs = false;
List<SemanticContext> operands = new ArrayList<SemanticContext>();
for (SemanticContext context : opnds) {
SemanticContext evaluated = context.evalPrecedence(parser, parserCallStack);
differs |= (evaluated != context);
if (evaluated == null) {
// The AND context is false if any element is false
return null;
}
else if (evaluated != NONE) {
// Reduce the result by skipping true elements
operands.add(evaluated);
}
}
if (!differs) {
return this;
}
if (operands.isEmpty()) {
// all elements were true, so the AND context is true
return NONE;
}
SemanticContext result = operands.get(0);
for (int i = 1; i < operands.size(); i++) {
result = SemanticContext.and(result, operands.get(i));
}
return result;
}
@Override
public String toString() {
return Utils.join(Arrays.asList(opnds).iterator(), "&&");
}
}
A semantic context which is true whenever at least one of the contained
contexts is true.
/**
* A semantic context which is true whenever at least one of the contained
* contexts is true.
*/
public static class OR extends Operator {
public final SemanticContext[] opnds;
public OR(SemanticContext a, SemanticContext b) {
Set<SemanticContext> operands = new HashSet<SemanticContext>();
if ( a instanceof OR ) operands.addAll(Arrays.asList(((OR)a).opnds));
else operands.add(a);
if ( b instanceof OR ) operands.addAll(Arrays.asList(((OR)b).opnds));
else operands.add(b);
List<PrecedencePredicate> precedencePredicates = filterPrecedencePredicates(operands);
if (!precedencePredicates.isEmpty()) {
// interested in the transition with the highest precedence
PrecedencePredicate reduced = Collections.max(precedencePredicates);
operands.add(reduced);
}
this.opnds = operands.toArray(new SemanticContext[operands.size()]);
}
@Override
public Collection<SemanticContext> getOperands() {
return Arrays.asList(opnds);
}
@Override
public boolean equals(Object obj) {
if ( this==obj ) return true;
if ( !(obj instanceof OR) ) return false;
OR other = (OR)obj;
return Arrays.equals(this.opnds, other.opnds);
}
@Override
public int hashCode() {
return MurmurHash.hashCode(opnds, OR.class.hashCode());
}
{@inheritDoc}
The evaluation of predicates by this context is short-circuiting, but
unordered.
/**
* {@inheritDoc}
*
* <p>
* The evaluation of predicates by this context is short-circuiting, but
* unordered.</p>
*/
@Override
public boolean eval(Recognizer<?,?> parser, RuleContext parserCallStack) {
for (SemanticContext opnd : opnds) {
if ( opnd.eval(parser, parserCallStack) ) return true;
}
return false;
}
@Override
public SemanticContext evalPrecedence(Recognizer<?, ?> parser, RuleContext parserCallStack) {
boolean differs = false;
List<SemanticContext> operands = new ArrayList<SemanticContext>();
for (SemanticContext context : opnds) {
SemanticContext evaluated = context.evalPrecedence(parser, parserCallStack);
differs |= (evaluated != context);
if (evaluated == NONE) {
// The OR context is true if any element is true
return NONE;
}
else if (evaluated != null) {
// Reduce the result by skipping false elements
operands.add(evaluated);
}
}
if (!differs) {
return this;
}
if (operands.isEmpty()) {
// all elements were false, so the OR context is false
return null;
}
SemanticContext result = operands.get(0);
for (int i = 1; i < operands.size(); i++) {
result = SemanticContext.or(result, operands.get(i));
}
return result;
}
@Override
public String toString() {
return Utils.join(Arrays.asList(opnds).iterator(), "||");
}
}
public static SemanticContext and(SemanticContext a, SemanticContext b) {
if ( a == null || a == NONE ) return b;
if ( b == null || b == NONE ) return a;
AND result = new AND(a, b);
if (result.opnds.length == 1) {
return result.opnds[0];
}
return result;
}
@see ParserATNSimulator#getPredsForAmbigAlts
/**
*
* @see ParserATNSimulator#getPredsForAmbigAlts
*/
public static SemanticContext or(SemanticContext a, SemanticContext b) {
if ( a == null ) return b;
if ( b == null ) return a;
if ( a == NONE || b == NONE ) return NONE;
OR result = new OR(a, b);
if (result.opnds.length == 1) {
return result.opnds[0];
}
return result;
}
private static List<PrecedencePredicate> filterPrecedencePredicates(Collection<? extends SemanticContext> collection) {
ArrayList<PrecedencePredicate> result = null;
for (Iterator<? extends SemanticContext> iterator = collection.iterator(); iterator.hasNext(); ) {
SemanticContext context = iterator.next();
if (context instanceof PrecedencePredicate) {
if (result == null) {
result = new ArrayList<PrecedencePredicate>();
}
result.add((PrecedencePredicate)context);
iterator.remove();
}
}
if (result == null) {
return Collections.emptyList();
}
return result;
}
}