Fix apply captures, class forward refs, and when bytecode
This commit is contained in:
parent
4b66454bf3
commit
615dc026f7
@ -30,9 +30,15 @@ class BlockStatement(
|
||||
val target = if (scope.skipScopeCreation) scope else scope.createChildScope(startPos)
|
||||
if (slotPlan.isNotEmpty()) target.applySlotPlan(slotPlan)
|
||||
if (captureSlots.isNotEmpty()) {
|
||||
val applyScope = scope as? ApplyScope
|
||||
for (capture in captureSlots) {
|
||||
val rec = scope.resolveCaptureRecord(capture.name)
|
||||
?: scope.raiseSymbolNotFound("symbol ${capture.name} not found")
|
||||
val rec = if (applyScope != null) {
|
||||
applyScope.resolveCaptureRecord(capture.name)
|
||||
?: applyScope.callScope.resolveCaptureRecord(capture.name)
|
||||
} else {
|
||||
scope.resolveCaptureRecord(capture.name)
|
||||
} ?: (applyScope?.callScope ?: scope)
|
||||
.raiseSymbolNotFound("symbol ${capture.name} not found")
|
||||
target.updateSlotFor(capture.name, rec)
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,7 +71,8 @@ class ClosureScope(val callScope: Scope, val closureScope: Scope) :
|
||||
}
|
||||
}
|
||||
|
||||
class ApplyScope(callScope: Scope, val applied: Scope) : Scope(callScope.parent?.parent ?: callScope.parent ?: callScope, thisObj = applied.thisObj) {
|
||||
class ApplyScope(val callScope: Scope, val applied: Scope) :
|
||||
Scope(callScope.parent?.parent ?: callScope.parent ?: callScope, thisObj = applied.thisObj) {
|
||||
|
||||
override fun get(name: String): ObjRecord? {
|
||||
return applied.get(name) ?: super.get(name)
|
||||
|
||||
@ -179,6 +179,63 @@ class Compiler(
|
||||
}
|
||||
}
|
||||
|
||||
private fun predeclareClassMembers(target: MutableSet<String>) {
|
||||
val saved = cc.savePos()
|
||||
var depth = 0
|
||||
val modifiers = setOf(
|
||||
"public", "private", "protected", "internal",
|
||||
"override", "abstract", "extern", "static", "transient"
|
||||
)
|
||||
fun nextNonWs(): Token {
|
||||
var t = cc.next()
|
||||
while (t.type == Token.Type.NEWLINE || t.type == Token.Type.SINGLE_LINE_COMMENT || t.type == Token.Type.MULTILINE_COMMENT) {
|
||||
t = cc.next()
|
||||
}
|
||||
return t
|
||||
}
|
||||
try {
|
||||
while (cc.hasNext()) {
|
||||
var t = cc.next()
|
||||
when (t.type) {
|
||||
Token.Type.LBRACE -> depth++
|
||||
Token.Type.RBRACE -> if (depth == 0) break else depth--
|
||||
Token.Type.ID -> if (depth == 0) {
|
||||
while (t.type == Token.Type.ID && t.value in modifiers) {
|
||||
t = nextNonWs()
|
||||
}
|
||||
when (t.value) {
|
||||
"fun", "fn", "val", "var" -> {
|
||||
val nameToken = nextNonWs()
|
||||
if (nameToken.type == Token.Type.ID) {
|
||||
val afterName = cc.peekNextNonWhitespace()
|
||||
if (afterName.type != Token.Type.DOT) {
|
||||
target.add(nameToken.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
"class", "object" -> {
|
||||
val nameToken = nextNonWs()
|
||||
if (nameToken.type == Token.Type.ID) {
|
||||
target.add(nameToken.value)
|
||||
}
|
||||
}
|
||||
"enum" -> {
|
||||
val next = nextNonWs()
|
||||
val nameToken = if (next.type == Token.Type.ID && next.value == "class") nextNonWs() else next
|
||||
if (nameToken.type == Token.Type.ID) {
|
||||
target.add(nameToken.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
cc.restorePos(saved)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildParamSlotPlan(names: List<String>): SlotPlan {
|
||||
val map = mutableMapOf<String, Int>()
|
||||
var idx = 0
|
||||
@ -228,6 +285,10 @@ class Compiler(
|
||||
val value = ObjString(packageName ?: "unknown").asReadonly
|
||||
return ConstRef(value)
|
||||
}
|
||||
if (name == "$~") {
|
||||
resolutionSink?.reference(name, pos)
|
||||
return LocalVarRef(name, pos)
|
||||
}
|
||||
if (name == "this") {
|
||||
resolutionSink?.reference(name, pos)
|
||||
return LocalVarRef(name, pos)
|
||||
@ -1415,8 +1476,20 @@ class Compiler(
|
||||
// and the source closure of the lambda which might have other thisObj.
|
||||
val context = scope.applyClosure(closureScope)
|
||||
if (paramSlotPlanSnapshot.isNotEmpty()) context.applySlotPlan(paramSlotPlanSnapshot)
|
||||
if (captureSlots.isNotEmpty() && context !is ApplyScope) {
|
||||
if (captureSlots.isNotEmpty()) {
|
||||
val moduleScope = if (context is ApplyScope) {
|
||||
var s: Scope? = closureScope
|
||||
while (s != null && s !is ModuleScope) {
|
||||
s = s.parent
|
||||
}
|
||||
s as? ModuleScope
|
||||
} else {
|
||||
null
|
||||
}
|
||||
for (capture in captureSlots) {
|
||||
if (moduleScope != null && moduleScope.getLocalRecordDirect(capture.name) != null) {
|
||||
continue
|
||||
}
|
||||
val rec = closureScope.resolveCaptureRecord(capture.name)
|
||||
?: closureScope.raiseSymbolNotFound("symbol ${capture.name} not found")
|
||||
context.updateSlotFor(capture.name, rec)
|
||||
@ -2530,6 +2603,11 @@ class Compiler(
|
||||
}
|
||||
return BlockStatement(stmt.block, newPlan, stmt.captureSlots, stmt.pos)
|
||||
}
|
||||
fun stripCatchCaptures(block: Statement): Statement {
|
||||
val stmt = block as? BlockStatement ?: return block
|
||||
if (stmt.captureSlots.isEmpty()) return stmt
|
||||
return BlockStatement(stmt.block, stmt.slotPlan, emptyList(), stmt.pos)
|
||||
}
|
||||
|
||||
val body = unwrapBytecodeDeep(parseBlock())
|
||||
val catches = mutableListOf<CatchBlockData>()
|
||||
@ -2572,7 +2650,12 @@ class Compiler(
|
||||
val block = try {
|
||||
resolutionSink?.enterScope(ScopeKind.BLOCK, catchVar.pos, null)
|
||||
resolutionSink?.declareSymbol(catchVar.value, SymbolKind.LOCAL, isMutable = false, pos = catchVar.pos)
|
||||
withCatchSlot(unwrapBytecodeDeep(parseBlockWithPredeclared(listOf(catchVar.value to false))), catchVar.value)
|
||||
stripCatchCaptures(
|
||||
withCatchSlot(
|
||||
unwrapBytecodeDeep(parseBlockWithPredeclared(listOf(catchVar.value to false))),
|
||||
catchVar.value
|
||||
)
|
||||
)
|
||||
} finally {
|
||||
resolutionSink?.exitScope(cc.currentPos())
|
||||
}
|
||||
@ -2586,9 +2669,11 @@ class Compiler(
|
||||
val block = try {
|
||||
resolutionSink?.enterScope(ScopeKind.BLOCK, itToken.pos, null)
|
||||
resolutionSink?.declareSymbol(itToken.value, SymbolKind.LOCAL, isMutable = false, pos = itToken.pos)
|
||||
withCatchSlot(
|
||||
unwrapBytecodeDeep(parseBlockWithPredeclared(listOf(itToken.value to false), skipLeadingBrace = true)),
|
||||
itToken.value
|
||||
stripCatchCaptures(
|
||||
withCatchSlot(
|
||||
unwrapBytecodeDeep(parseBlockWithPredeclared(listOf(itToken.value to false), skipLeadingBrace = true)),
|
||||
itToken.value
|
||||
)
|
||||
)
|
||||
} finally {
|
||||
resolutionSink?.exitScope(cc.currentPos())
|
||||
@ -2860,6 +2945,7 @@ class Compiler(
|
||||
pendingDeclStart = null
|
||||
resolutionSink?.declareSymbol(nameToken.value, SymbolKind.CLASS, isMutable = false, pos = nameToken.pos)
|
||||
return inCodeContext(CodeContext.ClassBody(nameToken.value, isExtern = isExtern)) {
|
||||
val classCtx = codeContexts.lastOrNull() as? CodeContext.ClassBody
|
||||
val constructorArgsDeclaration =
|
||||
if (cc.skipTokenOfType(Token.Type.LPAREN, isOptional = true))
|
||||
parseArgsDeclaration(isClassDeclaration = true)
|
||||
@ -2892,6 +2978,11 @@ class Compiler(
|
||||
cc.skipTokenOfType(Token.Type.NEWLINE, isOptional = true)
|
||||
|
||||
pushInitScope()
|
||||
constructorArgsDeclaration?.params?.forEach { param ->
|
||||
if (param.accessType != null) {
|
||||
classCtx?.declaredMembers?.add(param.name)
|
||||
}
|
||||
}
|
||||
|
||||
// Robust body detection: peek next non-whitespace token; if it's '{', consume and parse the body
|
||||
var classBodyRange: MiniRange? = null
|
||||
@ -2945,6 +3036,7 @@ class Compiler(
|
||||
resolutionSink?.declareSymbol(param.name, kind, mutable, param.pos)
|
||||
}
|
||||
val st = try {
|
||||
classCtx?.let { predeclareClassMembers(it.declaredMembers) }
|
||||
withLocalNames(constructorArgsDeclaration?.params?.map { it.name }?.toSet() ?: emptySet()) {
|
||||
parseScript()
|
||||
}
|
||||
|
||||
@ -407,6 +407,12 @@ open class Scope(
|
||||
|
||||
fun updateSlotFor(name: String, record: ObjRecord) {
|
||||
nameToSlot[name]?.let { slots[it] = record }
|
||||
if (objects[name] == null) {
|
||||
objects[name] = record
|
||||
}
|
||||
if (localBindings[name] == null) {
|
||||
localBindings[name] = record
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -80,6 +80,22 @@ class BytecodeCompiler(
|
||||
is net.sergeych.lyng.ForInStatement -> compileForIn(name, stmt)
|
||||
is net.sergeych.lyng.DoWhileStatement -> compileDoWhile(name, stmt)
|
||||
is net.sergeych.lyng.WhileStatement -> compileWhile(name, stmt)
|
||||
is net.sergeych.lyng.WhenStatement -> {
|
||||
val value = compileWhen(stmt, true) ?: return null
|
||||
builder.emit(Opcode.RET, value.slot)
|
||||
val localCount = maxOf(nextSlot, value.slot + 1) - scopeSlotCount
|
||||
builder.build(
|
||||
name,
|
||||
localCount,
|
||||
addrCount = nextAddrSlot,
|
||||
returnLabels = returnLabels,
|
||||
scopeSlotIndices,
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
localSlotNames,
|
||||
localSlotMutables
|
||||
)
|
||||
}
|
||||
is BlockStatement -> compileBlock(name, stmt)
|
||||
is VarDeclStatement -> compileVarDecl(name, stmt)
|
||||
is net.sergeych.lyng.ThrowStatement -> compileThrowStatement(name, stmt)
|
||||
|
||||
@ -2184,7 +2184,6 @@ class ScriptTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Ignore("incremental enable")
|
||||
@Test
|
||||
fun testAccessEHData() = runTest {
|
||||
eval(
|
||||
@ -2207,7 +2206,6 @@ class ScriptTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Ignore("incremental enable")
|
||||
@Test
|
||||
fun testTryFinally() = runTest {
|
||||
val c = Scope()
|
||||
@ -2231,7 +2229,6 @@ class ScriptTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Ignore("incremental enable")
|
||||
@Test
|
||||
fun testThrowFromKotlin() = runTest {
|
||||
val c = Script.newScope()
|
||||
@ -2256,7 +2253,6 @@ class ScriptTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Ignore("incremental enable")
|
||||
@Test
|
||||
fun testReturnValue1() = runTest {
|
||||
val r = eval(
|
||||
@ -2278,7 +2274,6 @@ class ScriptTest {
|
||||
assertEquals("111", r.toString())
|
||||
}
|
||||
|
||||
@Ignore("incremental enable")
|
||||
@Test
|
||||
fun doWhileValuesTest() = runTest {
|
||||
eval(
|
||||
@ -2323,7 +2318,6 @@ class ScriptTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Ignore("incremental enable")
|
||||
@Test
|
||||
fun doWhileValuesLabelTest() = runTest {
|
||||
withTimeout(5.seconds) {
|
||||
@ -2357,7 +2351,6 @@ class ScriptTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore("incremental enable")
|
||||
@Test
|
||||
fun testSimpleWhen() = runTest {
|
||||
eval(
|
||||
@ -2382,7 +2375,6 @@ class ScriptTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Ignore("incremental enable")
|
||||
@Test
|
||||
fun testWhenIs() = runTest {
|
||||
eval(
|
||||
@ -2413,7 +2405,6 @@ class ScriptTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Ignore("incremental enable")
|
||||
@Test
|
||||
fun testWhenIn() = runTest {
|
||||
eval(
|
||||
@ -2453,7 +2444,6 @@ class ScriptTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Ignore("incremental enable")
|
||||
@Test
|
||||
fun testParseSpecialVars() {
|
||||
val l = parseLyng("$~".toSource("test$~"))
|
||||
@ -2462,7 +2452,6 @@ class ScriptTest {
|
||||
assertEquals("$~", l[0].value)
|
||||
}
|
||||
|
||||
@Ignore("incremental enable")
|
||||
@Test
|
||||
fun testMatchOperator() = runTest {
|
||||
eval(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user