Add list literal opcode and bytecode wrappers
This commit is contained in:
parent
aebe0890d8
commit
a4fc5ac6d5
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 Sergey S. Chernov
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.sergeych.lyng
|
||||||
|
|
||||||
|
import net.sergeych.lyng.obj.Obj
|
||||||
|
|
||||||
|
class ClassDeclStatement(
|
||||||
|
private val delegate: Statement,
|
||||||
|
private val startPos: Pos,
|
||||||
|
) : Statement() {
|
||||||
|
override val pos: Pos = startPos
|
||||||
|
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
return delegate.execute(scope)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -251,6 +251,10 @@ class Compiler(
|
|||||||
val statements = mutableListOf<Statement>()
|
val statements = mutableListOf<Statement>()
|
||||||
val start = cc.currentPos()
|
val start = cc.currentPos()
|
||||||
// Track locals at script level for fast local refs
|
// Track locals at script level for fast local refs
|
||||||
|
val needsSlotPlan = slotPlanStack.isEmpty()
|
||||||
|
if (needsSlotPlan) {
|
||||||
|
slotPlanStack.add(SlotPlan(mutableMapOf(), 0))
|
||||||
|
}
|
||||||
return try {
|
return try {
|
||||||
withLocalNames(emptySet()) {
|
withLocalNames(emptySet()) {
|
||||||
// package level declarations
|
// package level declarations
|
||||||
@ -360,6 +364,9 @@ class Compiler(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
if (needsSlotPlan) {
|
||||||
|
slotPlanStack.removeLast()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,9 +394,51 @@ class Compiler(
|
|||||||
private val currentRangeParamNames: Set<String>
|
private val currentRangeParamNames: Set<String>
|
||||||
get() = rangeParamNamesStack.lastOrNull() ?: emptySet()
|
get() = rangeParamNamesStack.lastOrNull() ?: emptySet()
|
||||||
|
|
||||||
|
private fun containsLoopControl(stmt: Statement, inLoop: Boolean = false): Boolean {
|
||||||
|
val target = if (stmt is BytecodeStatement) stmt.original else stmt
|
||||||
|
return when (target) {
|
||||||
|
is BreakStatement, is ContinueStatement -> !inLoop
|
||||||
|
is IfStatement -> {
|
||||||
|
containsLoopControl(target.ifBody, inLoop) ||
|
||||||
|
(target.elseBody?.let { containsLoopControl(it, inLoop) } ?: false)
|
||||||
|
}
|
||||||
|
is ForInStatement -> {
|
||||||
|
containsLoopControl(target.body, true) ||
|
||||||
|
(target.elseStatement?.let { containsLoopControl(it, inLoop) } ?: false)
|
||||||
|
}
|
||||||
|
is WhileStatement -> {
|
||||||
|
containsLoopControl(target.body, true) ||
|
||||||
|
(target.elseStatement?.let { containsLoopControl(it, inLoop) } ?: false)
|
||||||
|
}
|
||||||
|
is DoWhileStatement -> {
|
||||||
|
containsLoopControl(target.body, true) ||
|
||||||
|
(target.elseStatement?.let { containsLoopControl(it, inLoop) } ?: false)
|
||||||
|
}
|
||||||
|
is BlockStatement -> target.statements().any { containsLoopControl(it, inLoop) }
|
||||||
|
is VarDeclStatement -> target.initializer?.let { containsLoopControl(it, inLoop) } ?: false
|
||||||
|
is ReturnStatement, is ThrowStatement, is ExpressionStatement -> false
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun wrapBytecode(stmt: Statement): Statement {
|
private fun wrapBytecode(stmt: Statement): Statement {
|
||||||
if (!useBytecodeStatements) return stmt
|
if (!useBytecodeStatements) return stmt
|
||||||
val allowLocals = codeContexts.lastOrNull() is CodeContext.Function
|
if (codeContexts.lastOrNull() is CodeContext.ClassBody) {
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
if (stmt is FunctionDeclStatement ||
|
||||||
|
stmt is ClassDeclStatement ||
|
||||||
|
stmt is EnumDeclStatement ||
|
||||||
|
stmt is BreakStatement ||
|
||||||
|
stmt is ContinueStatement ||
|
||||||
|
stmt is ReturnStatement
|
||||||
|
) {
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
if (containsLoopControl(stmt)) {
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
val allowLocals = codeContexts.lastOrNull() !is CodeContext.ClassBody
|
||||||
val returnLabels = returnLabelStack.lastOrNull() ?: emptySet()
|
val returnLabels = returnLabelStack.lastOrNull() ?: emptySet()
|
||||||
return BytecodeStatement.wrap(
|
return BytecodeStatement.wrap(
|
||||||
stmt,
|
stmt,
|
||||||
@ -738,11 +787,7 @@ class Compiler(
|
|||||||
isCall = true
|
isCall = true
|
||||||
val lambda = parseLambdaExpression()
|
val lambda = parseLambdaExpression()
|
||||||
val argPos = next.pos
|
val argPos = next.pos
|
||||||
val argStmt = object : Statement() {
|
val args = listOf(ParsedArgument(ExpressionStatement(lambda, argPos), next.pos))
|
||||||
override val pos: Pos = argPos
|
|
||||||
override suspend fun execute(scope: Scope): Obj = lambda.get(scope).value
|
|
||||||
}
|
|
||||||
val args = listOf(ParsedArgument(argStmt, next.pos))
|
|
||||||
operand = when (left) {
|
operand = when (left) {
|
||||||
is LocalVarRef -> if (left.name == "this") {
|
is LocalVarRef -> if (left.name == "this") {
|
||||||
ThisMethodSlotCallRef(next.value, args, true, isOptional)
|
ThisMethodSlotCallRef(next.value, args, true, isOptional)
|
||||||
@ -1421,11 +1466,7 @@ class Compiler(
|
|||||||
if (next.type == Token.Type.COMMA || next.type == Token.Type.RPAREN) {
|
if (next.type == Token.Type.COMMA || next.type == Token.Type.RPAREN) {
|
||||||
val localVar = LocalVarRef(name, t1.pos)
|
val localVar = LocalVarRef(name, t1.pos)
|
||||||
val argPos = t1.pos
|
val argPos = t1.pos
|
||||||
val argStmt = object : Statement() {
|
return ParsedArgument(ExpressionStatement(localVar, argPos), t1.pos, isSplat = false, name = name)
|
||||||
override val pos: Pos = argPos
|
|
||||||
override suspend fun execute(scope: Scope): Obj = localVar.evalValue(scope)
|
|
||||||
}
|
|
||||||
return ParsedArgument(argStmt, t1.pos, isSplat = false, name = name)
|
|
||||||
}
|
}
|
||||||
val rhs = parseExpression() ?: t2.raiseSyntax("expected expression after named argument '${name}:'")
|
val rhs = parseExpression() ?: t2.raiseSyntax("expected expression after named argument '${name}:'")
|
||||||
return ParsedArgument(rhs, t1.pos, isSplat = false, name = name)
|
return ParsedArgument(rhs, t1.pos, isSplat = false, name = name)
|
||||||
@ -1468,11 +1509,7 @@ class Compiler(
|
|||||||
// last argument - callable
|
// last argument - callable
|
||||||
val callableAccessor = parseLambdaExpression()
|
val callableAccessor = parseLambdaExpression()
|
||||||
args += ParsedArgument(
|
args += ParsedArgument(
|
||||||
// transform ObjRef to the callable value
|
ExpressionStatement(callableAccessor, end.pos),
|
||||||
object : Statement() {
|
|
||||||
override val pos: Pos = end.pos
|
|
||||||
override suspend fun execute(scope: Scope): Obj = callableAccessor.get(scope).value
|
|
||||||
},
|
|
||||||
end.pos
|
end.pos
|
||||||
)
|
)
|
||||||
lastBlockArgument = true
|
lastBlockArgument = true
|
||||||
@ -1499,11 +1536,7 @@ class Compiler(
|
|||||||
if (next.type == Token.Type.COMMA || next.type == Token.Type.RPAREN) {
|
if (next.type == Token.Type.COMMA || next.type == Token.Type.RPAREN) {
|
||||||
val localVar = LocalVarRef(name, t1.pos)
|
val localVar = LocalVarRef(name, t1.pos)
|
||||||
val argPos = t1.pos
|
val argPos = t1.pos
|
||||||
val argStmt = object : Statement() {
|
return ParsedArgument(ExpressionStatement(localVar, argPos), t1.pos, isSplat = false, name = name)
|
||||||
override val pos: Pos = argPos
|
|
||||||
override suspend fun execute(scope: Scope): Obj = localVar.evalValue(scope)
|
|
||||||
}
|
|
||||||
return ParsedArgument(argStmt, t1.pos, isSplat = false, name = name)
|
|
||||||
}
|
}
|
||||||
val rhs = parseExpression() ?: t2.raiseSyntax("expected expression after named argument '${name}:'")
|
val rhs = parseExpression() ?: t2.raiseSyntax("expected expression after named argument '${name}:'")
|
||||||
return ParsedArgument(rhs, t1.pos, isSplat = false, name = name)
|
return ParsedArgument(rhs, t1.pos, isSplat = false, name = name)
|
||||||
@ -1555,11 +1588,7 @@ class Compiler(
|
|||||||
// into the lambda body. This ensures expected order:
|
// into the lambda body. This ensures expected order:
|
||||||
// foo { ... }.bar() == (foo { ... }).bar()
|
// foo { ... }.bar() == (foo { ... }).bar()
|
||||||
val callableAccessor = parseLambdaExpression()
|
val callableAccessor = parseLambdaExpression()
|
||||||
val argStmt = object : Statement() {
|
listOf(ParsedArgument(ExpressionStatement(callableAccessor, cc.currentPos()), cc.currentPos()))
|
||||||
override val pos: Pos = cc.currentPos()
|
|
||||||
override suspend fun execute(scope: Scope): Obj = callableAccessor.get(scope).value
|
|
||||||
}
|
|
||||||
listOf(ParsedArgument(argStmt, cc.currentPos()))
|
|
||||||
} else {
|
} else {
|
||||||
val r = parseArgs()
|
val r = parseArgs()
|
||||||
detectedBlockArgument = r.second
|
detectedBlockArgument = r.second
|
||||||
@ -2320,7 +2349,7 @@ class Compiler(
|
|||||||
)
|
)
|
||||||
|
|
||||||
val stmtPos = startPos
|
val stmtPos = startPos
|
||||||
return object : Statement() {
|
val enumDeclStatement = object : Statement() {
|
||||||
override val pos: Pos = stmtPos
|
override val pos: Pos = stmtPos
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
val enumClass = ObjEnumClass.createSimpleEnum(nameToken.value, names)
|
val enumClass = ObjEnumClass.createSimpleEnum(nameToken.value, names)
|
||||||
@ -2328,6 +2357,7 @@ class Compiler(
|
|||||||
return enumClass
|
return enumClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return EnumDeclStatement(enumDeclStatement, stmtPos)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun parseObjectDeclaration(isExtern: Boolean = false): Statement {
|
private suspend fun parseObjectDeclaration(isExtern: Boolean = false): Statement {
|
||||||
@ -2586,7 +2616,7 @@ class Compiler(
|
|||||||
return instance
|
return instance
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
object : Statement() {
|
val classDeclStatement = object : Statement() {
|
||||||
override val pos: Pos = startPos
|
override val pos: Pos = startPos
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
// the main statement should create custom ObjClass instance with field
|
// the main statement should create custom ObjClass instance with field
|
||||||
@ -2643,6 +2673,7 @@ class Compiler(
|
|||||||
return newClass
|
return newClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ClassDeclStatement(classDeclStatement, startPos)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -3279,11 +3310,12 @@ class Compiler(
|
|||||||
return annotatedFnBody
|
return annotatedFnBody
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val declaredFn = FunctionDeclStatement(fnCreateStatement, start)
|
||||||
if (isStatic) {
|
if (isStatic) {
|
||||||
currentInitScope += fnCreateStatement
|
currentInitScope += declaredFn
|
||||||
NopStatement
|
NopStatement
|
||||||
} else
|
} else
|
||||||
fnCreateStatement
|
declaredFn
|
||||||
}.also {
|
}.also {
|
||||||
val bodyRange = lastParsedBlockRange
|
val bodyRange = lastParsedBlockRange
|
||||||
// Also emit a post-parse MiniFunDecl to be robust in case early emission was skipped by some path
|
// Also emit a post-parse MiniFunDecl to be robust in case early emission was skipped by some path
|
||||||
@ -3823,43 +3855,26 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (extTypeName != null) {
|
||||||
|
val prop = if (getter != null || setter != null) {
|
||||||
|
ObjProperty(name, getter, setter)
|
||||||
|
} else {
|
||||||
|
// Simple val extension with initializer
|
||||||
|
val initExpr = initialExpression ?: throw ScriptError(start, "Extension val must be initialized")
|
||||||
|
ObjProperty(name, initExpr, null)
|
||||||
|
}
|
||||||
|
return ExtensionPropertyDeclStatement(
|
||||||
|
extTypeName = extTypeName,
|
||||||
|
property = prop,
|
||||||
|
visibility = visibility,
|
||||||
|
setterVisibility = setterVisibility,
|
||||||
|
startPos = start
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return object : Statement() {
|
return object : Statement() {
|
||||||
override val pos: Pos = start
|
override val pos: Pos = start
|
||||||
override suspend fun execute(context: Scope): Obj {
|
override suspend fun execute(context: Scope): Obj {
|
||||||
if (extTypeName != null) {
|
|
||||||
val prop = if (getter != null || setter != null) {
|
|
||||||
ObjProperty(name, getter, setter)
|
|
||||||
} else {
|
|
||||||
// Simple val extension with initializer
|
|
||||||
val initExpr = initialExpression ?: throw ScriptError(start, "Extension val must be initialized")
|
|
||||||
ObjProperty(
|
|
||||||
name,
|
|
||||||
object : Statement() {
|
|
||||||
override val pos: Pos = initExpr.pos
|
|
||||||
override suspend fun execute(scp: Scope): Obj = initExpr.execute(scp)
|
|
||||||
},
|
|
||||||
null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val type = context[extTypeName]?.value ?: context.raiseSymbolNotFound("class $extTypeName not found")
|
|
||||||
if (type !is ObjClass) context.raiseClassCastError("$extTypeName is not the class instance")
|
|
||||||
|
|
||||||
context.addExtension(
|
|
||||||
type,
|
|
||||||
name,
|
|
||||||
ObjRecord(
|
|
||||||
prop,
|
|
||||||
isMutable = false,
|
|
||||||
visibility = visibility,
|
|
||||||
writeVisibility = setterVisibility,
|
|
||||||
declaringClass = null,
|
|
||||||
type = ObjRecord.Type.Property
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return prop
|
|
||||||
}
|
|
||||||
// In true class bodies (not inside a function), store fields under a class-qualified key to support MI collisions
|
// In true class bodies (not inside a function), store fields under a class-qualified key to support MI collisions
|
||||||
// Do NOT infer declaring class from runtime thisObj here; only the compile-time captured
|
// Do NOT infer declaring class from runtime thisObj here; only the compile-time captured
|
||||||
// ClassBody qualifies for class-field storage. Otherwise, this is a plain local.
|
// ClassBody qualifies for class-field storage. Otherwise, this is a plain local.
|
||||||
|
|||||||
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 Sergey S. Chernov
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.sergeych.lyng
|
||||||
|
|
||||||
|
import net.sergeych.lyng.obj.Obj
|
||||||
|
|
||||||
|
class EnumDeclStatement(
|
||||||
|
private val delegate: Statement,
|
||||||
|
private val startPos: Pos,
|
||||||
|
) : Statement() {
|
||||||
|
override val pos: Pos = startPos
|
||||||
|
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
return delegate.execute(scope)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 Sergey S. Chernov
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.sergeych.lyng
|
||||||
|
|
||||||
|
import net.sergeych.lyng.obj.Obj
|
||||||
|
import net.sergeych.lyng.obj.ObjClass
|
||||||
|
import net.sergeych.lyng.obj.ObjProperty
|
||||||
|
import net.sergeych.lyng.obj.ObjRecord
|
||||||
|
|
||||||
|
class ExtensionPropertyDeclStatement(
|
||||||
|
val extTypeName: String,
|
||||||
|
val property: ObjProperty,
|
||||||
|
val visibility: Visibility,
|
||||||
|
val setterVisibility: Visibility?,
|
||||||
|
private val startPos: Pos,
|
||||||
|
) : Statement() {
|
||||||
|
override val pos: Pos = startPos
|
||||||
|
|
||||||
|
override suspend fun execute(context: Scope): Obj {
|
||||||
|
val type = context[extTypeName]?.value ?: context.raiseSymbolNotFound("class $extTypeName not found")
|
||||||
|
if (type !is ObjClass) context.raiseClassCastError("$extTypeName is not the class instance")
|
||||||
|
context.addExtension(
|
||||||
|
type,
|
||||||
|
property.name,
|
||||||
|
ObjRecord(
|
||||||
|
property,
|
||||||
|
isMutable = false,
|
||||||
|
visibility = visibility,
|
||||||
|
writeVisibility = setterVisibility,
|
||||||
|
declaringClass = null,
|
||||||
|
type = ObjRecord.Type.Property
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return property
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 Sergey S. Chernov
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.sergeych.lyng
|
||||||
|
|
||||||
|
import net.sergeych.lyng.obj.Obj
|
||||||
|
|
||||||
|
class FunctionDeclStatement(
|
||||||
|
private val delegate: Statement,
|
||||||
|
private val startPos: Pos,
|
||||||
|
) : Statement() {
|
||||||
|
override val pos: Pos = startPos
|
||||||
|
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
return delegate.execute(scope)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -31,6 +31,7 @@ sealed class BytecodeConst {
|
|||||||
data class ObjRef(val value: Obj) : BytecodeConst()
|
data class ObjRef(val value: Obj) : BytecodeConst()
|
||||||
data class Ref(val value: net.sergeych.lyng.obj.ObjRef) : BytecodeConst()
|
data class Ref(val value: net.sergeych.lyng.obj.ObjRef) : BytecodeConst()
|
||||||
data class StatementVal(val statement: net.sergeych.lyng.Statement) : BytecodeConst()
|
data class StatementVal(val statement: net.sergeych.lyng.Statement) : BytecodeConst()
|
||||||
|
data class ListLiteralPlan(val spreads: List<Boolean>) : BytecodeConst()
|
||||||
data class SlotPlan(val plan: Map<String, Int>) : BytecodeConst()
|
data class SlotPlan(val plan: Map<String, Int>) : BytecodeConst()
|
||||||
data class ExtensionPropertyDecl(
|
data class ExtensionPropertyDecl(
|
||||||
val extTypeName: String,
|
val extTypeName: String,
|
||||||
|
|||||||
@ -100,6 +100,7 @@ class BytecodeStatement private constructor(
|
|||||||
target.resultExpr?.let { containsUnsupportedStatement(it) } ?: false
|
target.resultExpr?.let { containsUnsupportedStatement(it) } ?: false
|
||||||
is net.sergeych.lyng.ThrowStatement ->
|
is net.sergeych.lyng.ThrowStatement ->
|
||||||
containsUnsupportedStatement(target.throwExpr)
|
containsUnsupportedStatement(target.throwExpr)
|
||||||
|
is net.sergeych.lyng.ExtensionPropertyDeclStatement -> false
|
||||||
else -> true
|
else -> true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -182,6 +182,8 @@ class CmdBuilder {
|
|||||||
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
||||||
Opcode.SET_INDEX ->
|
Opcode.SET_INDEX ->
|
||||||
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
||||||
|
Opcode.LIST_LITERAL ->
|
||||||
|
listOf(OperandKind.CONST, OperandKind.SLOT, OperandKind.COUNT, OperandKind.SLOT)
|
||||||
Opcode.EVAL_FALLBACK, Opcode.EVAL_REF, Opcode.EVAL_STMT ->
|
Opcode.EVAL_FALLBACK, Opcode.EVAL_REF, Opcode.EVAL_STMT ->
|
||||||
listOf(OperandKind.ID, OperandKind.SLOT)
|
listOf(OperandKind.ID, OperandKind.SLOT)
|
||||||
}
|
}
|
||||||
@ -371,6 +373,7 @@ class CmdBuilder {
|
|||||||
Opcode.GET_NAME -> CmdGetName(operands[0], operands[1])
|
Opcode.GET_NAME -> CmdGetName(operands[0], operands[1])
|
||||||
Opcode.GET_INDEX -> CmdGetIndex(operands[0], operands[1], operands[2])
|
Opcode.GET_INDEX -> CmdGetIndex(operands[0], operands[1], operands[2])
|
||||||
Opcode.SET_INDEX -> CmdSetIndex(operands[0], operands[1], operands[2])
|
Opcode.SET_INDEX -> CmdSetIndex(operands[0], operands[1], operands[2])
|
||||||
|
Opcode.LIST_LITERAL -> CmdListLiteral(operands[0], operands[1], operands[2], operands[3])
|
||||||
Opcode.EVAL_FALLBACK -> CmdEvalFallback(operands[0], operands[1])
|
Opcode.EVAL_FALLBACK -> CmdEvalFallback(operands[0], operands[1])
|
||||||
Opcode.EVAL_REF -> CmdEvalRef(operands[0], operands[1])
|
Opcode.EVAL_REF -> CmdEvalRef(operands[0], operands[1])
|
||||||
Opcode.EVAL_STMT -> CmdEvalStmt(operands[0], operands[1])
|
Opcode.EVAL_STMT -> CmdEvalStmt(operands[0], operands[1])
|
||||||
|
|||||||
@ -183,6 +183,7 @@ object CmdDisassembler {
|
|||||||
is CmdGetName -> Opcode.GET_NAME to intArrayOf(cmd.nameId, cmd.dst)
|
is CmdGetName -> Opcode.GET_NAME to intArrayOf(cmd.nameId, cmd.dst)
|
||||||
is CmdGetIndex -> Opcode.GET_INDEX to intArrayOf(cmd.targetSlot, cmd.indexSlot, cmd.dst)
|
is CmdGetIndex -> Opcode.GET_INDEX to intArrayOf(cmd.targetSlot, cmd.indexSlot, cmd.dst)
|
||||||
is CmdSetIndex -> Opcode.SET_INDEX to intArrayOf(cmd.targetSlot, cmd.indexSlot, cmd.valueSlot)
|
is CmdSetIndex -> Opcode.SET_INDEX to intArrayOf(cmd.targetSlot, cmd.indexSlot, cmd.valueSlot)
|
||||||
|
is CmdListLiteral -> Opcode.LIST_LITERAL to intArrayOf(cmd.planId, cmd.baseSlot, cmd.count, cmd.dst)
|
||||||
is CmdEvalFallback -> Opcode.EVAL_FALLBACK to intArrayOf(cmd.id, cmd.dst)
|
is CmdEvalFallback -> Opcode.EVAL_FALLBACK to intArrayOf(cmd.id, cmd.dst)
|
||||||
is CmdEvalRef -> Opcode.EVAL_REF to intArrayOf(cmd.id, cmd.dst)
|
is CmdEvalRef -> Opcode.EVAL_REF to intArrayOf(cmd.id, cmd.dst)
|
||||||
is CmdEvalStmt -> Opcode.EVAL_STMT to intArrayOf(cmd.id, cmd.dst)
|
is CmdEvalStmt -> Opcode.EVAL_STMT to intArrayOf(cmd.id, cmd.dst)
|
||||||
@ -265,6 +266,8 @@ object CmdDisassembler {
|
|||||||
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
||||||
Opcode.SET_INDEX ->
|
Opcode.SET_INDEX ->
|
||||||
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
||||||
|
Opcode.LIST_LITERAL ->
|
||||||
|
listOf(OperandKind.CONST, OperandKind.SLOT, OperandKind.COUNT, OperandKind.SLOT)
|
||||||
Opcode.EVAL_FALLBACK, Opcode.EVAL_REF, Opcode.EVAL_STMT ->
|
Opcode.EVAL_FALLBACK, Opcode.EVAL_REF, Opcode.EVAL_STMT ->
|
||||||
listOf(OperandKind.ID, OperandKind.SLOT)
|
listOf(OperandKind.ID, OperandKind.SLOT)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1223,6 +1223,35 @@ class CmdGetName(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CmdListLiteral(
|
||||||
|
internal val planId: Int,
|
||||||
|
internal val baseSlot: Int,
|
||||||
|
internal val count: Int,
|
||||||
|
internal val dst: Int,
|
||||||
|
) : Cmd() {
|
||||||
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
|
val plan = frame.fn.constants.getOrNull(planId) as? BytecodeConst.ListLiteralPlan
|
||||||
|
?: error("LIST_LITERAL expects ListLiteralPlan at $planId")
|
||||||
|
val list = ArrayList<Obj>(count)
|
||||||
|
for (i in 0 until count) {
|
||||||
|
val value = frame.slotToObj(baseSlot + i)
|
||||||
|
if (plan.spreads.getOrNull(i) == true) {
|
||||||
|
when (value) {
|
||||||
|
is ObjList -> {
|
||||||
|
list.ensureCapacity(list.size + value.list.size)
|
||||||
|
list.addAll(value.list)
|
||||||
|
}
|
||||||
|
else -> frame.scope.raiseError("Spread element must be list")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
list.add(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frame.storeObjResult(dst, ObjList(list))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class CmdSetField(
|
class CmdSetField(
|
||||||
internal val recvSlot: Int,
|
internal val recvSlot: Int,
|
||||||
internal val fieldId: Int,
|
internal val fieldId: Int,
|
||||||
|
|||||||
@ -130,6 +130,7 @@ enum class Opcode(val code: Int) {
|
|||||||
GET_INDEX(0xA2),
|
GET_INDEX(0xA2),
|
||||||
SET_INDEX(0xA3),
|
SET_INDEX(0xA3),
|
||||||
GET_NAME(0xA4),
|
GET_NAME(0xA4),
|
||||||
|
LIST_LITERAL(0xA5),
|
||||||
|
|
||||||
EVAL_FALLBACK(0xB0),
|
EVAL_FALLBACK(0xB0),
|
||||||
RESOLVE_SCOPE_SLOT(0xB1),
|
RESOLVE_SCOPE_SLOT(0xB1),
|
||||||
|
|||||||
@ -1,10 +1,8 @@
|
|||||||
|
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import net.sergeych.lyng.eval
|
import net.sergeych.lyng.eval
|
||||||
import kotlin.test.Ignore
|
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
|
|
||||||
@Ignore("TODO(bytecode-only): uses fallback")
|
|
||||||
class IfNullAssignTest {
|
class IfNullAssignTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@ -21,10 +21,8 @@
|
|||||||
|
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import net.sergeych.lyng.eval
|
import net.sergeych.lyng.eval
|
||||||
import kotlin.test.Ignore
|
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
|
|
||||||
@Ignore("TODO(bytecode-only): uses fallback")
|
|
||||||
class ScriptTest_OptionalAssign {
|
class ScriptTest_OptionalAssign {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@ -249,20 +249,6 @@ fun List.sort() {
|
|||||||
sortWith { a, b -> a <=> b }
|
sortWith { a, b -> a <=> b }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Represents a single stack trace element. */
|
|
||||||
class StackTraceEntry(
|
|
||||||
val sourceName: String,
|
|
||||||
val line: Int,
|
|
||||||
val column: Int,
|
|
||||||
val sourceString: String
|
|
||||||
) {
|
|
||||||
val at by lazy { "%s:%s:%s"(sourceName,line+1,column+1) }
|
|
||||||
/* Formatted representation: source:line:column: text. */
|
|
||||||
override fun toString() {
|
|
||||||
"%s: %s"(at, sourceString.trim())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Print this exception and its stack trace to standard output. */
|
/* Print this exception and its stack trace to standard output. */
|
||||||
fun Exception.printStackTrace() {
|
fun Exception.printStackTrace() {
|
||||||
println(this)
|
println(this)
|
||||||
@ -337,3 +323,17 @@ class lazy(creatorParam) : Delegate {
|
|||||||
value
|
value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Represents a single stack trace element. */
|
||||||
|
class StackTraceEntry(
|
||||||
|
val sourceName: String,
|
||||||
|
val line: Int,
|
||||||
|
val column: Int,
|
||||||
|
val sourceString: String
|
||||||
|
) {
|
||||||
|
val at by lazy { "%s:%s:%s"(sourceName,line+1,column+1) }
|
||||||
|
/* Formatted representation: source:line:column: text. */
|
||||||
|
override fun toString() {
|
||||||
|
"%s: %s"(at, sourceString.trim())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user