org.antlr.v4.runtime.atn.SemanticContext Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of antlr4-runtime Show documentation
Show all versions of antlr4-runtime Show documentation
The ANTLR 4 Runtime (Optimized)
/*
* Copyright (c) 2012 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.NotNull;
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 {@code p1&&p2}, or a sum of products {@code p1||p2}.
*
* I have scoped the {@link AND}, {@link OR}, and {@link Predicate} subclasses of
* {@link SemanticContext} within the scope of this outer class.
*/
public abstract class SemanticContext {
/**
* 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.
*/
public abstract boolean eval(Recognizer parser, RuleContext parserCallStack);
/**
* 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.
*
* - {@link #NONE}: if the predicate simplifies to {@code true} after
* precedence predicates are evaluated.
* - {@code null}: if the predicate simplifies to {@code false} after
* precedence predicates are evaluated.
* - {@code this}: if the semantic context is not changed as a result of
* precedence predicate evaluation.
* - A non-{@code null} {@link SemanticContext}: the new simplified
* semantic context after precedence predicates are evaluated.
*
*/
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 {
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
*/
public static abstract class Operator extends SemanticContext {
/**
* Gets the operands for the semantic context operator.
*
* @return a collection of {@link SemanticContext} operands for the
* operator.
*
* @since 4.3
*/
@NotNull
public abstract Collection getOperands();
}
/**
* A semantic context which is true whenever none of the contained contexts
* is false.
*/
public static class AND extends Operator {
@NotNull public final SemanticContext[] opnds;
public AND(@NotNull SemanticContext a, @NotNull SemanticContext b) {
Set operands = new HashSet();
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 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 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.
*/
@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 operands = new ArrayList();
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(opnds, "&&");
}
}
/**
* A semantic context which is true whenever at least one of the contained
* contexts is true.
*/
public static class OR extends Operator {
@NotNull public final SemanticContext[] opnds;
public OR(@NotNull SemanticContext a, @NotNull SemanticContext b) {
Set operands = new HashSet();
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 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 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.
*/
@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 operands = new ArrayList();
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(opnds, "||");
}
}
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
*/
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 filterPrecedencePredicates(Collection extends SemanticContext> collection) {
ArrayList 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();
}
result.add((PrecedencePredicate)context);
iterator.remove();
}
}
if (result == null) {
return Collections.emptyList();
}
return result;
}
}