| /* 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. |
| */ |
| |
| #include "atn/PredictionContext.h" |
| #include "atn/ATNConfig.h" |
| #include "atn/ATNSimulator.h" |
| #include "Exceptions.h" |
| #include "atn/SemanticContext.h" |
| #include "support/Arrays.h" |
| |
| #include "atn/ATNConfigSet.h" |
| |
| using namespace antlr4::atn; |
| using namespace antlrcpp; |
| |
| ATNConfigSet::ATNConfigSet(bool fullCtx) : fullCtx(fullCtx) { |
| InitializeInstanceFields(); |
| } |
| |
| ATNConfigSet::ATNConfigSet(const Ref<ATNConfigSet> &old) : ATNConfigSet(old->fullCtx) { |
| addAll(old); |
| uniqueAlt = old->uniqueAlt; |
| conflictingAlts = old->conflictingAlts; |
| hasSemanticContext = old->hasSemanticContext; |
| dipsIntoOuterContext = old->dipsIntoOuterContext; |
| } |
| |
| ATNConfigSet::~ATNConfigSet() { |
| } |
| |
| bool ATNConfigSet::add(const Ref<ATNConfig> &config) { |
| return add(config, nullptr); |
| } |
| |
| bool ATNConfigSet::add(const Ref<ATNConfig> &config, PredictionContextMergeCache *mergeCache) { |
| if (_readonly) { |
| throw IllegalStateException("This set is readonly"); |
| } |
| if (config->semanticContext != SemanticContext::NONE) { |
| hasSemanticContext = true; |
| } |
| if (config->getOuterContextDepth() > 0) { |
| dipsIntoOuterContext = true; |
| } |
| |
| size_t hash = getHash(config.get()); |
| ATNConfig *existing = _configLookup[hash]; |
| if (existing == nullptr) { |
| _configLookup[hash] = config.get(); |
| _cachedHashCode = 0; |
| configs.push_back(config); // track order here |
| |
| return true; |
| } |
| |
| // a previous (s,i,pi,_), merge with it and save result |
| bool rootIsWildcard = !fullCtx; |
| Ref<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 = std::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; |
| } |
| |
| bool ATNConfigSet::addAll(const Ref<ATNConfigSet> &other) { |
| for (auto &c : other->configs) { |
| add(c); |
| } |
| return false; |
| } |
| |
| std::vector<ATNState*> ATNConfigSet::getStates() { |
| std::vector<ATNState*> states; |
| for (auto c : configs) { |
| states.push_back(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 |
| */ |
| |
| BitSet ATNConfigSet::getAlts() { |
| BitSet alts; |
| for (ATNConfig config : configs) { |
| alts.set(config.alt); |
| } |
| return alts; |
| } |
| |
| std::vector<Ref<SemanticContext>> ATNConfigSet::getPredicates() { |
| std::vector<Ref<SemanticContext>> preds; |
| for (auto c : configs) { |
| if (c->semanticContext != SemanticContext::NONE) { |
| preds.push_back(c->semanticContext); |
| } |
| } |
| return preds; |
| } |
| |
| Ref<ATNConfig> ATNConfigSet::get(size_t i) const { |
| return configs[i]; |
| } |
| |
| void ATNConfigSet::optimizeConfigs(ATNSimulator *interpreter) { |
| if (_readonly) { |
| throw IllegalStateException("This set is readonly"); |
| } |
| if (_configLookup.empty()) |
| return; |
| |
| for (auto &config : configs) { |
| config->context = interpreter->getCachedContext(config->context); |
| } |
| } |
| |
| bool ATNConfigSet::operator == (const ATNConfigSet &other) { |
| if (&other == this) { |
| return true; |
| } |
| |
| if (configs.size() != other.configs.size()) |
| return false; |
| |
| if (fullCtx != other.fullCtx || uniqueAlt != other.uniqueAlt || |
| conflictingAlts != other.conflictingAlts || hasSemanticContext != other.hasSemanticContext || |
| dipsIntoOuterContext != other.dipsIntoOuterContext) // includes stack context |
| return false; |
| |
| return Arrays::equals(configs, other.configs); |
| } |
| |
| size_t ATNConfigSet::hashCode() { |
| if (!isReadonly() || _cachedHashCode == 0) { |
| _cachedHashCode = 1; |
| for (auto &i : configs) { |
| _cachedHashCode = 31 * _cachedHashCode + i->hashCode(); // Same as Java's list hashCode impl. |
| } |
| } |
| |
| return _cachedHashCode; |
| } |
| |
| size_t ATNConfigSet::size() { |
| return configs.size(); |
| } |
| |
| bool ATNConfigSet::isEmpty() { |
| return configs.empty(); |
| } |
| |
| void ATNConfigSet::clear() { |
| if (_readonly) { |
| throw IllegalStateException("This set is readonly"); |
| } |
| configs.clear(); |
| _cachedHashCode = 0; |
| _configLookup.clear(); |
| } |
| |
| bool ATNConfigSet::isReadonly() { |
| return _readonly; |
| } |
| |
| void ATNConfigSet::setReadonly(bool readonly) { |
| _readonly = readonly; |
| _configLookup.clear(); |
| } |
| |
| std::string ATNConfigSet::toString() { |
| std::stringstream ss; |
| ss << "["; |
| for (size_t i = 0; i < configs.size(); i++) { |
| ss << configs[i]->toString(); |
| } |
| ss << "]"; |
| |
| if (hasSemanticContext) { |
| ss << ",hasSemanticContext = " << hasSemanticContext; |
| } |
| if (uniqueAlt != ATN::INVALID_ALT_NUMBER) { |
| ss << ",uniqueAlt = " << uniqueAlt; |
| } |
| |
| if (conflictingAlts.size() > 0) { |
| ss << ",conflictingAlts = "; |
| ss << conflictingAlts.toString(); |
| } |
| |
| if (dipsIntoOuterContext) { |
| ss << ", dipsIntoOuterContext"; |
| } |
| return ss.str(); |
| } |
| |
| size_t ATNConfigSet::getHash(ATNConfig *c) { |
| size_t hashCode = 7; |
| hashCode = 31 * hashCode + c->state->stateNumber; |
| hashCode = 31 * hashCode + c->alt; |
| hashCode = 31 * hashCode + c->semanticContext->hashCode(); |
| return hashCode; |
| } |
| |
| void ATNConfigSet::InitializeInstanceFields() { |
| uniqueAlt = 0; |
| hasSemanticContext = false; |
| dipsIntoOuterContext = false; |
| |
| _readonly = false; |
| _cachedHashCode = 0; |
| } |