From 90ef7a57d5cf2d5cdb0537294b18f9ba7ee810c2 Mon Sep 17 00:00:00 2001 From: Edoardo Luppi Date: Tue, 5 Dec 2023 13:43:13 +0100 Subject: [PATCH] refactor: fix potential deadlocks in Java runtime (antlr4#3752) See https://github.com/antlr/antlr4/pull/3752 Commit 875911c2b2e6e50317e75ff2b5e80f4014a75954 --- .../v4/kotlinruntime/ParserRuleContext.kt | 6 +++ .../org/antlr/v4/kotlinruntime/RuleContext.kt | 6 +-- .../antlr/v4/kotlinruntime/atn/ATNConfig.kt | 6 +-- .../v4/kotlinruntime/atn/ATNConfigSet.kt | 4 +- .../atn/EmptyPredictionContext.kt | 10 +++++ .../antlr/v4/kotlinruntime/atn/LL1Analyzer.kt | 4 +- .../v4/kotlinruntime/atn/LexerATNConfig.kt | 4 +- .../v4/kotlinruntime/atn/LexerATNSimulator.kt | 4 +- .../kotlinruntime/atn/ParserATNSimulator.kt | 16 +++---- .../v4/kotlinruntime/atn/PredictionContext.kt | 44 ++++++++---------- .../atn/PredictionContextCache.kt | 2 +- .../v4/kotlinruntime/atn/PredictionMode.kt | 2 +- .../v4/kotlinruntime/atn/SemanticContext.kt | 45 +++++++++++-------- .../atn/SingletonPredictionContext.kt | 2 +- 14 files changed, 84 insertions(+), 71 deletions(-) diff --git a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/ParserRuleContext.kt b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/ParserRuleContext.kt index e779e260..e79d05cb 100644 --- a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/ParserRuleContext.kt +++ b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/ParserRuleContext.kt @@ -8,6 +8,7 @@ package org.antlr.v4.kotlinruntime import org.antlr.v4.kotlinruntime.ast.Position import org.antlr.v4.kotlinruntime.misc.Interval import org.antlr.v4.kotlinruntime.tree.* +import kotlin.jvm.JvmStatic import kotlin.reflect.KClass // @@ -34,6 +35,11 @@ import kotlin.reflect.KClass // * satisfy the superclass interface. // */ open class ParserRuleContext : RuleContext { + companion object { + @JvmStatic + val EMPTY: ParserRuleContext = ParserRuleContext() + } + // override fun setParent(parent: RuleContext) { // TODO("not implemented") //To change body of created functions use File | Settings | File Templates. // } diff --git a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/RuleContext.kt b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/RuleContext.kt index 07e82d98..7126e53f 100644 --- a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/RuleContext.kt +++ b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/RuleContext.kt @@ -62,10 +62,6 @@ import org.antlr.v4.kotlinruntime.tree.Trees * * @see ParserRuleContext */ - -val EMPTY_RULECTX = ParserRuleContext() - - open class RuleContext : RuleNode { // override fun setParent(parent: RuleContext) { // TODO("not implemented") //To change body of created functions use File | Settings | File Templates. @@ -212,7 +208,7 @@ open class RuleContext : RuleNode { } // recog null unless ParserRuleContext, in which case we use subclass toString(...) - fun toString(recog: Recognizer<*, *>?, stop: RuleContext = EMPTY_RULECTX): String { + fun toString(recog: Recognizer<*, *>?, stop: RuleContext = ParserRuleContext.EMPTY): String { val ruleNames = recog?.ruleNames val ruleNamesList = if (ruleNames != null) listOf(*ruleNames) else null return toString(ruleNamesList, stop) diff --git a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/ATNConfig.kt b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/ATNConfig.kt index 9563cd5e..83fa3a28 100644 --- a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/ATNConfig.kt +++ b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/ATNConfig.kt @@ -87,7 +87,7 @@ open class ATNConfig { constructor(state: ATNState, alt: Int, context: PredictionContext?, - semanticContext: SemanticContext = SemanticContext.NONE) { + semanticContext: SemanticContext = SemanticContext.Empty.Instance) { this.state = state this.alt = alt this.context = context @@ -132,7 +132,7 @@ open class ATNConfig { return (this.state.stateNumber == other.state.stateNumber && this.alt == other.alt - && (this.context === other.context || (this.context != null && this.context == other.context)) + && this.context == other.context && this.semanticContext == other.semanticContext && this.isPrecedenceFilterSuppressed == other.isPrecedenceFilterSuppressed) } @@ -168,7 +168,7 @@ open class ATNConfig { buf.append(context!!.toString()) buf.append("]") } - if (semanticContext != null && semanticContext !== SemanticContext.NONE) { + if (semanticContext != null && semanticContext !== SemanticContext.Empty.Instance) { buf.append(",") buf.append(semanticContext) } diff --git a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/ATNConfigSet.kt b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/ATNConfigSet.kt index 9d52bee2..5c1f8e68 100755 --- a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/ATNConfigSet.kt +++ b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/ATNConfigSet.kt @@ -89,7 +89,7 @@ open class ATNConfigSet constructor( get() { val preds = ArrayList() for (c in configs) { - if (c.semanticContext !== SemanticContext.NONE) { + if (c.semanticContext !== SemanticContext.Empty.Instance) { preds.add(c.semanticContext!!) } } @@ -178,7 +178,7 @@ open class ATNConfigSet constructor( config: ATNConfig, mergeCache: DoubleKeyMap?): Boolean { if (isReadonly) throw IllegalStateException("This set is readonly") - if (config.semanticContext !== SemanticContext.NONE) { + if (config.semanticContext !== SemanticContext.Empty.Instance) { hasSemanticContext = true } if (config.outerContextDepth > 0) { diff --git a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/EmptyPredictionContext.kt b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/EmptyPredictionContext.kt index 574c6da2..89d7a7f7 100644 --- a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/EmptyPredictionContext.kt +++ b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/EmptyPredictionContext.kt @@ -6,7 +6,17 @@ package org.antlr.v4.kotlinruntime.atn +import kotlin.jvm.JvmStatic + class EmptyPredictionContext : SingletonPredictionContext(null, PredictionContext.EMPTY_RETURN_STATE) { + companion object { + /** + * Represents `$` in local context prediction, which means wildcard. + * `*+x = *`. + */ + @JvmStatic + val Instance: EmptyPredictionContext = EmptyPredictionContext() + } override val isEmpty: Boolean get() = true diff --git a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/LL1Analyzer.kt b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/LL1Analyzer.kt index ba832b69..69e26fd1 100644 --- a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/LL1Analyzer.kt +++ b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/LL1Analyzer.kt @@ -34,7 +34,7 @@ class LL1Analyzer(val atn: ATN) { look[alt] = IntervalSet() val lookBusy = HashSet() val seeThruPreds = false // fail to get lookahead upon pred - _LOOK(s!!.transition(alt).target!!, null, PredictionContext.EMPTY, + _LOOK(s!!.transition(alt).target!!, null, EmptyPredictionContext.Instance, look[alt]!!, lookBusy, BitSet(), seeThruPreds, false) // Wipe out lookahead for this alternative if we found nothing // or we had a predicate when we !seeThruPreds @@ -156,7 +156,7 @@ class LL1Analyzer(val atn: ATN) { return } - if (ctx !== PredictionContext.EMPTY) { + if (ctx !== EmptyPredictionContext.Instance) { // run thru all possible stack tops in ctx val removed = calledRuleStack.get(s.ruleIndex) try { diff --git a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/LexerATNConfig.kt b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/LexerATNConfig.kt index c3c49b2c..0ebe8c2f 100644 --- a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/LexerATNConfig.kt +++ b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/LexerATNConfig.kt @@ -23,7 +23,7 @@ class LexerATNConfig : ATNConfig { constructor(state: ATNState, alt: Int, - context: PredictionContext) : super(state, alt, context, SemanticContext.NONE) { + context: PredictionContext) : super(state, alt, context, SemanticContext.Empty.Instance) { this.passedThroughNonGreedyDecision = false this.lexerActionExecutor = null } @@ -31,7 +31,7 @@ class LexerATNConfig : ATNConfig { constructor(state: ATNState, alt: Int, context: PredictionContext, - lexerActionExecutor: LexerActionExecutor) : super(state, alt, context, SemanticContext.NONE) { + lexerActionExecutor: LexerActionExecutor) : super(state, alt, context, SemanticContext.Empty.Instance) { this.lexerActionExecutor = lexerActionExecutor this.passedThroughNonGreedyDecision = false } diff --git a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/LexerATNSimulator.kt b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/LexerATNSimulator.kt index c76816ea..c1805478 100644 --- a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/LexerATNSimulator.kt +++ b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/LexerATNSimulator.kt @@ -350,7 +350,7 @@ class LexerATNSimulator(protected val recog: Lexer?, atn: ATN, protected fun computeStartState(input: CharStream, p: ATNState): ATNConfigSet { - val initialContext = PredictionContext.EMPTY + val initialContext = EmptyPredictionContext.Instance val configs = OrderedATNConfigSet() for (i in 0 until p.numberOfTransitions) { val target = p.transition(i).target @@ -390,7 +390,7 @@ class LexerATNSimulator(protected val recog: Lexer?, atn: ATN, configs.add(config) return true } else { - configs.add(LexerATNConfig(config, config.state, PredictionContext.EMPTY)) + configs.add(LexerATNConfig(config, config.state, EmptyPredictionContext.Instance)) currentAltReachedAcceptState = true } } diff --git a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/ParserATNSimulator.kt b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/ParserATNSimulator.kt index 89010a00..1a1bb4d6 100755 --- a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/ParserATNSimulator.kt +++ b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/ParserATNSimulator.kt @@ -350,7 +350,7 @@ open class ParserATNSimulator( } if (s0 == null) { - if (outerContext == null) outerContext = EMPTY_RULECTX + if (outerContext == null) outerContext = ParserRuleContext.EMPTY if (debug || debug_list_atn_decisions) { System.out.println("predictATN decision " + dfa.decision + " exec LA(1)==" + getLookaheadName(input) + @@ -359,7 +359,7 @@ open class ParserATNSimulator( val fullCtx = false var s0_closure = computeStartState(dfa.atnStartState, - EMPTY_RULECTX, + ParserRuleContext.EMPTY, fullCtx) if (dfa.isPrecedenceDfa) { @@ -1176,8 +1176,8 @@ open class ParserATNSimulator( var nPredAlts = 0 for (i in 1..nalts) { if (altToPred!![i] == null) { - altToPred[i] = SemanticContext.NONE - } else if (altToPred[i] !== SemanticContext.NONE) { + altToPred[i] = SemanticContext.Empty.Instance + } else if (altToPred[i] !== SemanticContext.Empty.Instance) { nPredAlts++ } } @@ -1206,7 +1206,7 @@ open class ParserATNSimulator( if (ambigAlts != null && ambigAlts.get(i)) { pairs.add(DFAState.PredPrediction(pred!!, i)) } - if (pred !== SemanticContext.NONE) containsPredicate = true + if (pred !== SemanticContext.Empty.Instance) containsPredicate = true } return if (!containsPredicate) { @@ -1308,7 +1308,7 @@ open class ParserATNSimulator( val succeeded = ATNConfigSet(configs.fullCtx) val failed = ATNConfigSet(configs.fullCtx) for (c in configs) { - if (c.semanticContext !== SemanticContext.NONE) { + if (c.semanticContext !== SemanticContext.Empty.Instance) { val predicateEvaluationResult = evalSemanticContext(c.semanticContext, outerContext, c.alt, configs.fullCtx) if (predicateEvaluationResult) { succeeded.add(c) @@ -1333,7 +1333,7 @@ open class ParserATNSimulator( complete: Boolean): BitSet { val predictions = BitSet() for (pair in predPredictions) { - if (pair!!.pred === SemanticContext.NONE) { + if (pair!!.pred === SemanticContext.Empty.Instance) { predictions.set(pair!!.alt) if (!complete) { break @@ -1430,7 +1430,7 @@ open class ParserATNSimulator( for (i in 0 until config.context!!.size()) { if (config.context!!.getReturnState(i) == PredictionContext.EMPTY_RETURN_STATE) { if (fullCtx) { - configs.add(ATNConfig(config, config.state, PredictionContext.EMPTY), mergeCache) + configs.add(ATNConfig(config, config.state, EmptyPredictionContext.Instance), mergeCache) continue } else { // we have no context info, just chase follow links (if greedy) diff --git a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/PredictionContext.kt b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/PredictionContext.kt index 990b379d..8ba79414 100644 --- a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/PredictionContext.kt +++ b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/PredictionContext.kt @@ -8,7 +8,7 @@ package org.antlr.v4.kotlinruntime.atn import com.strumenta.kotlinmultiplatform.IdentityHashMap import com.strumenta.kotlinmultiplatform.assert -import org.antlr.v4.kotlinruntime.EMPTY_RULECTX +import org.antlr.v4.kotlinruntime.ParserRuleContext import org.antlr.v4.kotlinruntime.Recognizer import org.antlr.v4.kotlinruntime.RuleContext import org.antlr.v4.kotlinruntime.misc.DoubleKeyMap @@ -38,9 +38,9 @@ abstract class PredictionContext protected constructor( */ val cachedHashCode: Int ) { - /** This means only the [.EMPTY] (wildcard? not sure) context is in set. */ + /** This means only the [EmptyPredictionContext.Instance] (wildcard? not sure) context is in set. */ open val isEmpty: Boolean - get() = this === EMPTY + get() = this === EmptyPredictionContext.Instance abstract fun size(): Int @@ -65,7 +65,7 @@ abstract class PredictionContext protected constructor( } fun toStrings(recognizer: Recognizer<*, *>, currentState: Int): Array { - return toStrings(recognizer, EMPTY, currentState) + return toStrings(recognizer, EmptyPredictionContext.Instance, currentState) } // FROM SAM @@ -134,12 +134,6 @@ abstract class PredictionContext protected constructor( } companion object { - /** - * Represents `$` in local context prediction, which means wildcard. - * `*+x = *`. - */ - val EMPTY = EmptyPredictionContext() - /** * Represents `$` in an array in full context mode, when `$` * doesn't mean wildcard: `$ + x = [$,x]`. Here, @@ -150,15 +144,15 @@ abstract class PredictionContext protected constructor( private const val INITIAL_HASH = 1 /** Convert a [RuleContext] tree to a [PredictionContext] graph. - * Return [.EMPTY] if `outerContext` is empty or null. + * Return [EmptyPredictionContext.Instance] if `outerContext` is empty or null. */ fun fromRuleContext(atn: ATN, outerContext: RuleContext?): PredictionContext { - val outerContext1 = outerContext ?: EMPTY_RULECTX + val outerContext1 = outerContext ?: ParserRuleContext.EMPTY // if we are in RuleContext of start rule, s, then PredictionContext // is EMPTY. Nobody called us. (if we are empty, return empty) - if (outerContext1.readParent() == null || outerContext1 === EMPTY_RULECTX) { - return EMPTY + if (outerContext1.readParent() == null || outerContext1 === ParserRuleContext.EMPTY) { + return EmptyPredictionContext.Instance } // If we have a parent, convert it to a PredictionContext graph @@ -337,8 +331,8 @@ abstract class PredictionContext protected constructor( /** * Handle case where at least one of `a` or `b` is - * [.EMPTY]. In the following diagrams, the symbol `$` is used - * to represent [.EMPTY]. + * [EmptyPredictionContext.Instance]. In the following diagrams, the symbol `$` is used + * to represent [EmptyPredictionContext.Instance]. * *

Local-Context Merges

* @@ -347,11 +341,11 @@ abstract class PredictionContext protected constructor( * is true. * * - * [.EMPTY] is superset of any graph; return [.EMPTY].

+ * [EmptyPredictionContext.Instance] is superset of any graph; return [EmptyPredictionContext.Instance].

* * * - * [.EMPTY] and anything is `#EMPTY`, so merged parent is + * [EmptyPredictionContext.Instance] and anything is `#EMPTY`, so merged parent is * `#EMPTY`; return left graph.

* * @@ -369,7 +363,7 @@ abstract class PredictionContext protected constructor( * * * - * Must keep all contexts; [.EMPTY] in array is a special value (and + * Must keep all contexts; [EmptyPredictionContext.Instance] in array is a special value (and * null parent).

* * @@ -387,17 +381,17 @@ abstract class PredictionContext protected constructor( rootIsWildcard: Boolean ): PredictionContext? { if (rootIsWildcard) { - if (a === EMPTY) return EMPTY // * + b = * - if (b === EMPTY) return EMPTY // a + * = * + if (a === EmptyPredictionContext.Instance) return EmptyPredictionContext.Instance // * + b = * + if (b === EmptyPredictionContext.Instance) return EmptyPredictionContext.Instance // a + * = * } else { - if (a === EMPTY && b === EMPTY) return EMPTY // $ + $ = $ + if (a === EmptyPredictionContext.Instance && b === EmptyPredictionContext.Instance) return EmptyPredictionContext.Instance // $ + $ = $ - if (a === EMPTY) { // $ + x = [x,$] + if (a === EmptyPredictionContext.Instance) { // $ + x = [x,$] val payloads = intArrayOf(b.returnState, EMPTY_RETURN_STATE) val parents = arrayOf(b.parent, null) return ArrayPredictionContext(parents, payloads) } - if (b === EMPTY) { // x + $ = [x,$] ($ is always last if present) + if (b === EmptyPredictionContext.Instance) { // x + $ = [x,$] ($ is always last if present) val payloads = intArrayOf(a.returnState, EMPTY_RETURN_STATE) val parents = arrayOf(a.parent, null) return ArrayPredictionContext(parents, payloads) @@ -599,7 +593,7 @@ abstract class PredictionContext protected constructor( val updated: PredictionContext if (parents.size == 0) { - updated = EMPTY + updated = EmptyPredictionContext.Instance } else if (parents.size == 1) { updated = SingletonPredictionContext.create(parents[0], context.getReturnState(0)) } else { diff --git a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/PredictionContextCache.kt b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/PredictionContextCache.kt index d3c5af8f..ea98a7f4 100644 --- a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/PredictionContextCache.kt +++ b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/PredictionContextCache.kt @@ -18,7 +18,7 @@ class PredictionContextCache { * Protect shared cache from unsafe thread access. */ fun add(ctx: PredictionContext): PredictionContext { - if (ctx === PredictionContext.EMPTY) return PredictionContext.EMPTY + if (ctx === EmptyPredictionContext.Instance) return EmptyPredictionContext.Instance val existing = cache[ctx] if (existing != null) { // System.out.println(name+" reuses "+existing); diff --git a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/PredictionMode.kt b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/PredictionMode.kt index 04d618f3..a2fa036e 100644 --- a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/PredictionMode.kt +++ b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/PredictionMode.kt @@ -248,7 +248,7 @@ enum class PredictionMode { val dup = ATNConfigSet() for (c in configs) { var mutableC = c - mutableC = ATNConfig(mutableC, SemanticContext.NONE) + mutableC = ATNConfig(mutableC, SemanticContext.Empty.Instance) dup.add(mutableC) } configs = dup diff --git a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/SemanticContext.kt b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/SemanticContext.kt index ec9ab1f4..2555b699 100644 --- a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/SemanticContext.kt +++ b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/SemanticContext.kt @@ -13,6 +13,8 @@ import org.antlr.v4.kotlinruntime.Recognizer import org.antlr.v4.kotlinruntime.RuleContext import org.antlr.v4.kotlinruntime.atn.SemanticContext.* import org.antlr.v4.kotlinruntime.misc.MurmurHash +import kotlin.jvm.JvmStatic + // TODO(Edoardo): apply commit https://github.com/antlr/antlr4/commit/875911c2b2e6e50317e75ff2b5e80f4014a75954 @@ -50,7 +52,7 @@ abstract class SemanticContext { * @return The simplified semantic context after precedence predicates are * evaluated, which will be one of the following values. * - * * [.NONE]: if the predicate simplifies to `true` after + * * [Empty.Instance]: if the predicate simplifies to `true` after * precedence predicates are evaluated. * * `null`: if the predicate simplifies to `false` after * precedence predicates are evaluated. @@ -64,6 +66,21 @@ abstract class SemanticContext { return this } + class Empty : SemanticContext() { + companion object { + /** + * The default [SemanticContext], which is semantically equivalent to + * a predicate of the form `{true}?`. + */ + @JvmStatic + val Instance: Empty = Empty() + } + + override fun eval(parser: Recognizer<*, *>, parserCallStack: RuleContext): Boolean { + return false + } + } + class Predicate : SemanticContext { val ruleIndex: Int val predIndex: Int @@ -117,7 +134,7 @@ abstract class SemanticContext { override fun evalPrecedence(parser: Recognizer<*, *>, parserCallStack: RuleContext): SemanticContext? { return if (parser.precpred(parserCallStack, precedence)) { - NONE + Empty.Instance } else { null } @@ -237,7 +254,7 @@ abstract class SemanticContext { if (evaluated == null) { // The AND context is false if any element is false return null - } else if (evaluated !== NONE) { + } else if (evaluated !== Empty.Instance) { // Reduce the result by skipping true elements operands.add(evaluated) } @@ -249,7 +266,7 @@ abstract class SemanticContext { if (operands.isEmpty()) { // all elements were true, so the AND context is true - return NONE + return Empty.Instance } var result: SemanticContext? = operands[0] @@ -330,9 +347,9 @@ abstract class SemanticContext { for (context in opnds) { val evaluated = context.evalPrecedence(parser, parserCallStack) differs = differs or (evaluated !== context) - if (evaluated === NONE) { + if (evaluated === Empty.Instance) { // The OR context is true if any element is true - return NONE + return Empty.Instance } else if (evaluated != null) { // Reduce the result by skipping false elements operands.add(evaluated) @@ -362,19 +379,9 @@ abstract class SemanticContext { } companion object { - /** - * The default [SemanticContext], which is semantically equivalent to - * a predicate of the form `{true}?`. - */ - val NONE = object : SemanticContext() { - override fun eval(parser: Recognizer<*, *>, parserCallStack: RuleContext): Boolean { - return false - } - } - fun and(a: SemanticContext?, b: SemanticContext?): SemanticContext? { - if (a == null || a === NONE) return b - if (b == null || b === NONE) return a + if (a == null || a === Empty.Instance) return b + if (b == null || b === Empty.Instance) return a val result = AND(a, b) return if (result.opnds.size == 1) { result.opnds[0] @@ -389,7 +396,7 @@ abstract class SemanticContext { fun or(a: SemanticContext?, b: SemanticContext?): SemanticContext? { if (a == null) return b if (b == null) return a - if (a === NONE || b === NONE) return NONE + if (a === Empty.Instance || b === Empty.Instance) return Empty.Instance val result = OR(a, b) return if (result.opnds.size == 1) { result.opnds[0] diff --git a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/SingletonPredictionContext.kt b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/SingletonPredictionContext.kt index c2f0ba0b..b050b9a4 100644 --- a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/SingletonPredictionContext.kt +++ b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/SingletonPredictionContext.kt @@ -58,7 +58,7 @@ open class SingletonPredictionContext internal constructor(val parent: Predictio fun create(parent: PredictionContext?, returnState: Int): SingletonPredictionContext { return if (returnState == PredictionContext.EMPTY_RETURN_STATE && parent == null) { // someone can pass in the bits of an array ctx that mean $ - PredictionContext.EMPTY + EmptyPredictionContext.Instance } else SingletonPredictionContext(parent, returnState) } }