From af887929928cec33b18c38b4d1cb2a4d862f2f19 Mon Sep 17 00:00:00 2001 From: Edoardo Luppi Date: Thu, 4 Jul 2024 18:49:13 +0200 Subject: [PATCH 1/2] refactor: cleanup the ParseTree hierarchy and open its functions --- .../benchmarks/mysql/MySQLErrorListener.kt | 4 +- .../v4/kotlinruntime/BailErrorStrategy.kt | 4 +- .../v4/kotlinruntime/DefaultErrorStrategy.kt | 2 +- .../org/antlr/v4/kotlinruntime/Parser.kt | 18 ++--- .../v4/kotlinruntime/ParserRuleContext.kt | 74 +++++++++++++------ .../org/antlr/v4/kotlinruntime/RuleContext.kt | 17 +++-- .../org/antlr/v4/kotlinruntime/atn/ATN.kt | 2 +- .../v4/kotlinruntime/atn/PredictionContext.kt | 4 +- .../antlr/v4/kotlinruntime/tree/ParseTree.kt | 14 ++-- .../v4/kotlinruntime/tree/TerminalNodeImpl.kt | 4 +- .../org/antlr/v4/kotlinruntime/tree/Tree.kt | 2 +- .../org/antlr/v4/kotlinruntime/tree/Trees.kt | 12 +-- 12 files changed, 93 insertions(+), 64 deletions(-) diff --git a/antlr-kotlin-benchmarks/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/benchmarks/mysql/MySQLErrorListener.kt b/antlr-kotlin-benchmarks/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/benchmarks/mysql/MySQLErrorListener.kt index 784de3b0..ca98e893 100644 --- a/antlr-kotlin-benchmarks/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/benchmarks/mysql/MySQLErrorListener.kt +++ b/antlr-kotlin-benchmarks/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/benchmarks/mysql/MySQLErrorListener.kt @@ -106,8 +106,8 @@ public class MySQLErrorListener : BaseErrorListener() { // Walk up from generic rules to reach something that gives us more context, if needed. var context = parser.context - while (context?.parent != null && simpleRules.contains(context.ruleIndex)) { - context = context.parent as ParserRuleContext? + while (context?.getParent() != null && simpleRules.contains(context.ruleIndex)) { + context = context.getParent() } // Try to find the expected input by examining the current parser context and diff --git a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/BailErrorStrategy.kt b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/BailErrorStrategy.kt index a591565c..2016286d 100644 --- a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/BailErrorStrategy.kt +++ b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/BailErrorStrategy.kt @@ -46,7 +46,7 @@ public class BailErrorStrategy : DefaultErrorStrategy() { while (context != null) { context.exception = e - context = context.readParent() + context = context.getParent() } throw ParseCancellationException(e) @@ -63,7 +63,7 @@ public class BailErrorStrategy : DefaultErrorStrategy() { while (context != null) { context.exception = e - context = context.readParent() + context = context.getParent() } throw ParseCancellationException(e) diff --git a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/DefaultErrorStrategy.kt b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/DefaultErrorStrategy.kt index c499eae2..faefc9db 100644 --- a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/DefaultErrorStrategy.kt +++ b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/DefaultErrorStrategy.kt @@ -735,7 +735,7 @@ public open class DefaultErrorStrategy : ANTLRErrorStrategy { val rt = invokingState!!.transition(0) as RuleTransition val follow = atn.nextTokens(rt.followState) recoverSet.addAll(follow) - ctx = ctx.readParent() + ctx = ctx.getParent() } recoverSet.remove(Token.EPSILON) diff --git a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/Parser.kt b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/Parser.kt index a7c8fc9d..41737385 100644 --- a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/Parser.kt +++ b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/Parser.kt @@ -584,7 +584,7 @@ public abstract class Parser(input: TokenStream) : Recognizer? = null - // This does not exist in the Java runtime - public val position: Position? - get() = if (start != null && stop?.endPoint() != null) { - Position(start!!.startPoint(), stop!!.endPoint()!!) - } else { - null - } - /** * The initial token in this context. * @@ -81,6 +91,7 @@ public open class ParserRuleContext : RuleContext { * * If the rule successfully completed, this is `null`. */ + @JvmField public var exception: RecognitionException? = null override val childCount: Int @@ -98,11 +109,26 @@ public open class ParserRuleContext : RuleContext { } } + // Override to make the type more specific + override val ruleContext: ParserRuleContext + get() = this + + // Override to make the type more specific + override val payload: ParserRuleContext + get() = this + public constructor() public constructor(parent: ParserRuleContext?, invokingStateNumber: Int) : super(parent, invokingStateNumber) - override fun readParent(): ParserRuleContext? = - super.readParent() as ParserRuleContext? + // Override to make the type more specific + override fun getParent(): ParserRuleContext? = + parent as ParserRuleContext? + + override fun setParent(value: RuleContext?) { + // Although we could assign 'value' straight away, we cast it + // to ensure we are dealing with a compatible type + parent = value as ParserRuleContext? + } /** * Copy a context (I'm deliberately not using copy constructor) to avoid @@ -153,12 +179,12 @@ public open class ParserRuleContext : RuleContext { * Other [addChild] methods call this. * * We cannot set the parent pointer of the incoming node - * because the existing interfaces do not have a [assignParent] + * because the existing interfaces do not have a [setParent] * method and I don't want to break backward compatibility for this. * * @since 4.7 */ - public fun addAnyChild(t: T): T { + public open fun addAnyChild(t: T): T { var childrenTemp = children if (childrenTemp == null) { @@ -170,14 +196,14 @@ public open class ParserRuleContext : RuleContext { return t } - public fun addChild(ruleInvocation: RuleContext): RuleContext = + public open fun addChild(ruleInvocation: RuleContext): RuleContext = addAnyChild(ruleInvocation) /** * Add a token leaf node child and force its parent to be this node. */ - public fun addChild(t: TerminalNode): TerminalNode { - t.assignParent(this) + public open fun addChild(t: TerminalNode): TerminalNode { + t.setParent(this) return addAnyChild(t) } @@ -186,8 +212,8 @@ public open class ParserRuleContext : RuleContext { * * @since 4.7 */ - public fun addErrorNode(errorNode: ErrorNode): ErrorNode { - errorNode.assignParent(this) + public open fun addErrorNode(errorNode: ErrorNode): ErrorNode { + errorNode.setParent(this) return addAnyChild(errorNode) } @@ -197,7 +223,7 @@ public open class ParserRuleContext : RuleContext { * * If we have `# label`, we will need to remove generic `ruleContext` object. */ - public fun removeLastChild() { + public open fun removeLastChild() { val tempChildren = children tempChildren?.removeAt(tempChildren.size - 1) } @@ -211,7 +237,7 @@ public open class ParserRuleContext : RuleContext { } } - public fun getChild(ctxType: KClass, i: Int): T? { + public open fun getChild(ctxType: KClass, i: Int): T? { val tempChildren = children if (tempChildren == null || i < 0 || i >= tempChildren.size) { @@ -235,7 +261,7 @@ public open class ParserRuleContext : RuleContext { return null } - public fun getToken(ttype: Int, i: Int): TerminalNode? { + public open fun getToken(ttype: Int, i: Int): TerminalNode? { val tempChildren = children if (tempChildren == null || i < 0 || i >= tempChildren.size) { @@ -262,7 +288,7 @@ public open class ParserRuleContext : RuleContext { return null } - public fun getTokens(ttype: Int): List { + public open fun getTokens(ttype: Int): List { val tempChildren = children ?: return emptyList() val tokens = ArrayList() @@ -279,10 +305,10 @@ public open class ParserRuleContext : RuleContext { return tokens } - public fun getRuleContext(ctxType: KClass, i: Int): T? = + public open fun getRuleContext(ctxType: KClass, i: Int): T? = getChild(ctxType, i) - public fun getRuleContexts(ctxType: KClass): List { + public open fun getRuleContexts(ctxType: KClass): List { val tempChildren = children ?: return emptyList() val contexts = ArrayList() @@ -299,7 +325,7 @@ public open class ParserRuleContext : RuleContext { /** * Used for rule context info debugging during parse-time, not so much for ATN debugging. */ - public fun toInfoString(recognizer: Parser): String { + public open fun toInfoString(recognizer: Parser): String { val rules = recognizer.getRuleInvocationStack(this).toMutableList() rules.reverse() return "ParserRuleContext$rules{start=$start, stop=$stop}" 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 57129231..452c858c 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 @@ -8,6 +8,7 @@ import org.antlr.v4.kotlinruntime.tree.ParseTree import org.antlr.v4.kotlinruntime.tree.ParseTreeVisitor import org.antlr.v4.kotlinruntime.tree.RuleNode import org.antlr.v4.kotlinruntime.tree.Trees +import kotlin.jvm.JvmField /** * A rule context is a record of a single rule invocation. @@ -68,7 +69,8 @@ public open class RuleContext : RuleNode { /** * What context invoked this rule? */ - public var parent: RuleContext? = null + @JvmField + protected var parent: RuleContext? = null /** * What state invoked the rule associated with this context? @@ -77,6 +79,7 @@ public open class RuleContext : RuleNode { * If [parent] is `null`, this should be `-1` as this context * object represents the start rule. */ + @JvmField public var invokingState: Int = -1 /** @@ -151,14 +154,14 @@ public open class RuleContext : RuleNode { this.invokingState = invokingState } - override fun readParent(): RuleContext? = + override fun getParent(): RuleContext? = parent - override fun assignParent(value: RuleContext?) { + override fun setParent(value: RuleContext?) { parent = value } - public fun depth(): Int { + public open fun depth(): Int { var n = 0 var p: RuleContext? = this @@ -191,7 +194,7 @@ public open class RuleContext : RuleNode { * * Print just a node if this is a leaf. */ - public fun toStringTree(ruleNames: List?): String = + public open fun toStringTree(ruleNames: List?): String = Trees.toStringTree(this, ruleNames) override fun toStringTree(): String = @@ -201,7 +204,7 @@ public open class RuleContext : RuleNode { toString(ruleNames = null, stop = null) // recog null unless ParserRuleContext, in which case we use subclass toString(...) - public fun toString(recog: Recognizer<*, *>?, stop: RuleContext = ParserRuleContext.EMPTY): String { + public open fun toString(recog: Recognizer<*, *>?, stop: RuleContext = ParserRuleContext.EMPTY): String { val ruleNames = recog?.ruleNames val ruleNamesList = if (ruleNames != null) { listOf(*ruleNames) @@ -212,7 +215,7 @@ public open class RuleContext : RuleNode { return toString(ruleNamesList, stop) } - public fun toString(ruleNames: List?, stop: RuleContext? = null): String { + public open fun toString(ruleNames: List?, stop: RuleContext? = null): String { val buf = StringBuilder() var p: RuleContext? = this buf.append("[") diff --git a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/ATN.kt b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/ATN.kt index cfb73337..23dbdc19 100644 --- a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/ATN.kt +++ b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/atn/ATN.kt @@ -178,7 +178,7 @@ public class ATN(public val grammarType: ATNType, public val maxTokenType: Int) following = nextTokens(rt.followState) expected.addAll(following) expected.remove(Token.EPSILON) - ctx = ctx.readParent() + ctx = ctx.getParent() } if (following.contains(Token.EPSILON)) { 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 0331d9ad..e8dd32fc 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 @@ -64,12 +64,12 @@ public abstract class PredictionContext protected constructor( // If we are in RuleContext of start rule, s, then PredictionContext // is EMPTY. Nobody called us. (if we are empty, return empty) - if (tempOuterContext.parent == null || tempOuterContext === ParserRuleContext.EMPTY) { + if (tempOuterContext.getParent() == null || tempOuterContext === ParserRuleContext.EMPTY) { return EmptyPredictionContext } // If we have a parent, convert it to a PredictionContext graph - val parent = fromRuleContext(atn, tempOuterContext.readParent()) + val parent = fromRuleContext(atn, tempOuterContext.getParent()) val state = atn.states[tempOuterContext.invokingState] val transition = state!!.transition(0) as RuleTransition return SingletonPredictionContext.create(parent, transition.followState.stateNumber) diff --git a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/tree/ParseTree.kt b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/tree/ParseTree.kt index f1558497..c8c0c2af 100644 --- a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/tree/ParseTree.kt +++ b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/tree/ParseTree.kt @@ -23,6 +23,12 @@ public interface ParseTree : SyntaxTree { */ public val text: String + // Narrows down the return type to ParseTree + override fun getParent(): ParseTree? + + // Narrows down the return type to ParseTree + override fun getChild(i: Int): ParseTree? + /** * Set the parent for this node. * @@ -39,7 +45,7 @@ public interface ParseTree : SyntaxTree { * * @since 4.7 */ - public fun assignParent(value: RuleContext?) + public fun setParent(value: RuleContext?) /** * The [ParseTreeVisitor] needs a double dispatch method. @@ -51,10 +57,4 @@ public interface ParseTree : SyntaxTree { * based upon the parser. */ public fun toStringTree(parser: Parser): String - - // Narrows down the return type to ParseTree - override fun readParent(): ParseTree? - - // Narrows down the return type to ParseTree - override fun getChild(i: Int): ParseTree? } diff --git a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/tree/TerminalNodeImpl.kt b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/tree/TerminalNodeImpl.kt index de92430d..6dec81fb 100644 --- a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/tree/TerminalNodeImpl.kt +++ b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/tree/TerminalNodeImpl.kt @@ -25,10 +25,10 @@ public open class TerminalNodeImpl(override var symbol: Token) : TerminalNode { return Interval(tokenIndex, tokenIndex) } - override fun readParent(): ParseTree? = + override fun getParent(): ParseTree? = parent - override fun assignParent(value: RuleContext?) { + override fun setParent(value: RuleContext?) { parent = value } diff --git a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/tree/Tree.kt b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/tree/Tree.kt index 801fb069..8c6c519d 100644 --- a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/tree/Tree.kt +++ b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/tree/Tree.kt @@ -30,7 +30,7 @@ public interface Tree { * * If the return value is `null`, then this node is the root of the tree. */ - public fun readParent(): Tree? + public fun getParent(): Tree? /** * If there are children, get the `i`th value indexed from `0`. diff --git a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/tree/Trees.kt b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/tree/Trees.kt index ca5cdfa9..1acce5ca 100644 --- a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/tree/Trees.kt +++ b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/tree/Trees.kt @@ -125,17 +125,17 @@ public object Trees { * @since 4.5.1 */ public fun getAncestors(t: Tree): List { - if (t.readParent() == null) { + if (t.getParent() == null) { return emptyList() } val ancestors = ArrayList() - var t1 = t.readParent() + var t1 = t.getParent() while (t1 != null) { // Insert at start ancestors.add(0, t1) - t1 = t1.readParent() + t1 = t1.getParent() } return ancestors @@ -147,11 +147,11 @@ public object Trees { * @since 4.5.1 */ public fun isAncestorOf(t: Tree?, u: Tree?): Boolean { - if (t == null || u == null || t.readParent() == null) { + if (t == null || u == null || t.getParent() == null) { return false } - var p = u.readParent() + var p = u.getParent() while (p != null) { // Keep reference equality! @@ -159,7 +159,7 @@ public object Trees { return true } - p = p.readParent() + p = p.getParent() } return false From 5e2e82e453afb29bfa86df76927479ecb25045c9 Mon Sep 17 00:00:00 2001 From: Edoardo Luppi Date: Thu, 4 Jul 2024 18:53:31 +0200 Subject: [PATCH 2/2] refactor: minor cleanups --- .../kotlin/org/antlr/v4/kotlinruntime/Parser.kt | 6 ++++-- .../kotlin/org/antlr/v4/kotlinruntime/Token.kt | 10 ++++------ .../v4/kotlinruntime/tree/IterativeParseTreeWalker.kt | 6 +++++- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/Parser.kt b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/Parser.kt index 41737385..38d5f760 100644 --- a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/Parser.kt +++ b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/Parser.kt @@ -49,8 +49,10 @@ public abstract class Parser(input: TokenStream) : Recognizer) { - (ctx.children as ArrayList<*>).trimToSize() + val children = ctx.children + + if (children is ArrayList<*>) { + children.trimToSize() } } } diff --git a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/Token.kt b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/Token.kt index 3a554c73..5d1e2330 100644 --- a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/Token.kt +++ b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/Token.kt @@ -117,10 +117,8 @@ public interface Token { public fun startPoint(): Point = Point(line, charPositionInLine) - public fun endPoint(): Point? = - if (text == null) { - null - } else { - Point(line, charPositionInLine).advance(text!!) - } + public fun endPoint(): Point? { + val text = this.text ?: return null + return Point(line, charPositionInLine).advance(text) + } } diff --git a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/tree/IterativeParseTreeWalker.kt b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/tree/IterativeParseTreeWalker.kt index 28f23b52..6005ac91 100644 --- a/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/tree/IterativeParseTreeWalker.kt +++ b/antlr-kotlin-runtime/src/commonMain/kotlin/org/antlr/v4/kotlinruntime/tree/IterativeParseTreeWalker.kt @@ -57,7 +57,11 @@ public open class IterativeParseTreeWalker : ParseTreeWalker() { // No next, sibling, so move up currentNode = nodeStack.removeFirst() currentIndex = indexStack.pop() - } while (currentNode != null) + + // The original condition was 'currentNode != null', + // but since nodeStack.removeFirst() throws if there + // is no element, the condition is always true + } while (true) } } }