From 80933c287d228ee46a580bd70033b9b4d838057b Mon Sep 17 00:00:00 2001 From: sergeych Date: Mon, 12 Jan 2026 16:15:19 +0100 Subject: [PATCH] Refactor nullable suffix handling in compiler (nullable declaration bug fixed) --- .../kotlin/net/sergeych/lyng/Compiler.kt | 7 +++++- .../net/sergeych/lyng/CompilerContext.kt | 23 +++++++++++++------ lynglib/src/commonTest/kotlin/ScriptTest.kt | 2 +- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt index ad93c31..e7ac460 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt @@ -1048,7 +1048,12 @@ class Compiler( } // Nullable suffix after base or generic - val isNullable = cc.skipTokenOfType(Token.Type.QUESTION, isOptional = true) + val isNullable = if (cc.skipTokenOfType(Token.Type.QUESTION, isOptional = true)) { + true + } else if (cc.skipTokenOfType(Token.Type.IFNULLASSIGN, isOptional = true)) { + cc.pushPendingAssign() + true + } else false val endPos = cc.currentPos() val miniRef = buildBaseRef(if (miniArgs != null) endPos else lastEnd, miniArgs, isNullable) diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/CompilerContext.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/CompilerContext.kt index 4f4b350..c0ce639 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/CompilerContext.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/CompilerContext.kt @@ -35,8 +35,9 @@ class CompilerContext(val tokens: List) { var currentIndex = 0 private var pendingGT = 0 + private var pendingAssign = false - fun hasNext() = currentIndex < tokens.size || pendingGT > 0 + fun hasNext() = currentIndex < tokens.size || pendingGT > 0 || pendingAssign fun hasPrevious() = currentIndex > 0 fun next(): Token { if (pendingGT > 0) { @@ -44,6 +45,11 @@ class CompilerContext(val tokens: List) { val last = tokens[currentIndex - 1] return Token(">", last.pos.copy(column = last.pos.column + 1), Token.Type.GT) } + if (pendingAssign) { + pendingAssign = false + val last = tokens[currentIndex - 1] + return Token("=", last.pos.copy(column = last.pos.column + 1), Token.Type.ASSIGN) + } return if (currentIndex < tokens.size) tokens[currentIndex++] else Token("", tokens.last().pos, Token.Type.EOF) } @@ -52,16 +58,19 @@ class CompilerContext(val tokens: List) { pendingGT++ } - fun previous() = if (pendingGT > 0) { - pendingGT-- // This is wrong, previous should go back. - // But we don't really use previous() in generics parser after splitting. - throw IllegalStateException("previous() not supported after pushPendingGT") + fun pushPendingAssign() { + pendingAssign = true + } + + fun previous() = if (pendingGT > 0 || pendingAssign) { + throw IllegalStateException("previous() not supported after pushPending tokens") } else if (!hasPrevious()) throw IllegalStateException("No previous token") else tokens[--currentIndex] - fun savePos() = (currentIndex shl 2) or (pendingGT and 3) + fun savePos() = (currentIndex shl 3) or (pendingGT and 3) or (if (pendingAssign) 4 else 0) fun restorePos(pos: Int) { - currentIndex = pos shr 2 + currentIndex = pos shr 3 pendingGT = pos and 3 + pendingAssign = (pos and 4) != 0 } fun ensureLabelIsValid(pos: Pos, label: String) { diff --git a/lynglib/src/commonTest/kotlin/ScriptTest.kt b/lynglib/src/commonTest/kotlin/ScriptTest.kt index 5728223..34a71ca 100644 --- a/lynglib/src/commonTest/kotlin/ScriptTest.kt +++ b/lynglib/src/commonTest/kotlin/ScriptTest.kt @@ -4803,7 +4803,7 @@ class ScriptTest { fun f(a: String = "foo") = a + "!" fun g(a: String? = null) = a ?: "!!" assertEquals(f(), "foo!") - assertEquals(f(), "!!") + assertEquals(g(), "!!") assertEquals(f("bar"), "bar!") class T(b: Int=42,c: String?=null) assertEquals(42, T().b)