org.antlr.v4.runtime.atn.ATNConfigSet.java Source code

Java tutorial

Introduction

Here is the source code for org.antlr.v4.runtime.atn.ATNConfigSet.java

Source

/*
 * 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.misc.AbstractEqualityComparator;
import org.antlr.v4.runtime.misc.Array2DHashSet;
import org.antlr.v4.runtime.misc.DoubleKeyMap;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * Specialized {@link Set}{@code <}{@link ATNConfig}{@code >} that can track
 * info about the set, with support for combining similar configurations using a
 * graph-structured stack.
 */
public class ATNConfigSet implements Set<ATNConfig> {
    /**
     * The reason that we need this is because we don't want the hash map to use
     * the standard hash code and equals. We need all configurations with the same
     * {@code (s,i,_,semctx)} to be equal. Unfortunately, this key effectively doubles
     * the number of objects associated with ATNConfigs. The other solution is to
     * use a hash table that lets us specify the equals/hashcode operation.
     */
    public static class ConfigHashSet extends AbstractConfigHashSet {
        public ConfigHashSet() {
            super(ConfigEqualityComparator.INSTANCE);
        }
    }

    public static final class ConfigEqualityComparator extends AbstractEqualityComparator<ATNConfig> {
        public static final ConfigEqualityComparator INSTANCE = new ConfigEqualityComparator();

        private ConfigEqualityComparator() {
        }

        @Override
        public int hashCode(ATNConfig o) {
            int hashCode = 7;
            hashCode = 31 * hashCode + o.state.stateNumber;
            hashCode = 31 * hashCode + o.alt;
            hashCode = 31 * hashCode + o.semanticContext.hashCode();
            return hashCode;
        }

        @Override
        public boolean equals(ATNConfig a, ATNConfig b) {
            if (a == b)
                return true;
            if (a == null || b == null)
                return false;
            return a.state.stateNumber == b.state.stateNumber && a.alt == b.alt
                    && a.semanticContext.equals(b.semanticContext);
        }
    }

    /** Indicates that the set of configurations is read-only. Do not
     *  allow any code to manipulate the set; DFA states will point at
     *  the sets and they must not change. This does not protect the other
     *  fields; in particular, conflictingAlts is set after
     *  we've made this readonly.
      */
    protected boolean readonly = false;

    /**
     * All configs but hashed by (s, i, _, pi) not including context. Wiped out
     * when we go readonly as this set becomes a DFA state.
     */
    public AbstractConfigHashSet configLookup;

    /** Track the elements as they are added to the set; supports get(i) */
    public final ArrayList<ATNConfig> configs = new ArrayList<ATNConfig>(7);

    // TODO: these fields make me pretty uncomfortable but nice to pack up info together, saves recomputation
    // TODO: can we track conflicts as they are added to save scanning configs later?
    public int uniqueAlt;
    /** Currently this is only used when we detect SLL conflict; this does
     *  not necessarily represent the ambiguous alternatives. In fact,
     *  I should also point out that this seems to include predicated alternatives
     *  that have predicates that evaluate to false. Computed in computeTargetState().
      */
    protected BitSet conflictingAlts;

    // Used in parser and lexer. In lexer, it indicates we hit a pred
    // while computing a closure operation.  Don't make a DFA state from this.
    public boolean hasSemanticContext;
    public boolean dipsIntoOuterContext;

    /** Indicates that this configuration set is part of a full context
     *  LL prediction. It will be used to determine how to merge $. With SLL
     *  it's a wildcard whereas it is not for LL context merge.
     */
    public final boolean fullCtx;

    private int cachedHashCode = -1;

    public ATNConfigSet(boolean fullCtx) {
        configLookup = new ConfigHashSet();
        this.fullCtx = fullCtx;
    }

    public ATNConfigSet() {
        this(true);
    }

    public ATNConfigSet(ATNConfigSet old) {
        this(old.fullCtx);
        addAll(old);
        this.uniqueAlt = old.uniqueAlt;
        this.conflictingAlts = old.conflictingAlts;
        this.hasSemanticContext = old.hasSemanticContext;
        this.dipsIntoOuterContext = old.dipsIntoOuterContext;
    }

    @Override
    public boolean add(ATNConfig config) {
        return add(config, null);
    }

    /**
     * Adding a new config means merging contexts with existing configs for
     * {@code (s, i, pi, _)}, where {@code s} is the
     * {@link ATNConfig#state}, {@code i} is the {@link ATNConfig#alt}, and
     * {@code pi} is the {@link ATNConfig#semanticContext}. We use
     * {@code (s,i,pi)} as key.
     *
     * <p>This method updates {@link #dipsIntoOuterContext} and
     * {@link #hasSemanticContext} when necessary.</p>
     */
    public boolean add(ATNConfig config,
            DoubleKeyMap<PredictionContext, PredictionContext, PredictionContext> mergeCache) {
        if (readonly)
            throw new IllegalStateException("This set is readonly");
        if (config.semanticContext != SemanticContext.NONE) {
            hasSemanticContext = true;
        }
        if (config.getOuterContextDepth() > 0) {
            dipsIntoOuterContext = true;
        }
        ATNConfig existing = configLookup.getOrAdd(config);
        if (existing == config) { // we added this new one
            cachedHashCode = -1;
            configs.add(config); // track order here
            return true;
        }
        // a previous (s,i,pi,_), merge with it and save result
        boolean rootIsWildcard = !fullCtx;
        PredictionContext merged = PredictionContext.merge(existing.context, config.context, rootIsWildcard,
                mergeCache);
        // no need to check for existing.context, config.context in cache
        // since only way to create new graphs is "call rule" and here. We
        // cache at both places.
        existing.reachesIntoOuterContext = Math.max(existing.reachesIntoOuterContext,
                config.reachesIntoOuterContext);

        // make sure to preserve the precedence filter suppression during the merge
        if (config.isPrecedenceFilterSuppressed()) {
            existing.setPrecedenceFilterSuppressed(true);
        }

        existing.context = merged; // replace context; no need to alt mapping
        return true;
    }

