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

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

The newest version!
/*
 * 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.misc.MurmurHash;
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.misc.Nullable;
import org.antlr.v4.runtime.misc.ObjectEqualityComparator;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.IdentityHashMap;
import java.util.Map;

/**
 * Represents a location with context in an ATN. The location is identified by
 * the following values:
 *
 * 
    *
  • The current ATN state
  • *
  • The predicted alternative
  • *
  • The semantic context which must be true for this configuration to be * enabled
  • *
  • The syntactic context, which is represented as a graph-structured stack * whose path(s) lead to the root of the rule invocations leading to this * state.
  • *
* *

In addition to these values, {@link ATNConfig} stores several properties * about paths taken to get to the location which were added over time to help * with performance, correctness, and/or debugging.

* *
    *
  • {@link #getReachesIntoOuterContext()}: Used to ensure semantic predicates * are not evaluated in the wrong context.
  • *
  • {@link #hasPassedThroughNonGreedyDecision()}: Used for enabling * first-match-wins instead of longest-match-wins after crossing a non-greedy * decision.
  • *
  • {@link #getLexerActionExecutor()}: Used for tracking the lexer action(s) * to execute should this instance be selected during lexing.
  • *
  • {@link #isPrecedenceFilterSuppressed()}: A state variable for one of the * dynamic disambiguation strategies employed by * {@link ParserATNSimulator#applyPrecedenceFilter}.
  • *
* *

Due to the use of a graph-structured stack, a single {@link ATNConfig} is * capable of representing many individual ATN configurations which reached the * same location in an ATN by following different paths.

* *

PERF: To conserve memory, {@link ATNConfig} is split into several * different concrete types. {@link ATNConfig} itself stores the minimum amount * of information typically used to define an {@link ATNConfig} instance. * Various derived types provide additional storage space for cases where a * non-default value is used for some of the object properties. The * {@link ATNConfig#create} and {@link ATNConfig#transform} methods * automatically select the smallest concrete type capable of representing the * unique information for any given {@link ATNConfig}.

*/ public class ATNConfig { /** * This field stores the bit mask for implementing the * {@link #isPrecedenceFilterSuppressed} property as a bit within the * existing {@link #altAndOuterContextDepth} field. */ private static final int SUPPRESS_PRECEDENCE_FILTER = 0x80000000; /** The ATN state associated with this configuration */ @NotNull private final ATNState state; /** * This is a bit-field currently containing the following values. * *
    *
  • 0x00FFFFFF: Alternative
  • *
  • 0x7F000000: Outer context depth
  • *
  • 0x80000000: Suppress precedence filter
  • *
*/ private int altAndOuterContextDepth; /** The stack of invoking states leading to the rule/states associated * with this config. We track only those contexts pushed during * execution of the ATN simulator. */ @NotNull private PredictionContext context; protected ATNConfig(@NotNull ATNState state, int alt, @NotNull PredictionContext context) { assert (alt & 0xFFFFFF) == alt; this.state = state; this.altAndOuterContextDepth = alt; this.context = context; } protected ATNConfig(@NotNull ATNConfig c, @NotNull ATNState state, @NotNull PredictionContext context) { this.state = state; this.altAndOuterContextDepth = c.altAndOuterContextDepth; this.context = context; } public static ATNConfig create(@NotNull ATNState state, int alt, @Nullable PredictionContext context) { return create(state, alt, context, SemanticContext.NONE, null); } public static ATNConfig create(@NotNull ATNState state, int alt, @Nullable PredictionContext context, @NotNull SemanticContext semanticContext) { return create(state, alt, context, semanticContext, null); } public static ATNConfig create(@NotNull ATNState state, int alt, @Nullable PredictionContext context, @NotNull SemanticContext semanticContext, LexerActionExecutor lexerActionExecutor) { if (semanticContext != SemanticContext.NONE) { if (lexerActionExecutor != null) { return new ActionSemanticContextATNConfig(lexerActionExecutor, semanticContext, state, alt, context, false); } else { return new SemanticContextATNConfig(semanticContext, state, alt, context); } } else if (lexerActionExecutor != null) { return new ActionATNConfig(lexerActionExecutor, state, alt, context, false); } else { return new ATNConfig(state, alt, context); } } /** Gets the ATN state associated with this configuration */ @NotNull public final ATNState getState() { return state; } /** What alt (or lexer rule) is predicted by this configuration */ public final int getAlt() { return altAndOuterContextDepth & 0x00FFFFFF; } @NotNull public final PredictionContext getContext() { return context; } public void setContext(@NotNull PredictionContext context) { this.context = context; } public final boolean getReachesIntoOuterContext() { return getOuterContextDepth() != 0; } /** * We cannot execute predicates dependent upon local context unless * we know for sure we are in the correct context. Because there is * no way to do this efficiently, we simply cannot evaluate * dependent predicates unless we are in the rule that initially * invokes the ATN simulator. * *

* closure() tracks the depth of how far we dip into the outer context: * depth > 0. Note that it may not be totally accurate depth since I * don't ever decrement. TODO: make it a boolean then

*/ public final int getOuterContextDepth() { return (altAndOuterContextDepth >>> 24) & 0x7F; } public void setOuterContextDepth(int outerContextDepth) { assert outerContextDepth >= 0; // saturate at 0x7F - everything but zero/positive is only used for debug information anyway outerContextDepth = Math.min(outerContextDepth, 0x7F); this.altAndOuterContextDepth = (outerContextDepth << 24) | (altAndOuterContextDepth & ~0x7F000000); } @Nullable public LexerActionExecutor getLexerActionExecutor() { return null; } @NotNull public SemanticContext getSemanticContext() { return SemanticContext.NONE; } public boolean hasPassedThroughNonGreedyDecision() { return false; } @Override public final ATNConfig clone() { return transform(this.getState(), false); } public final ATNConfig transform(@NotNull ATNState state, boolean checkNonGreedy) { return transform(state, this.context, this.getSemanticContext(), checkNonGreedy, this.getLexerActionExecutor()); } public final ATNConfig transform(@NotNull ATNState state, @NotNull SemanticContext semanticContext, boolean checkNonGreedy) { return transform(state, this.context, semanticContext, checkNonGreedy, this.getLexerActionExecutor()); } public final ATNConfig transform(@NotNull ATNState state, @Nullable PredictionContext context, boolean checkNonGreedy) { return transform(state, context, this.getSemanticContext(), checkNonGreedy, this.getLexerActionExecutor()); } public final ATNConfig transform(@NotNull ATNState state, LexerActionExecutor lexerActionExecutor, boolean checkNonGreedy) { return transform(state, context, this.getSemanticContext(), checkNonGreedy, lexerActionExecutor); } private ATNConfig transform(@NotNull ATNState state, @Nullable PredictionContext context, @NotNull SemanticContext semanticContext, boolean checkNonGreedy, LexerActionExecutor lexerActionExecutor) { boolean passedThroughNonGreedy = checkNonGreedy && checkNonGreedyDecision(this, state); if (semanticContext != SemanticContext.NONE) { if (lexerActionExecutor != null || passedThroughNonGreedy) { return new ActionSemanticContextATNConfig(lexerActionExecutor, semanticContext, this, state, context, passedThroughNonGreedy); } else { return new SemanticContextATNConfig(semanticContext, this, state, context); } } else if (lexerActionExecutor != null || passedThroughNonGreedy) { return new ActionATNConfig(lexerActionExecutor, this, state, context, passedThroughNonGreedy); } else { return new ATNConfig(this, state, context); } } private static boolean checkNonGreedyDecision(ATNConfig source, ATNState target) { return source.hasPassedThroughNonGreedyDecision() || target instanceof DecisionState && ((DecisionState)target).nonGreedy; } public ATNConfig appendContext(int context, PredictionContextCache contextCache) { PredictionContext appendedContext = getContext().appendContext(context, contextCache); ATNConfig result = transform(getState(), appendedContext, false); return result; } public ATNConfig appendContext(PredictionContext context, PredictionContextCache contextCache) { PredictionContext appendedContext = getContext().appendContext(context, contextCache); ATNConfig result = transform(getState(), appendedContext, false); return result; } /** * Determines if this {@link ATNConfig} fully contains another * {@link ATNConfig}. * *

An ATN configuration represents a position (including context) in an * ATN during parsing. Since {@link ATNConfig} stores the context as a * graph, a single {@link ATNConfig} instance is capable of representing * many ATN configurations which are all in the same "location" but have * different contexts. These {@link ATNConfig} instances are again merged * when they are added to an {@link ATNConfigSet}. This method supports * {@link ATNConfigSet#contains} by evaluating whether a particular * {@link ATNConfig} contains all of the ATN configurations represented by * another {@link ATNConfig}.

* *

An {@link ATNConfig} a contains another {@link ATNConfig} * b if all of the following conditions are met:

* *
    *
  • The configurations are in the same state ({@link #getState()})
  • *
  • The configurations predict the same alternative * ({@link #getAlt()})
  • *
  • The semantic context of a implies the semantic context of * b (this method performs a weaker equality check)
  • *
  • Joining the prediction contexts of a and b results * in the prediction context of a
  • *
* *

This method implements a conservative approximation of containment. As * a result, when this method returns {code true} it is known that parsing * from {@code subconfig} can only recognize a subset of the inputs which * can be recognized starting at the current {@link ATNConfig}. However, due * to the imprecise evaluation of implication for the semantic contexts, no * assumptions can be made about the relationship between the configurations * when this method returns {@code false}.

* * @param subconfig The sub configuration. * @return {@code true} if this configuration contains {@code subconfig}; * otherwise, {@code false}. */ public boolean contains(ATNConfig subconfig) { if (this.getState().stateNumber != subconfig.getState().stateNumber || this.getAlt() != subconfig.getAlt() || !this.getSemanticContext().equals(subconfig.getSemanticContext())) { return false; } Deque leftWorkList = new ArrayDeque(); Deque rightWorkList = new ArrayDeque(); leftWorkList.add(getContext()); rightWorkList.add(subconfig.getContext()); while (!leftWorkList.isEmpty()) { PredictionContext left = leftWorkList.pop(); PredictionContext right = rightWorkList.pop(); if (left == right) { return true; } if (left.size() < right.size()) { return false; } if (right.isEmpty()) { return left.hasEmpty(); } else { for (int i = 0; i < right.size(); i++) { int index = left.findReturnState(right.getReturnState(i)); if (index < 0) { // assumes invokingStates has no duplicate entries return false; } leftWorkList.push(left.getParent(index)); rightWorkList.push(right.getParent(i)); } } } return false; } public final boolean isPrecedenceFilterSuppressed() { return (altAndOuterContextDepth & SUPPRESS_PRECEDENCE_FILTER) != 0; } public final void setPrecedenceFilterSuppressed(boolean value) { if (value) { this.altAndOuterContextDepth |= SUPPRESS_PRECEDENCE_FILTER; } else { this.altAndOuterContextDepth &= ~SUPPRESS_PRECEDENCE_FILTER; } } /** An ATN configuration is equal to another if both have * the same state, they predict the same alternative, and * syntactic/semantic contexts are the same. */ @Override public boolean equals(Object o) { if (!(o instanceof ATNConfig)) { return false; } return this.equals((ATNConfig)o); } public boolean equals(ATNConfig other) { if (this == other) { return true; } else if (other == null) { return false; } return this.getState().stateNumber==other.getState().stateNumber && this.getAlt()==other.getAlt() && this.getReachesIntoOuterContext() == other.getReachesIntoOuterContext() && this.getContext().equals(other.getContext()) && this.getSemanticContext().equals(other.getSemanticContext()) && this.isPrecedenceFilterSuppressed() == other.isPrecedenceFilterSuppressed() && this.hasPassedThroughNonGreedyDecision() == other.hasPassedThroughNonGreedyDecision() && ObjectEqualityComparator.INSTANCE.equals(this.getLexerActionExecutor(), other.getLexerActionExecutor()); } @Override public int hashCode() { int hashCode = MurmurHash.initialize(7); hashCode = MurmurHash.update(hashCode, getState().stateNumber); hashCode = MurmurHash.update(hashCode, getAlt()); hashCode = MurmurHash.update(hashCode, getReachesIntoOuterContext() ? 1 : 0); hashCode = MurmurHash.update(hashCode, getContext()); hashCode = MurmurHash.update(hashCode, getSemanticContext()); hashCode = MurmurHash.update(hashCode, hasPassedThroughNonGreedyDecision() ? 1 : 0); hashCode = MurmurHash.update(hashCode, getLexerActionExecutor()); hashCode = MurmurHash.finish(hashCode, 7); return hashCode; } /** * Returns a graphical representation of the current {@link ATNConfig} in * Graphviz format. The graph can be stored to a .dot file * and then rendered to an image using Graphviz. * * @return A Graphviz graph representing the current {@link ATNConfig}. * * @see http://www.graphviz.org/ */ public String toDotString() { StringBuilder builder = new StringBuilder(); builder.append("digraph G {\n"); builder.append("rankdir=LR;\n"); Map visited = new IdentityHashMap(); Deque workList = new ArrayDeque(); workList.add(getContext()); visited.put(getContext(), getContext()); while (!workList.isEmpty()) { PredictionContext current = workList.pop(); for (int i = 0; i < current.size(); i++) { builder.append(" s").append(System.identityHashCode(current)); builder.append("->"); builder.append("s").append(System.identityHashCode(current.getParent(i))); builder.append("[label=\"").append(current.getReturnState(i)).append("\"];\n"); if (visited.put(current.getParent(i), current.getParent(i)) == null) { workList.push(current.getParent(i)); } } } builder.append("}\n"); return builder.toString(); } @Override public String toString() { return toString(null, true, false); } public String toString(@Nullable Recognizer recog, boolean showAlt) { return toString(recog, showAlt, true); } public String toString(@Nullable Recognizer recog, boolean showAlt, boolean showContext) { StringBuilder buf = new StringBuilder(); // if ( state.ruleIndex>=0 ) { // if ( recog!=null ) buf.append(recog.getRuleNames()[state.ruleIndex]+":"); // else buf.append(state.ruleIndex+":"); // } String[] contexts; if (showContext) { contexts = getContext().toStrings(recog, this.getState().stateNumber); } else { contexts = new String[] { "?" }; } boolean first = true; for (String contextDesc : contexts) { if ( first ) { first = false; } else { buf.append(", "); } buf.append('('); buf.append(getState()); if ( showAlt ) { buf.append(","); buf.append(getAlt()); } if ( getContext()!=null ) { buf.append(","); buf.append(contextDesc); } if ( getSemanticContext()!=null && getSemanticContext() != SemanticContext.NONE ) { buf.append(","); buf.append(getSemanticContext()); } if ( getReachesIntoOuterContext() ) { buf.append(",up=").append(getOuterContextDepth()); } buf.append(')'); } return buf.toString(); } /** * This class was derived from {@link ATNConfig} purely as a memory * optimization. It allows for the creation of an {@link ATNConfig} with a * non-default semantic context. * *

See the {@link ATNConfig} documentation for more information about * conserving memory through the use of several concrete types.

*/ private static class SemanticContextATNConfig extends ATNConfig { @NotNull private final SemanticContext semanticContext; public SemanticContextATNConfig(SemanticContext semanticContext, @NotNull ATNState state, int alt, @Nullable PredictionContext context) { super(state, alt, context); this.semanticContext = semanticContext; } public SemanticContextATNConfig(SemanticContext semanticContext, @NotNull ATNConfig c, @NotNull ATNState state, @Nullable PredictionContext context) { super(c, state, context); this.semanticContext = semanticContext; } @Override public SemanticContext getSemanticContext() { return semanticContext; } } /** * This class was derived from {@link ATNConfig} purely as a memory * optimization. It allows for the creation of an {@link ATNConfig} with a * lexer action. * *

See the {@link ATNConfig} documentation for more information about * conserving memory through the use of several concrete types.

*/ private static class ActionATNConfig extends ATNConfig { private final LexerActionExecutor lexerActionExecutor; private final boolean passedThroughNonGreedyDecision; public ActionATNConfig(LexerActionExecutor lexerActionExecutor, @NotNull ATNState state, int alt, @Nullable PredictionContext context, boolean passedThroughNonGreedyDecision) { super(state, alt, context); this.lexerActionExecutor = lexerActionExecutor; this.passedThroughNonGreedyDecision = passedThroughNonGreedyDecision; } protected ActionATNConfig(LexerActionExecutor lexerActionExecutor, @NotNull ATNConfig c, @NotNull ATNState state, @Nullable PredictionContext context, boolean passedThroughNonGreedyDecision) { super(c, state, context); if (c.getSemanticContext() != SemanticContext.NONE) { throw new UnsupportedOperationException(); } this.lexerActionExecutor = lexerActionExecutor; this.passedThroughNonGreedyDecision = passedThroughNonGreedyDecision; } @Override public LexerActionExecutor getLexerActionExecutor() { return lexerActionExecutor; } @Override public boolean hasPassedThroughNonGreedyDecision() { return passedThroughNonGreedyDecision; } } /** * This class was derived from {@link SemanticContextATNConfig} purely as a memory * optimization. It allows for the creation of an {@link ATNConfig} with * both a lexer action and a non-default semantic context. * *

See the {@link ATNConfig} documentation for more information about * conserving memory through the use of several concrete types.

*/ private static class ActionSemanticContextATNConfig extends SemanticContextATNConfig { private final LexerActionExecutor lexerActionExecutor; private final boolean passedThroughNonGreedyDecision; public ActionSemanticContextATNConfig(LexerActionExecutor lexerActionExecutor, @NotNull SemanticContext semanticContext, @NotNull ATNState state, int alt, @Nullable PredictionContext context, boolean passedThroughNonGreedyDecision) { super(semanticContext, state, alt, context); this.lexerActionExecutor = lexerActionExecutor; this.passedThroughNonGreedyDecision = passedThroughNonGreedyDecision; } public ActionSemanticContextATNConfig(LexerActionExecutor lexerActionExecutor, @NotNull SemanticContext semanticContext, @NotNull ATNConfig c, @NotNull ATNState state, @Nullable PredictionContext context, boolean passedThroughNonGreedyDecision) { super(semanticContext, c, state, context); this.lexerActionExecutor = lexerActionExecutor; this.passedThroughNonGreedyDecision = passedThroughNonGreedyDecision; } @Override public LexerActionExecutor getLexerActionExecutor() { return lexerActionExecutor; } @Override public boolean hasPassedThroughNonGreedyDecision() { return passedThroughNonGreedyDecision; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy