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

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

There is a newer version: 4.9.0
Show 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.RuleContext;
import org.antlr.v4.runtime.misc.AbstractEqualityComparator;
import org.antlr.v4.runtime.misc.FlexibleHashMap;
import org.antlr.v4.runtime.misc.MurmurHash;
import org.antlr.v4.runtime.misc.NotNull;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentMap;

public abstract class PredictionContext {
	@NotNull
	public static final PredictionContext EMPTY_LOCAL = EmptyPredictionContext.LOCAL_CONTEXT;
	@NotNull
	public static final PredictionContext EMPTY_FULL = EmptyPredictionContext.FULL_CONTEXT;

	public static final int EMPTY_LOCAL_STATE_KEY = Integer.MIN_VALUE;
	public static final int EMPTY_FULL_STATE_KEY = Integer.MAX_VALUE;

	private static final int INITIAL_HASH = 1;

	/**
	 * Stores the computed hash code of this {@link PredictionContext}. The hash
	 * code is computed in parts to match the following reference algorithm.
	 *
	 * 
	 *  private int referenceHashCode() {
	 *      int hash = {@link MurmurHash#initialize MurmurHash.initialize}({@link #INITIAL_HASH});
	 *
	 *      for (int i = 0; i < {@link #size()}; i++) {
	 *          hash = {@link MurmurHash#update MurmurHash.update}(hash, {@link #getParent getParent}(i));
	 *      }
	 *
	 *      for (int i = 0; i < {@link #size()}; i++) {
	 *          hash = {@link MurmurHash#update MurmurHash.update}(hash, {@link #getReturnState getReturnState}(i));
	 *      }
	 *
	 *      hash = {@link MurmurHash#finish MurmurHash.finish}(hash, 2 * {@link #size()});
	 *      return hash;
	 *  }
	 * 
*/ private final int cachedHashCode; protected PredictionContext(int cachedHashCode) { this.cachedHashCode = cachedHashCode; } protected static int calculateEmptyHashCode() { int hash = MurmurHash.initialize(INITIAL_HASH); hash = MurmurHash.finish(hash, 0); return hash; } protected static int calculateHashCode(PredictionContext parent, int returnState) { int hash = MurmurHash.initialize(INITIAL_HASH); hash = MurmurHash.update(hash, parent); hash = MurmurHash.update(hash, returnState); hash = MurmurHash.finish(hash, 2); return hash; } protected static int calculateHashCode(PredictionContext[] parents, int[] returnStates) { int hash = MurmurHash.initialize(INITIAL_HASH); for (PredictionContext parent : parents) { hash = MurmurHash.update(hash, parent); } for (int returnState : returnStates) { hash = MurmurHash.update(hash, returnState); } hash = MurmurHash.finish(hash, 2 * parents.length); return hash; } public abstract int size(); public abstract int getReturnState(int index); public abstract int findReturnState(int returnState); @NotNull public abstract PredictionContext getParent(int index); protected abstract PredictionContext addEmptyContext(); protected abstract PredictionContext removeEmptyContext(); public static PredictionContext fromRuleContext(@NotNull ATN atn, @NotNull RuleContext outerContext) { return fromRuleContext(atn, outerContext, true); } public static PredictionContext fromRuleContext(@NotNull ATN atn, @NotNull RuleContext outerContext, boolean fullContext) { if (outerContext.isEmpty()) { return fullContext ? EMPTY_FULL : EMPTY_LOCAL; } PredictionContext parent; if (outerContext.parent != null) { parent = PredictionContext.fromRuleContext(atn, outerContext.parent, fullContext); } else { parent = fullContext ? EMPTY_FULL : EMPTY_LOCAL; } ATNState state = atn.states.get(outerContext.invokingState); RuleTransition transition = (RuleTransition)state.transition(0); return parent.getChild(transition.followState.stateNumber); } private static PredictionContext addEmptyContext(PredictionContext context) { return context.addEmptyContext(); } private static PredictionContext removeEmptyContext(PredictionContext context) { return context.removeEmptyContext(); } public static PredictionContext join(PredictionContext context0, PredictionContext context1) { return join(context0, context1, PredictionContextCache.UNCACHED); } /*package*/ static PredictionContext join(@NotNull final PredictionContext context0, @NotNull final PredictionContext context1, @NotNull PredictionContextCache contextCache) { if (context0 == context1) { return context0; } if (context0.isEmpty()) { return isEmptyLocal(context0) ? context0 : addEmptyContext(context1); } else if (context1.isEmpty()) { return isEmptyLocal(context1) ? context1 : addEmptyContext(context0); } final int context0size = context0.size(); final int context1size = context1.size(); if (context0size == 1 && context1size == 1 && context0.getReturnState(0) == context1.getReturnState(0)) { PredictionContext merged = contextCache.join(context0.getParent(0), context1.getParent(0)); if (merged == context0.getParent(0)) { return context0; } else if (merged == context1.getParent(0)) { return context1; } else { return merged.getChild(context0.getReturnState(0)); } } int count = 0; PredictionContext[] parentsList = new PredictionContext[context0size + context1size]; int[] returnStatesList = new int[parentsList.length]; int leftIndex = 0; int rightIndex = 0; boolean canReturnLeft = true; boolean canReturnRight = true; while (leftIndex < context0size && rightIndex < context1size) { if (context0.getReturnState(leftIndex) == context1.getReturnState(rightIndex)) { parentsList[count] = contextCache.join(context0.getParent(leftIndex), context1.getParent(rightIndex)); returnStatesList[count] = context0.getReturnState(leftIndex); canReturnLeft = canReturnLeft && parentsList[count] == context0.getParent(leftIndex); canReturnRight = canReturnRight && parentsList[count] == context1.getParent(rightIndex); leftIndex++; rightIndex++; } else if (context0.getReturnState(leftIndex) < context1.getReturnState(rightIndex)) { parentsList[count] = context0.getParent(leftIndex); returnStatesList[count] = context0.getReturnState(leftIndex); canReturnRight = false; leftIndex++; } else { assert context1.getReturnState(rightIndex) < context0.getReturnState(leftIndex); parentsList[count] = context1.getParent(rightIndex); returnStatesList[count] = context1.getReturnState(rightIndex); canReturnLeft = false; rightIndex++; } count++; } while (leftIndex < context0size) { parentsList[count] = context0.getParent(leftIndex); returnStatesList[count] = context0.getReturnState(leftIndex); leftIndex++; canReturnRight = false; count++; } while (rightIndex < context1size) { parentsList[count] = context1.getParent(rightIndex); returnStatesList[count] = context1.getReturnState(rightIndex); rightIndex++; canReturnLeft = false; count++; } if (canReturnLeft) { return context0; } else if (canReturnRight) { return context1; } if (count < parentsList.length) { parentsList = Arrays.copyOf(parentsList, count); returnStatesList = Arrays.copyOf(returnStatesList, count); } if (parentsList.length == 0) { // if one of them was EMPTY_LOCAL, it would be empty and handled at the beginning of the method return EMPTY_FULL; } else if (parentsList.length == 1) { return new SingletonPredictionContext(parentsList[0], returnStatesList[0]); } else { return new ArrayPredictionContext(parentsList, returnStatesList); } } public static boolean isEmptyLocal(PredictionContext context) { return context == EMPTY_LOCAL; } public static PredictionContext getCachedContext( @NotNull PredictionContext context, @NotNull ConcurrentMap contextCache, @NotNull PredictionContext.IdentityHashMap visited) { if (context.isEmpty()) { return context; } PredictionContext existing = visited.get(context); if (existing != null) { return existing; } existing = contextCache.get(context); if (existing != null) { visited.put(context, existing); return existing; } boolean changed = false; PredictionContext[] parents = new PredictionContext[context.size()]; for (int i = 0; i < parents.length; i++) { PredictionContext parent = getCachedContext(context.getParent(i), contextCache, visited); if (changed || parent != context.getParent(i)) { if (!changed) { parents = new PredictionContext[context.size()]; for (int j = 0; j < context.size(); j++) { parents[j] = context.getParent(j); } changed = true; } parents[i] = parent; } } if (!changed) { existing = contextCache.putIfAbsent(context, context); visited.put(context, existing != null ? existing : context); return context; } // We know parents.length>0 because context.isEmpty() is checked at the beginning of the method. PredictionContext updated; if (parents.length == 1) { updated = new SingletonPredictionContext(parents[0], context.getReturnState(0)); } else { ArrayPredictionContext arrayPredictionContext = (ArrayPredictionContext)context; updated = new ArrayPredictionContext(parents, arrayPredictionContext.returnStates, context.cachedHashCode); } existing = contextCache.putIfAbsent(updated, updated); visited.put(updated, existing != null ? existing : updated); visited.put(context, existing != null ? existing : updated); return updated; } public PredictionContext appendContext(int returnContext, PredictionContextCache contextCache) { return appendContext(PredictionContext.EMPTY_FULL.getChild(returnContext), contextCache); } public abstract PredictionContext appendContext(PredictionContext suffix, PredictionContextCache contextCache); public PredictionContext getChild(int returnState) { return new SingletonPredictionContext(this, returnState); } public abstract boolean isEmpty(); public abstract boolean hasEmpty(); @Override public final int hashCode() { return cachedHashCode; } @Override public abstract boolean equals(Object o); //@Override //public String toString() { // return toString(null, Integer.MAX_VALUE); //} public String[] toStrings(Recognizer recognizer, int currentState) { return toStrings(recognizer, PredictionContext.EMPTY_FULL, currentState); } public String[] toStrings(Recognizer recognizer, PredictionContext stop, int currentState) { List result = new ArrayList(); outer: for (int perm = 0; ; perm++) { int offset = 0; boolean last = true; PredictionContext p = this; int stateNumber = currentState; StringBuilder localBuffer = new StringBuilder(); localBuffer.append("["); while ( !p.isEmpty() && p != stop ) { int index = 0; if (p.size() > 0) { int bits = 1; while ((1 << bits) < p.size()) { bits++; } int mask = (1 << bits) - 1; index = (perm >> offset) & mask; last &= index >= p.size() - 1; if (index >= p.size()) { continue outer; } offset += bits; } if ( recognizer!=null ) { if (localBuffer.length() > 1) { // first char is '[', if more than that this isn't the first rule localBuffer.append(' '); } ATN atn = recognizer.getATN(); ATNState s = atn.states.get(stateNumber); String ruleName = recognizer.getRuleNames()[s.ruleIndex]; localBuffer.append(ruleName); } else if ( p.getReturnState(index)!=EMPTY_FULL_STATE_KEY ) { if ( !p.isEmpty() ) { if (localBuffer.length() > 1) { // first char is '[', if more than that this isn't the first rule localBuffer.append(' '); } localBuffer.append(p.getReturnState(index)); } } stateNumber = p.getReturnState(index); p = p.getParent(index); } localBuffer.append("]"); result.add(localBuffer.toString()); if (last) { break; } } return result.toArray(new String[result.size()]); } public static final class IdentityHashMap extends FlexibleHashMap { public IdentityHashMap() { super(IdentityEqualityComparator.INSTANCE); } } public static final class IdentityEqualityComparator extends AbstractEqualityComparator { public static final IdentityEqualityComparator INSTANCE = new IdentityEqualityComparator(); private IdentityEqualityComparator() { } @Override public int hashCode(PredictionContext obj) { return obj.hashCode(); } @Override public boolean equals(PredictionContext a, PredictionContext b) { return a == b; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy