diff --git a/runtime/Cpp/runtime/src/atn/ATNConfig.h b/runtime/Cpp/runtime/src/atn/ATNConfig.h index 9b3e750808..1090903ecd 100755 --- a/runtime/Cpp/runtime/src/atn/ATNConfig.h +++ b/runtime/Cpp/runtime/src/atn/ATNConfig.h @@ -89,6 +89,8 @@ namespace atn { ATNConfig(ATNConfig const&) = default; + ATNConfig(ATNConfig&&) = default; + virtual ~ATNConfig() = default; virtual size_t hashCode() const; diff --git a/runtime/Cpp/runtime/src/atn/LL1Analyzer.cpp b/runtime/Cpp/runtime/src/atn/LL1Analyzer.cpp index e7cf22b17f..9f457a7bc5 100755 --- a/runtime/Cpp/runtime/src/atn/LL1Analyzer.cpp +++ b/runtime/Cpp/runtime/src/atn/LL1Analyzer.cpp @@ -22,6 +22,141 @@ using namespace antlr4; using namespace antlr4::atn; using namespace antlrcpp; +namespace { + + struct ATNConfigHasher final { + size_t operator()(const ATNConfig& atn_config) const { + return atn_config.hashCode(); + } + }; + + struct ATNConfigComparer final { + bool operator()(const ATNConfig& lhs, const ATNConfig& rhs) const { + return lhs == rhs; + } + }; + + class LL1AnalyzerImpl final { + public: + LL1AnalyzerImpl(const ATN& atn, misc::IntervalSet& look, bool seeThruPreds, bool addEOF) : _atn(atn), _look(look), _seeThruPreds(seeThruPreds), _addEOF(addEOF) {} + + /// + /// Compute set of tokens that can follow {@code s} in the ATN in the + /// specified {@code ctx}. + ///

+ /// If {@code ctx} is {@code null} and {@code stopState} or the end of the + /// rule containing {@code s} is reached, is added to + /// the result set. If {@code ctx} is not {@code null} and {@code addEOF} is + /// {@code true} and {@code stopState} or the end of the outermost rule is + /// reached, is added to the result set. + ///

+ /// the ATN state. + /// the ATN state to stop at. This can be a + /// to detect epsilon paths through a closure. + /// The outer context, or {@code null} if the outer context should + /// not be used. + /// The result lookahead set. + /// A set used for preventing epsilon closures in the ATN + /// from causing a stack overflow. Outside code should pass + /// {@code new HashSet} for this argument. + /// A set used for preventing left recursion in the + /// ATN from causing a stack overflow. Outside code should pass + /// {@code new BitSet()} for this argument. + /// {@code true} to true semantic predicates as + /// implicitly {@code true} and "see through them", otherwise {@code false} + /// to treat semantic predicates as opaque and add to the + /// result if one is encountered. + /// Add to the result if the end of the + /// outermost context is reached. This parameter has no effect if {@code ctx} + /// is {@code null}. + void LOOK(ATNState *s, ATNState *stopState, Ref const& ctx) { + if (!_lookBusy.insert(ATNConfig(s, 0, ctx)).second) { + return; + } + + // ml: s can never be null, hence no need to check if stopState is != null. + if (s == stopState) { + if (ctx == nullptr) { + _look.add(Token::EPSILON); + return; + } else if (ctx->isEmpty() && _addEOF) { + _look.add(Token::EOF); + return; + } + } + + if (s->getStateType() == ATNState::RULE_STOP) { + if (ctx == nullptr) { + _look.add(Token::EPSILON); + return; + } else if (ctx->isEmpty() && _addEOF) { + _look.add(Token::EOF); + return; + } + + if (ctx != PredictionContext::EMPTY) { + bool removed = _calledRuleStack.test(s->ruleIndex); + _calledRuleStack[s->ruleIndex] = false; + // run thru all possible stack tops in ctx + for (size_t i = 0; i < ctx->size(); i++) { + ATNState *returnState = _atn.states[ctx->getReturnState(i)]; + LOOK(returnState, stopState, ctx->getParent(i)); + } + if (removed) { + _calledRuleStack.set(s->ruleIndex); + } + return; + } + } + + size_t n = s->transitions.size(); + for (size_t i = 0; i < n; i++) { + Transition *t = s->transitions[i]; + + if (t->getSerializationType() == Transition::RULE) { + if (_calledRuleStack[(static_cast(t))->target->ruleIndex]) { + continue; + } + + Ref newContext = SingletonPredictionContext::create(ctx, (static_cast(t))->followState->stateNumber); + + _calledRuleStack.set((static_cast(t))->target->ruleIndex); + LOOK(t->target, stopState, newContext); + _calledRuleStack[(static_cast(t))->target->ruleIndex] = false; + + } else if (is(t)) { + if (_seeThruPreds) { + LOOK(t->target, stopState, ctx); + } else { + _look.add(LL1Analyzer::HIT_PRED); + } + } else if (t->isEpsilon()) { + LOOK(t->target, stopState, ctx); + } else if (t->getSerializationType() == Transition::WILDCARD) { + _look.addAll(misc::IntervalSet::of(Token::MIN_USER_TOKEN_TYPE, static_cast(_atn.maxTokenType))); + } else { + misc::IntervalSet set = t->label(); + if (!set.isEmpty()) { + if (is(t)) { + set = set.complement(misc::IntervalSet::of(Token::MIN_USER_TOKEN_TYPE, static_cast(_atn.maxTokenType))); + } + _look.addAll(set); + } + } + } + } + + private: + const ATN& _atn; + misc::IntervalSet& _look; + antlrcpp::BitSet _calledRuleStack; + std::unordered_set _lookBusy; + bool _seeThruPreds; + bool _addEOF; + }; + +} + std::vector LL1Analyzer::getDecisionLookahead(ATNState *s) const { std::vector look; @@ -31,16 +166,11 @@ std::vector LL1Analyzer::getDecisionLookahead(ATNState *s) co look.resize(s->transitions.size()); // Fills all interval sets with defaults. for (size_t alt = 0; alt < s->transitions.size(); alt++) { - bool seeThruPreds = false; // fail to get lookahead upon pred - - ATNConfig::Set lookBusy; - antlrcpp::BitSet callRuleStack; - LOOK(s->transitions[alt]->target, nullptr, PredictionContext::EMPTY, - look[alt], lookBusy, callRuleStack, seeThruPreds, false); - + LL1AnalyzerImpl impl(_atn, look[alt], false, false); + impl.LOOK(s->transitions[alt]->target, nullptr, PredictionContext::EMPTY); // Wipe out lookahead for this alternative if we found nothing // or we had a predicate when we !seeThruPreds - if (look[alt].size() == 0 || look[alt].contains(HIT_PRED)) { + if (look[alt].size() == 0 || look[alt].contains(LL1Analyzer::HIT_PRED)) { look[alt].clear(); } } @@ -52,99 +182,9 @@ misc::IntervalSet LL1Analyzer::LOOK(ATNState *s, RuleContext *ctx) const { } misc::IntervalSet LL1Analyzer::LOOK(ATNState *s, ATNState *stopState, RuleContext *ctx) const { - misc::IntervalSet r; - bool seeThruPreds = true; // ignore preds; get all lookahead Ref lookContext = ctx != nullptr ? PredictionContext::fromRuleContext(_atn, ctx) : nullptr; - - ATNConfig::Set lookBusy; - antlrcpp::BitSet callRuleStack; - LOOK(s, stopState, lookContext, r, lookBusy, callRuleStack, seeThruPreds, true); - + misc::IntervalSet r; + LL1AnalyzerImpl impl(_atn, r, true, true); + impl.LOOK(s, stopState, lookContext); return r; } - -void LL1Analyzer::LOOK(ATNState *s, ATNState *stopState, Ref const& ctx, misc::IntervalSet &look, - ATNConfig::Set &lookBusy, antlrcpp::BitSet &calledRuleStack, bool seeThruPreds, bool addEOF) const { - - Ref c = std::make_shared(s, 0, ctx); - - if (lookBusy.count(c) > 0) // Keep in mind comparison is based on members of the class, not the actual instance. - return; - - lookBusy.insert(c); - - // ml: s can never be null, hence no need to check if stopState is != null. - if (s == stopState) { - if (ctx == nullptr) { - look.add(Token::EPSILON); - return; - } else if (ctx->isEmpty() && addEOF) { - look.add(Token::EOF); - return; - } - } - - if (s->getStateType() == ATNState::RULE_STOP) { - if (ctx == nullptr) { - look.add(Token::EPSILON); - return; - } else if (ctx->isEmpty() && addEOF) { - look.add(Token::EOF); - return; - } - - if (ctx != PredictionContext::EMPTY) { - bool removed = calledRuleStack.test(s->ruleIndex); - calledRuleStack[s->ruleIndex] = false; - auto onExit = finally([removed, &calledRuleStack, s] { - if (removed) { - calledRuleStack.set(s->ruleIndex); - } - }); - // run thru all possible stack tops in ctx - for (size_t i = 0; i < ctx->size(); i++) { - ATNState *returnState = _atn.states[ctx->getReturnState(i)]; - LOOK(returnState, stopState, ctx->getParent(i), look, lookBusy, calledRuleStack, seeThruPreds, addEOF); - } - return; - } - } - - size_t n = s->transitions.size(); - for (size_t i = 0; i < n; i++) { - Transition *t = s->transitions[i]; - - if (t->getSerializationType() == Transition::RULE) { - if (calledRuleStack[(static_cast(t))->target->ruleIndex]) { - continue; - } - - Ref newContext = SingletonPredictionContext::create(ctx, (static_cast(t))->followState->stateNumber); - auto onExit = finally([t, &calledRuleStack] { - calledRuleStack[(static_cast(t))->target->ruleIndex] = false; - }); - - calledRuleStack.set((static_cast(t))->target->ruleIndex); - LOOK(t->target, stopState, newContext, look, lookBusy, calledRuleStack, seeThruPreds, addEOF); - - } else if (is(t)) { - if (seeThruPreds) { - LOOK(t->target, stopState, ctx, look, lookBusy, calledRuleStack, seeThruPreds, addEOF); - } else { - look.add(HIT_PRED); - } - } else if (t->isEpsilon()) { - LOOK(t->target, stopState, ctx, look, lookBusy, calledRuleStack, seeThruPreds, addEOF); - } else if (t->getSerializationType() == Transition::WILDCARD) { - look.addAll(misc::IntervalSet::of(Token::MIN_USER_TOKEN_TYPE, static_cast(_atn.maxTokenType))); - } else { - misc::IntervalSet set = t->label(); - if (!set.isEmpty()) { - if (is(t)) { - set = set.complement(misc::IntervalSet::of(Token::MIN_USER_TOKEN_TYPE, static_cast(_atn.maxTokenType))); - } - look.addAll(set); - } - } - } -} diff --git a/runtime/Cpp/runtime/src/atn/LL1Analyzer.h b/runtime/Cpp/runtime/src/atn/LL1Analyzer.h index 071e01e918..cf17501d24 100755 --- a/runtime/Cpp/runtime/src/atn/LL1Analyzer.h +++ b/runtime/Cpp/runtime/src/atn/LL1Analyzer.h @@ -68,40 +68,7 @@ namespace atn { /// specified {@code ctx}. misc::IntervalSet LOOK(ATNState *s, ATNState *stopState, RuleContext *ctx) const; - /// - /// Compute set of tokens that can follow {@code s} in the ATN in the - /// specified {@code ctx}. - ///

- /// If {@code ctx} is {@code null} and {@code stopState} or the end of the - /// rule containing {@code s} is reached, is added to - /// the result set. If {@code ctx} is not {@code null} and {@code addEOF} is - /// {@code true} and {@code stopState} or the end of the outermost rule is - /// reached, is added to the result set. - ///

- /// the ATN state. - /// the ATN state to stop at. This can be a - /// to detect epsilon paths through a closure. - /// The outer context, or {@code null} if the outer context should - /// not be used. - /// The result lookahead set. - /// A set used for preventing epsilon closures in the ATN - /// from causing a stack overflow. Outside code should pass - /// {@code new HashSet} for this argument. - /// A set used for preventing left recursion in the - /// ATN from causing a stack overflow. Outside code should pass - /// {@code new BitSet()} for this argument. - /// {@code true} to true semantic predicates as - /// implicitly {@code true} and "see through them", otherwise {@code false} - /// to treat semantic predicates as opaque and add to the - /// result if one is encountered. - /// Add to the result if the end of the - /// outermost context is reached. This parameter has no effect if {@code ctx} - /// is {@code null}. private: - void LOOK(ATNState *s, ATNState *stopState, Ref const &ctx, - misc::IntervalSet &look, ATNConfig::Set &lookBusy, antlrcpp::BitSet &calledRuleStack, - bool seeThruPreds, bool addEOF) const; - const atn::ATN &_atn; };