    /** Return a List holding list of configs */
    public List<ATNConfig> elements() {
        return configs;
    }

    public Set<ATNState> getStates() {
        Set<ATNState> states = new HashSet<ATNState>();
        for (ATNConfig c : configs) {
            states.add(c.state);
        }
        return states;
    }

    /**
     * Gets the complete set of represented alternatives for the configuration
     * set.
     *
     * @return the set of represented alternatives in this configuration set
     *
     * @since 4.3
     */

    public BitSet getAlts() {
        BitSet alts = new BitSet();
        for (ATNConfig config : configs) {
            alts.set(config.alt);
        }
        return alts;
    }

    public List<SemanticContext> getPredicates() {
        List<SemanticContext> preds = new ArrayList<SemanticContext>();
        for (ATNConfig c : configs) {
            if (c.semanticContext != SemanticContext.NONE) {
                preds.add(c.semanticContext);
            }
        }
        return preds;
    }

    public ATNConfig get(int i) {
        return configs.get(i);
    }

    public void optimizeConfigs(ATNSimulator interpreter) {
        if (readonly)
            throw new IllegalStateException("This set is readonly");
        if (configLookup.isEmpty())
            return;

        for (ATNConfig config : configs) {
            //         int before = PredictionContext.getAllContextNodes(config.context).size();
            config.context = interpreter.getCachedContext(config.context);
            //         int after = PredictionContext.getAllContextNodes(config.context).size();
            //         System.out.println("configs "+before+"->"+after);
        }
    }

    @Override
    public boolean addAll(Collection<? extends ATNConfig> coll) {
        for (ATNConfig c : coll)
            add(c);
        return false;
    }

    @Override
    public boolean equals(Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof ATNConfigSet)) {
            return false;
        }

        //      System.out.print("equals " + this + ", " + o+" = ");
        ATNConfigSet other = (ATNConfigSet) o;
        boolean same = configs != null && configs.equals(other.configs) && // includes stack context
                this.fullCtx == other.fullCtx && this.uniqueAlt == other.uniqueAlt
                && this.conflictingAlts == other.conflictingAlts
                && this.hasSemanticContext == other.hasSemanticContext
                && this.dipsIntoOuterContext == other.dipsIntoOuterContext;

        //      System.out.println(same);
        return same;
    }

    @Override
    public int hashCode() {
        if (isReadonly()) {
            if (cachedHashCode == -1) {
                cachedHashCode = configs.hashCode();
            }

            return cachedHashCode;
        }

        return configs.hashCode();
    }

    @Override
    public int size() {
        return configs.size();
    }

    @Override
    public boolean isEmpty() {
        return configs.isEmpty();
    }

    @Override
    public boolean contains(Object o) {
        if (configLookup == null) {
            throw new UnsupportedOperationException("This method is not implemented for readonly sets.");
        }

        return configLookup.contains(o);
    }

    public boolean containsFast(ATNConfig obj) {
        if (configLookup == null) {
            throw new UnsupportedOperationException("This method is not implemented for readonly sets.");
        }

        return configLookup.containsFast(obj);
    }

    @Override
    public Iterator<ATNConfig> iterator() {
        return configs.iterator();
    }

    @Override
    public void clear() {
        if (readonly)
            throw new IllegalStateException("This set is readonly");
        configs.clear();
        cachedHashCode = -1;
        configLookup.clear();
    }

    public boolean isReadonly() {
        return readonly;
    }

    public void setReadonly(boolean readonly) {
        this.readonly = readonly;
        configLookup = null; // can't mod, no need for lookup cache
    }

    @Override
    public String toString() {
        StringBuilder buf = new StringBuilder();
        buf.append(elements().toString());
        if (hasSemanticContext)
            buf.append(",hasSemanticContext=").append(hasSemanticContext);
        if (uniqueAlt != ATN.INVALID_ALT_NUMBER)
            buf.append(",uniqueAlt=").append(uniqueAlt);
        if (conflictingAlts != null)
            buf.append(",conflictingAlts=").append(conflictingAlts);
        if (dipsIntoOuterContext)
            buf.append(",dipsIntoOuterContext");
        return buf.toString();
    }

    // satisfy interface

    @Override
    public ATNConfig[] toArray() {
        return configLookup.toArray();
    }

    @Override
    public <T> T[] toArray(T[] a) {
        return configLookup.toArray(a);
    }

    @Override
    public boolean remove(Object o) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean containsAll(Collection<?> c) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        throw new UnsupportedOperationException();
    }

    public static abstract class AbstractConfigHashSet extends Array2DHashSet<ATNConfig> {

        public AbstractConfigHashSet(AbstractEqualityComparator<? super ATNConfig> comparator) {
            this(comparator, 16, 2);
        }

        public AbstractConfigHashSet(AbstractEqualityComparator<? super ATNConfig> comparator, int initialCapacity,
                int initialBucketCapacity) {
            super(comparator, initialCapacity, initialBucketCapacity);
        }

        @Override
        protected final ATNConfig asElementType(Object o) {
            if (!(o instanceof ATNConfig)) {
                return null;
            }

            return (ATNConfig) o;
        }

        @Override
        protected final ATNConfig[][] createBuckets(int capacity) {
            return new ATNConfig[capacity][];
        }

        @Override
        protected final ATNConfig[] createBucket(int capacity) {
            return new ATNConfig[capacity];
        }

    }
}