All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.antlr.v4.runtime.atn.SemanticContext Maven / Gradle / Ivy

There is a newer version: 2.12.15
Show newest version
/*
 * 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 {@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 */ public abstract Collection getOperands(); } /** * 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 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(Arrays.asList(opnds).iterator(), "&&"); } } /** * 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 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(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 */ 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 collection) { ArrayList result = null; for (Iterator 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; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy