Enable tests and fix destructuring/prop setter parsing

This commit is contained in:
Sergey Chernov 2026-02-03 03:30:04 +03:00
parent 824a58bbc5
commit be337144eb
3 changed files with 115 additions and 17 deletions

View File

@ -1981,6 +1981,39 @@ class Compiler(
} }
} }
private suspend fun parseDestructuringPattern(): List<ListEntry> {
// it should be called after Token.Type.LBRACKET is consumed
val entries = mutableListOf<ListEntry>()
while (true) {
val t = cc.next()
when (t.type) {
Token.Type.COMMA -> {
// allow trailing/extra commas
}
Token.Type.RBRACKET -> return entries
Token.Type.LBRACKET -> {
val nested = parseDestructuringPattern()
entries += ListEntry.Element(ListLiteralRef(nested))
}
Token.Type.ELLIPSIS -> {
val id = cc.requireToken(Token.Type.ID, "Expected identifier after ...")
val ref = LocalVarRef(id.value, id.pos)
entries += ListEntry.Spread(ref)
}
Token.Type.ID -> {
val ref = LocalVarRef(t.value, t.pos)
if (cc.peekNextNonWhitespace().type == Token.Type.ELLIPSIS) {
cc.next()
entries += ListEntry.Spread(ref)
} else {
entries += ListEntry.Element(ref)
}
}
else -> throw ScriptError(t.pos, "invalid destructuring pattern: expected identifier")
}
}
}
private fun parseScopeOperator(operand: ObjRef?): ObjRef { private fun parseScopeOperator(operand: ObjRef?): ObjRef {
// implement global scope maybe? // implement global scope maybe?
if (operand == null) throw ScriptError(cc.next().pos, "Expecting expression before ::") if (operand == null) throw ScriptError(cc.next().pos, "Expecting expression before ::")
@ -4406,6 +4439,55 @@ class Compiler(
return parseBlockWithPredeclared(emptyList(), skipLeadingBrace) return parseBlockWithPredeclared(emptyList(), skipLeadingBrace)
} }
private suspend fun parseExpressionWithPredeclared(
predeclared: List<Pair<String, Boolean>>
): Statement {
val startPos = cc.currentPos()
resolutionSink?.enterScope(ScopeKind.BLOCK, startPos, null)
val exprSlotPlan = SlotPlan(mutableMapOf(), 0, nextScopeId++)
for ((name, isMutable) in predeclared) {
declareSlotNameIn(exprSlotPlan, name, isMutable, isDelegated = false)
resolutionSink?.declareSymbol(name, SymbolKind.LOCAL, isMutable, startPos, isOverride = false)
}
slotPlanStack.add(exprSlotPlan)
val capturePlan = CapturePlan(exprSlotPlan)
capturePlanStack.add(capturePlan)
val expr = try {
parseExpression() ?: throw ScriptError(cc.current().pos, "Expected expression")
} finally {
capturePlanStack.removeLast()
slotPlanStack.removeLast()
}
resolutionSink?.exitScope(cc.currentPos())
return expr
}
private suspend fun parseExpressionBlockWithPredeclared(
predeclared: List<Pair<String, Boolean>>
): Statement {
val startPos = cc.currentPos()
resolutionSink?.enterScope(ScopeKind.BLOCK, startPos, null)
val blockSlotPlan = SlotPlan(mutableMapOf(), 0, nextScopeId++)
for ((name, isMutable) in predeclared) {
declareSlotNameIn(blockSlotPlan, name, isMutable, isDelegated = false)
resolutionSink?.declareSymbol(name, SymbolKind.LOCAL, isMutable, startPos, isOverride = false)
}
slotPlanStack.add(blockSlotPlan)
val capturePlan = CapturePlan(blockSlotPlan)
capturePlanStack.add(capturePlan)
val expr = try {
parseExpression() ?: throw ScriptError(cc.current().pos, "Expected expression")
} finally {
capturePlanStack.removeLast()
slotPlanStack.removeLast()
}
val planSnapshot = slotPlanIndices(blockSlotPlan)
val block = Script(startPos, listOf(expr))
val stmt = BlockStatement(block, planSnapshot, capturePlan.captures.toList(), startPos)
resolutionSink?.exitScope(cc.currentPos())
return stmt
}
private fun resolveInitializerObjClass(initializer: Statement?): ObjClass? { private fun resolveInitializerObjClass(initializer: Statement?): ObjClass? {
if (initializer is BytecodeStatement) { if (initializer is BytecodeStatement) {
val fn = initializer.bytecodeFunction() val fn = initializer.bytecodeFunction()
@ -4647,7 +4729,7 @@ class Compiler(
// Destructuring // Destructuring
if (isStatic) throw ScriptError(start, "static destructuring is not supported") if (isStatic) throw ScriptError(start, "static destructuring is not supported")
val entries = parseArrayLiteral() val entries = parseDestructuringPattern()
val pattern = ListLiteralRef(entries) val pattern = ListLiteralRef(entries)
// Register all names in the pattern // Register all names in the pattern
@ -5043,14 +5125,20 @@ class Compiler(
implicitThisTypeName = extTypeName implicitThisTypeName = extTypeName
) )
) { ) {
parseBlock() parseBlockWithPredeclared(listOf(setArgName to true))
} }
object : Statement() { object : Statement() {
override val pos: Pos = body.pos override val pos: Pos = body.pos
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
val value = scope.args.list.firstOrNull() ?: ObjNull val value = scope.args.list.firstOrNull() ?: ObjNull
scope.addItem(setArgName, true, value, recordType = ObjRecord.Type.Argument) scope.addItem(setArgName, true, value, recordType = ObjRecord.Type.Argument)
return body.execute(scope) val prev = scope.skipScopeCreation
scope.skipScopeCreation = true
return try {
body.execute(scope)
} finally {
scope.skipScopeCreation = prev
}
} }
} }
} else if (cc.peekNextNonWhitespace().type == Token.Type.ASSIGN) { } else if (cc.peekNextNonWhitespace().type == Token.Type.ASSIGN) {
@ -5063,8 +5151,7 @@ class Compiler(
implicitThisTypeName = extTypeName implicitThisTypeName = extTypeName
) )
) { ) {
parseExpression() parseExpressionBlockWithPredeclared(listOf(setArgName to true))
?: throw ScriptError(cc.current().pos, "Expected setter expression")
} }
val st = expr val st = expr
object : Statement() { object : Statement() {
@ -5072,7 +5159,13 @@ class Compiler(
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
val value = scope.args.list.firstOrNull() ?: ObjNull val value = scope.args.list.firstOrNull() ?: ObjNull
scope.addItem(setArgName, true, value, recordType = ObjRecord.Type.Argument) scope.addItem(setArgName, true, value, recordType = ObjRecord.Type.Argument)
return st.execute(scope) val prev = scope.skipScopeCreation
scope.skipScopeCreation = true
return try {
st.execute(scope)
} finally {
scope.skipScopeCreation = prev
}
} }
} }
} else { } else {
@ -5100,14 +5193,20 @@ class Compiler(
implicitThisTypeName = extTypeName implicitThisTypeName = extTypeName
) )
) { ) {
parseBlock() parseBlockWithPredeclared(listOf(setArg.value to true))
} }
object : Statement() { object : Statement() {
override val pos: Pos = body.pos override val pos: Pos = body.pos
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
val value = scope.args.list.firstOrNull() ?: ObjNull val value = scope.args.list.firstOrNull() ?: ObjNull
scope.addItem(setArg.value, true, value, recordType = ObjRecord.Type.Argument) scope.addItem(setArg.value, true, value, recordType = ObjRecord.Type.Argument)
return body.execute(scope) val prev = scope.skipScopeCreation
scope.skipScopeCreation = true
return try {
body.execute(scope)
} finally {
scope.skipScopeCreation = prev
}
} }
} }
} else if (cc.peekNextNonWhitespace().type == Token.Type.ASSIGN) { } else if (cc.peekNextNonWhitespace().type == Token.Type.ASSIGN) {
@ -5120,17 +5219,20 @@ class Compiler(
implicitThisTypeName = extTypeName implicitThisTypeName = extTypeName
) )
) { ) {
parseExpression() ?: throw ScriptError( parseExpressionBlockWithPredeclared(listOf(setArg.value to true))
cc.current().pos,
"Expected setter expression"
)
} }
object : Statement() { object : Statement() {
override val pos: Pos = st.pos override val pos: Pos = st.pos
override suspend fun execute(scope: Scope): Obj { override suspend fun execute(scope: Scope): Obj {
val value = scope.args.list.firstOrNull() ?: ObjNull val value = scope.args.list.firstOrNull() ?: ObjNull
scope.addItem(setArg.value, true, value, recordType = ObjRecord.Type.Argument) scope.addItem(setArg.value, true, value, recordType = ObjRecord.Type.Argument)
return st.execute(scope) val prev = scope.skipScopeCreation
scope.skipScopeCreation = true
return try {
st.execute(scope)
} finally {
scope.skipScopeCreation = prev
}
} }
} }
} else { } else {

View File

@ -926,7 +926,6 @@ class ScriptTest {
) )
} }
@Ignore("incremental enable")
@Test @Test
fun bookTest0() = runTest { fun bookTest0() = runTest {
assertEquals( assertEquals(
@ -1041,7 +1040,6 @@ class ScriptTest {
// assertEquals( "4", c.eval("x+0").toString()) // assertEquals( "4", c.eval("x+0").toString())
} }
@Ignore("incremental enable")
@Test @Test
fun bookTest2() = runTest { fun bookTest2() = runTest {
val src = """ val src = """
@ -4609,7 +4607,6 @@ class ScriptTest {
} }
@Ignore("incremental enable: destructuring assignments not implemented in bytecode compiler")
@Test @Test
fun testDestructuringAssignment() = runTest { fun testDestructuringAssignment() = runTest {
eval( eval(

View File

@ -6,7 +6,6 @@ import kotlin.test.Test
class PropsTest { class PropsTest {
@Ignore("Setter parameter binding (value) not wired in compile-time resolution yet")
@Test @Test
fun propsProposal() = runTest { fun propsProposal() = runTest {
eval(""" eval("""