Add bytecode slot metadata and compile-time mutability
This commit is contained in:
parent
b4598bff98
commit
059e366787
@ -10,6 +10,7 @@ interpreter and fall back to the existing AST execution when needed.
|
|||||||
### Frame metadata
|
### Frame metadata
|
||||||
- localCount: number of local slots for this function (fixed at compile time).
|
- localCount: number of local slots for this function (fixed at compile time).
|
||||||
- argCount: number of arguments passed at call time.
|
- argCount: number of arguments passed at call time.
|
||||||
|
- scopeSlotNames: optional debug names for scope slots (locals/params), aligned to slot mapping.
|
||||||
- argBase = localCount.
|
- argBase = localCount.
|
||||||
|
|
||||||
### Slot layout
|
### Slot layout
|
||||||
@ -25,6 +26,10 @@ slots[localCount .. localCount+argCount-1] arguments
|
|||||||
- param i => slot localCount + i
|
- param i => slot localCount + i
|
||||||
- variadic extra => slot localCount + declaredParamCount + k
|
- variadic extra => slot localCount + declaredParamCount + k
|
||||||
|
|
||||||
|
### Debug metadata (optional)
|
||||||
|
- scopeSlotNames: array sized scopeSlotCount, each entry nullable.
|
||||||
|
- Intended for disassembly/debug tooling; VM semantics do not depend on it.
|
||||||
|
|
||||||
## 2) Slot ID Width
|
## 2) Slot ID Width
|
||||||
|
|
||||||
Per frame, select:
|
Per frame, select:
|
||||||
@ -113,6 +118,13 @@ Note: Any opcode can be compiled to FALLBACK if not implemented in a VM pass.
|
|||||||
- DIV_REAL S, S -> S
|
- DIV_REAL S, S -> S
|
||||||
- NEG_REAL S -> S
|
- NEG_REAL S -> S
|
||||||
|
|
||||||
|
### Arithmetic: OBJ
|
||||||
|
- ADD_OBJ S, S -> S
|
||||||
|
- SUB_OBJ S, S -> S
|
||||||
|
- MUL_OBJ S, S -> S
|
||||||
|
- DIV_OBJ S, S -> S
|
||||||
|
- MOD_OBJ S, S -> S
|
||||||
|
|
||||||
### Bitwise: INT
|
### Bitwise: INT
|
||||||
- AND_INT S, S -> S
|
- AND_INT S, S -> S
|
||||||
- OR_INT S, S -> S
|
- OR_INT S, S -> S
|
||||||
|
|||||||
@ -18,6 +18,7 @@
|
|||||||
package net.sergeych.lyng
|
package net.sergeych.lyng
|
||||||
|
|
||||||
import net.sergeych.lyng.Compiler.Companion.compile
|
import net.sergeych.lyng.Compiler.Companion.compile
|
||||||
|
import net.sergeych.lyng.bytecode.BytecodeStatement
|
||||||
import net.sergeych.lyng.miniast.*
|
import net.sergeych.lyng.miniast.*
|
||||||
import net.sergeych.lyng.obj.*
|
import net.sergeych.lyng.obj.*
|
||||||
import net.sergeych.lyng.pacman.ImportManager
|
import net.sergeych.lyng.pacman.ImportManager
|
||||||
@ -44,8 +45,9 @@ class Compiler(
|
|||||||
private val currentLocalNames: MutableSet<String>?
|
private val currentLocalNames: MutableSet<String>?
|
||||||
get() = localNamesStack.lastOrNull()
|
get() = localNamesStack.lastOrNull()
|
||||||
|
|
||||||
private data class SlotPlan(val slots: MutableMap<String, Int>, var nextIndex: Int)
|
private data class SlotEntry(val index: Int, val isMutable: Boolean, val isDelegated: Boolean)
|
||||||
private data class SlotLocation(val slot: Int, val depth: Int)
|
private data class SlotPlan(val slots: MutableMap<String, SlotEntry>, var nextIndex: Int)
|
||||||
|
private data class SlotLocation(val slot: Int, val depth: Int, val isMutable: Boolean, val isDelegated: Boolean)
|
||||||
private val slotPlanStack = mutableListOf<SlotPlan>()
|
private val slotPlanStack = mutableListOf<SlotPlan>()
|
||||||
|
|
||||||
// Track declared local variables count per function for precise capacity hints
|
// Track declared local variables count per function for precise capacity hints
|
||||||
@ -62,19 +64,20 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun declareLocalName(name: String) {
|
private fun declareLocalName(name: String, isMutable: Boolean, isDelegated: Boolean = false) {
|
||||||
// Add to current function's local set; only count if it was newly added (avoid duplicates)
|
// Add to current function's local set; only count if it was newly added (avoid duplicates)
|
||||||
val added = currentLocalNames?.add(name) == true
|
val added = currentLocalNames?.add(name) == true
|
||||||
if (added && localDeclCountStack.isNotEmpty()) {
|
if (added && localDeclCountStack.isNotEmpty()) {
|
||||||
localDeclCountStack[localDeclCountStack.lastIndex] = currentLocalDeclCount + 1
|
localDeclCountStack[localDeclCountStack.lastIndex] = currentLocalDeclCount + 1
|
||||||
}
|
}
|
||||||
declareSlotName(name)
|
declareSlotName(name, isMutable, isDelegated)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun declareSlotName(name: String) {
|
private fun declareSlotName(name: String, isMutable: Boolean, isDelegated: Boolean) {
|
||||||
|
if (codeContexts.lastOrNull() is CodeContext.ClassBody) return
|
||||||
val plan = slotPlanStack.lastOrNull() ?: return
|
val plan = slotPlanStack.lastOrNull() ?: return
|
||||||
if (plan.slots.containsKey(name)) return
|
if (plan.slots.containsKey(name)) return
|
||||||
plan.slots[name] = plan.nextIndex
|
plan.slots[name] = SlotEntry(plan.nextIndex, isMutable, isDelegated)
|
||||||
plan.nextIndex += 1
|
plan.nextIndex += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,14 +90,38 @@ class Compiler(
|
|||||||
idx++
|
idx++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return SlotPlan(map, idx)
|
val entries = mutableMapOf<String, SlotEntry>()
|
||||||
|
for ((name, index) in map) {
|
||||||
|
entries[name] = SlotEntry(index, isMutable = false, isDelegated = false)
|
||||||
|
}
|
||||||
|
return SlotPlan(entries, idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun markDelegatedSlot(name: String) {
|
||||||
|
val plan = slotPlanStack.lastOrNull() ?: return
|
||||||
|
val entry = plan.slots[name] ?: return
|
||||||
|
if (!entry.isDelegated) {
|
||||||
|
plan.slots[name] = entry.copy(isDelegated = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun slotPlanIndices(plan: SlotPlan): Map<String, Int> {
|
||||||
|
if (plan.slots.isEmpty()) return emptyMap()
|
||||||
|
val result = LinkedHashMap<String, Int>(plan.slots.size)
|
||||||
|
for ((name, entry) in plan.slots) {
|
||||||
|
result[name] = entry.index
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun lookupSlotLocation(name: String): SlotLocation? {
|
private fun lookupSlotLocation(name: String): SlotLocation? {
|
||||||
for (i in slotPlanStack.indices.reversed()) {
|
for (i in slotPlanStack.indices.reversed()) {
|
||||||
val slot = slotPlanStack[i].slots[name] ?: continue
|
val slot = slotPlanStack[i].slots[name] ?: continue
|
||||||
val depth = slotPlanStack.size - 1 - i
|
val depth = slotPlanStack.size - 1 - i
|
||||||
return SlotLocation(slot, depth)
|
if (codeContexts.any { it is CodeContext.ClassBody } && depth > 1) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return SlotLocation(slot.index, depth, slot.isMutable, slot.isDelegated)
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -339,6 +366,12 @@ class Compiler(
|
|||||||
private var lastAnnotation: (suspend (Scope, ObjString, Statement) -> Statement)? = null
|
private var lastAnnotation: (suspend (Scope, ObjString, Statement) -> Statement)? = null
|
||||||
private var isTransientFlag: Boolean = false
|
private var isTransientFlag: Boolean = false
|
||||||
private var lastLabel: String? = null
|
private var lastLabel: String? = null
|
||||||
|
private val useBytecodeStatements: Boolean = true
|
||||||
|
|
||||||
|
private fun wrapBytecode(stmt: Statement): Statement {
|
||||||
|
if (!useBytecodeStatements) return stmt
|
||||||
|
return BytecodeStatement.wrap(stmt, "stmt@${stmt.pos}", allowLocalSlots = true)
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun parseStatement(braceMeansLambda: Boolean = false): Statement? {
|
private suspend fun parseStatement(braceMeansLambda: Boolean = false): Statement? {
|
||||||
lastAnnotation = null
|
lastAnnotation = null
|
||||||
@ -348,16 +381,16 @@ class Compiler(
|
|||||||
val t = cc.next()
|
val t = cc.next()
|
||||||
return when (t.type) {
|
return when (t.type) {
|
||||||
Token.Type.ID, Token.Type.OBJECT -> {
|
Token.Type.ID, Token.Type.OBJECT -> {
|
||||||
parseKeywordStatement(t)
|
parseKeywordStatement(t)?.let { wrapBytecode(it) }
|
||||||
?: run {
|
?: run {
|
||||||
cc.previous()
|
cc.previous()
|
||||||
parseExpression()
|
parseExpression()?.let { wrapBytecode(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Token.Type.PLUS2, Token.Type.MINUS2 -> {
|
Token.Type.PLUS2, Token.Type.MINUS2 -> {
|
||||||
cc.previous()
|
cc.previous()
|
||||||
parseExpression()
|
parseExpression()?.let { wrapBytecode(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
Token.Type.ATLABEL -> {
|
Token.Type.ATLABEL -> {
|
||||||
@ -389,9 +422,9 @@ class Compiler(
|
|||||||
Token.Type.LBRACE -> {
|
Token.Type.LBRACE -> {
|
||||||
cc.previous()
|
cc.previous()
|
||||||
if (braceMeansLambda)
|
if (braceMeansLambda)
|
||||||
parseExpression()
|
parseExpression()?.let { wrapBytecode(it) }
|
||||||
else
|
else
|
||||||
parseBlock()
|
wrapBytecode(parseBlock())
|
||||||
}
|
}
|
||||||
|
|
||||||
Token.Type.RBRACE, Token.Type.RBRACKET -> {
|
Token.Type.RBRACE, Token.Type.RBRACKET -> {
|
||||||
@ -404,7 +437,7 @@ class Compiler(
|
|||||||
else -> {
|
else -> {
|
||||||
// could be expression
|
// could be expression
|
||||||
cc.previous()
|
cc.previous()
|
||||||
parseExpression()
|
parseExpression()?.let { wrapBytecode(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -800,7 +833,7 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
label?.let { cc.labels.remove(it) }
|
label?.let { cc.labels.remove(it) }
|
||||||
|
|
||||||
val paramSlotPlanSnapshot = if (paramSlotPlan.slots.isEmpty()) emptyMap() else paramSlotPlan.slots.toMap()
|
val paramSlotPlanSnapshot = slotPlanIndices(paramSlotPlan)
|
||||||
return ValueFnRef { closureScope ->
|
return ValueFnRef { closureScope ->
|
||||||
val stmt = object : Statement() {
|
val stmt = object : Statement() {
|
||||||
override val pos: Pos = body.pos
|
override val pos: Pos = body.pos
|
||||||
@ -1424,7 +1457,14 @@ class Compiler(
|
|||||||
val slotLoc = lookupSlotLocation(t.value)
|
val slotLoc = lookupSlotLocation(t.value)
|
||||||
val inClassCtx = codeContexts.any { it is CodeContext.ClassBody }
|
val inClassCtx = codeContexts.any { it is CodeContext.ClassBody }
|
||||||
when {
|
when {
|
||||||
slotLoc != null -> LocalSlotRef(t.value, slotLoc.slot, slotLoc.depth, t.pos)
|
slotLoc != null -> LocalSlotRef(
|
||||||
|
t.value,
|
||||||
|
slotLoc.slot,
|
||||||
|
slotLoc.depth,
|
||||||
|
slotLoc.isMutable,
|
||||||
|
slotLoc.isDelegated,
|
||||||
|
t.pos
|
||||||
|
)
|
||||||
PerfFlags.EMIT_FAST_LOCAL_REFS && (currentLocalNames?.contains(t.value) == true) ->
|
PerfFlags.EMIT_FAST_LOCAL_REFS && (currentLocalNames?.contains(t.value) == true) ->
|
||||||
FastLocalVarRef(t.value, t.pos)
|
FastLocalVarRef(t.value, t.pos)
|
||||||
inClassCtx -> ImplicitThisMemberRef(t.value, t.pos)
|
inClassCtx -> ImplicitThisMemberRef(t.value, t.pos)
|
||||||
@ -1798,7 +1838,7 @@ class Compiler(
|
|||||||
private suspend fun parseWhenStatement(): Statement {
|
private suspend fun parseWhenStatement(): Statement {
|
||||||
// has a value, when(value) ?
|
// has a value, when(value) ?
|
||||||
var t = cc.nextNonWhitespace()
|
var t = cc.nextNonWhitespace()
|
||||||
return if (t.type == Token.Type.LPAREN) {
|
val stmt = if (t.type == Token.Type.LPAREN) {
|
||||||
// when(value)
|
// when(value)
|
||||||
val value = parseStatement() ?: throw ScriptError(cc.currentPos(), "when(value) expected")
|
val value = parseStatement() ?: throw ScriptError(cc.currentPos(), "when(value) expected")
|
||||||
cc.skipTokenOfType(Token.Type.RPAREN)
|
cc.skipTokenOfType(Token.Type.RPAREN)
|
||||||
@ -1919,13 +1959,14 @@ class Compiler(
|
|||||||
// when { cond -> ... }
|
// when { cond -> ... }
|
||||||
TODO("when without object is not yet implemented")
|
TODO("when without object is not yet implemented")
|
||||||
}
|
}
|
||||||
|
return wrapBytecode(stmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun parseThrowStatement(start: Pos): Statement {
|
private suspend fun parseThrowStatement(start: Pos): Statement {
|
||||||
val throwStatement = parseStatement() ?: throw ScriptError(cc.currentPos(), "throw object expected")
|
val throwStatement = parseStatement() ?: throw ScriptError(cc.currentPos(), "throw object expected")
|
||||||
// Important: bind the created statement to the position of the `throw` keyword so that
|
// Important: bind the created statement to the position of the `throw` keyword so that
|
||||||
// any raised error reports the correct source location.
|
// any raised error reports the correct source location.
|
||||||
return object : Statement() {
|
val stmt = object : Statement() {
|
||||||
override val pos: Pos = start
|
override val pos: Pos = start
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
var errorObject = throwStatement.execute(scope)
|
var errorObject = throwStatement.execute(scope)
|
||||||
@ -1953,6 +1994,7 @@ class Compiler(
|
|||||||
return ObjVoid
|
return ObjVoid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return wrapBytecode(stmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class CatchBlockData(
|
private data class CatchBlockData(
|
||||||
@ -2185,9 +2227,15 @@ class Compiler(
|
|||||||
miniSink?.onEnterClass(node)
|
miniSink?.onEnterClass(node)
|
||||||
}
|
}
|
||||||
val bodyStart = nextBody.pos
|
val bodyStart = nextBody.pos
|
||||||
val st = withLocalNames(emptySet()) {
|
val classSlotPlan = SlotPlan(mutableMapOf(), 0)
|
||||||
|
slotPlanStack.add(classSlotPlan)
|
||||||
|
val st = try {
|
||||||
|
withLocalNames(emptySet()) {
|
||||||
parseScript()
|
parseScript()
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
slotPlanStack.removeLast()
|
||||||
|
}
|
||||||
val rbTok = cc.next()
|
val rbTok = cc.next()
|
||||||
if (rbTok.type != Token.Type.RBRACE) throw ScriptError(rbTok.pos, "unbalanced braces in object body")
|
if (rbTok.type != Token.Type.RBRACE) throw ScriptError(rbTok.pos, "unbalanced braces in object body")
|
||||||
classBodyRange = MiniRange(bodyStart, rbTok.pos)
|
classBodyRange = MiniRange(bodyStart, rbTok.pos)
|
||||||
@ -2324,9 +2372,15 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
// parse body
|
// parse body
|
||||||
val bodyStart = next.pos
|
val bodyStart = next.pos
|
||||||
val st = withLocalNames(constructorArgsDeclaration?.params?.map { it.name }?.toSet() ?: emptySet()) {
|
val classSlotPlan = SlotPlan(mutableMapOf(), 0)
|
||||||
|
slotPlanStack.add(classSlotPlan)
|
||||||
|
val st = try {
|
||||||
|
withLocalNames(constructorArgsDeclaration?.params?.map { it.name }?.toSet() ?: emptySet()) {
|
||||||
parseScript()
|
parseScript()
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
slotPlanStack.removeLast()
|
||||||
|
}
|
||||||
val rbTok = cc.next()
|
val rbTok = cc.next()
|
||||||
if (rbTok.type != Token.Type.RBRACE) throw ScriptError(rbTok.pos, "unbalanced braces in class body")
|
if (rbTok.type != Token.Type.RBRACE) throw ScriptError(rbTok.pos, "unbalanced braces in class body")
|
||||||
classBodyRange = MiniRange(bodyStart, rbTok.pos)
|
classBodyRange = MiniRange(bodyStart, rbTok.pos)
|
||||||
@ -2480,7 +2534,7 @@ class Compiler(
|
|||||||
val namesForLoop = (currentLocalNames?.toSet() ?: emptySet()) + tVar.value
|
val namesForLoop = (currentLocalNames?.toSet() ?: emptySet()) + tVar.value
|
||||||
val loopSlotPlan = SlotPlan(mutableMapOf(), 0)
|
val loopSlotPlan = SlotPlan(mutableMapOf(), 0)
|
||||||
slotPlanStack.add(loopSlotPlan)
|
slotPlanStack.add(loopSlotPlan)
|
||||||
declareSlotName(tVar.value)
|
declareSlotName(tVar.value, isMutable = true, isDelegated = false)
|
||||||
val (canBreak, body, elseStatement) = try {
|
val (canBreak, body, elseStatement) = try {
|
||||||
withLocalNames(namesForLoop) {
|
withLocalNames(namesForLoop) {
|
||||||
val loopParsed = cc.parseLoop {
|
val loopParsed = cc.parseLoop {
|
||||||
@ -2500,7 +2554,7 @@ class Compiler(
|
|||||||
} finally {
|
} finally {
|
||||||
slotPlanStack.removeLast()
|
slotPlanStack.removeLast()
|
||||||
}
|
}
|
||||||
val loopSlotPlanSnapshot = if (loopSlotPlan.slots.isEmpty()) emptyMap() else loopSlotPlan.slots.toMap()
|
val loopSlotPlanSnapshot = slotPlanIndices(loopSlotPlan)
|
||||||
|
|
||||||
return object : Statement() {
|
return object : Statement() {
|
||||||
override val pos: Pos = body.pos
|
override val pos: Pos = body.pos
|
||||||
@ -2805,7 +2859,7 @@ class Compiler(
|
|||||||
var result: Obj = ObjVoid
|
var result: Obj = ObjVoid
|
||||||
var wasBroken = false
|
var wasBroken = false
|
||||||
while (condition.execute(scope).toBool()) {
|
while (condition.execute(scope).toBool()) {
|
||||||
val loopScope = scope.createChildScope()
|
val loopScope = scope.createChildScope().apply { skipScopeCreation = true }
|
||||||
if (canBreak) {
|
if (canBreak) {
|
||||||
try {
|
try {
|
||||||
result = body.execute(loopScope)
|
result = body.execute(loopScope)
|
||||||
@ -2962,7 +3016,7 @@ class Compiler(
|
|||||||
val t2 = cc.nextNonWhitespace()
|
val t2 = cc.nextNonWhitespace()
|
||||||
|
|
||||||
// we generate different statements: optimization
|
// we generate different statements: optimization
|
||||||
return if (t2.type == Token.Type.ID && t2.value == "else") {
|
val stmt = if (t2.type == Token.Type.ID && t2.value == "else") {
|
||||||
val elseBody =
|
val elseBody =
|
||||||
parseStatement() ?: throw ScriptError(pos, "Bad else statement: expected statement")
|
parseStatement() ?: throw ScriptError(pos, "Bad else statement: expected statement")
|
||||||
IfStatement(condition, ifBody, elseBody, start)
|
IfStatement(condition, ifBody, elseBody, start)
|
||||||
@ -2970,6 +3024,7 @@ class Compiler(
|
|||||||
cc.previous()
|
cc.previous()
|
||||||
IfStatement(condition, ifBody, null, start)
|
IfStatement(condition, ifBody, null, start)
|
||||||
}
|
}
|
||||||
|
return wrapBytecode(stmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun parseFunctionDeclaration(
|
private suspend fun parseFunctionDeclaration(
|
||||||
@ -3115,7 +3170,7 @@ class Compiler(
|
|||||||
|
|
||||||
var closure: Scope? = null
|
var closure: Scope? = null
|
||||||
|
|
||||||
val paramSlotPlanSnapshot = if (paramSlotPlan.slots.isEmpty()) emptyMap() else paramSlotPlan.slots.toMap()
|
val paramSlotPlanSnapshot = slotPlanIndices(paramSlotPlan)
|
||||||
val fnBody = object : Statement() {
|
val fnBody = object : Statement() {
|
||||||
override val pos: Pos = t.pos
|
override val pos: Pos = t.pos
|
||||||
override suspend fun execute(callerContext: Scope): Obj {
|
override suspend fun execute(callerContext: Scope): Obj {
|
||||||
@ -3323,7 +3378,7 @@ class Compiler(
|
|||||||
} finally {
|
} finally {
|
||||||
slotPlanStack.removeLast()
|
slotPlanStack.removeLast()
|
||||||
}
|
}
|
||||||
val planSnapshot = if (blockSlotPlan.slots.isEmpty()) emptyMap() else blockSlotPlan.slots.toMap()
|
val planSnapshot = slotPlanIndices(blockSlotPlan)
|
||||||
val stmt = object : Statement() {
|
val stmt = 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 {
|
||||||
@ -3333,7 +3388,8 @@ class Compiler(
|
|||||||
return block.execute(target)
|
return block.execute(target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return stmt.also {
|
val wrapped = wrapBytecode(stmt)
|
||||||
|
return wrapped.also {
|
||||||
val t1 = cc.next()
|
val t1 = cc.next()
|
||||||
if (t1.type != Token.Type.RBRACE)
|
if (t1.type != Token.Type.RBRACE)
|
||||||
throw ScriptError(t1.pos, "unbalanced braces: expected block body end: }")
|
throw ScriptError(t1.pos, "unbalanced braces: expected block body end: }")
|
||||||
@ -3368,7 +3424,7 @@ class Compiler(
|
|||||||
|
|
||||||
// Register all names in the pattern
|
// Register all names in the pattern
|
||||||
pattern.forEachVariableWithPos { name, namePos ->
|
pattern.forEachVariableWithPos { name, namePos ->
|
||||||
declareLocalName(name)
|
declareLocalName(name, isMutable)
|
||||||
val declRange = MiniRange(namePos, namePos)
|
val declRange = MiniRange(namePos, namePos)
|
||||||
val node = MiniValDecl(
|
val node = MiniValDecl(
|
||||||
range = declRange,
|
range = declRange,
|
||||||
@ -3526,7 +3582,7 @@ class Compiler(
|
|||||||
val effectiveEqToken = if (isProperty) null else eqToken
|
val effectiveEqToken = if (isProperty) null else eqToken
|
||||||
|
|
||||||
// Register the local name at compile time so that subsequent identifiers can be emitted as fast locals
|
// Register the local name at compile time so that subsequent identifiers can be emitted as fast locals
|
||||||
if (!isStatic) declareLocalName(name)
|
if (!isStatic) declareLocalName(name, isMutable)
|
||||||
|
|
||||||
val isDelegate = if (isAbstract || actualExtern) {
|
val isDelegate = if (isAbstract || actualExtern) {
|
||||||
if (!isProperty && (effectiveEqToken?.type == Token.Type.ASSIGN || effectiveEqToken?.type == Token.Type.BY))
|
if (!isProperty && (effectiveEqToken?.type == Token.Type.ASSIGN || effectiveEqToken?.type == Token.Type.BY))
|
||||||
@ -3561,6 +3617,10 @@ class Compiler(
|
|||||||
else parseStatement(true)
|
else parseStatement(true)
|
||||||
?: throw ScriptError(effectiveEqToken!!.pos, "Expected initializer expression")
|
?: throw ScriptError(effectiveEqToken!!.pos, "Expected initializer expression")
|
||||||
|
|
||||||
|
if (!isStatic && isDelegate) {
|
||||||
|
markDelegatedSlot(name)
|
||||||
|
}
|
||||||
|
|
||||||
// Emit MiniValDecl for this declaration (before execution wiring), attach doc if any
|
// Emit MiniValDecl for this declaration (before execution wiring), attach doc if any
|
||||||
run {
|
run {
|
||||||
val declRange = MiniRange(pendingDeclStart ?: start, cc.currentPos())
|
val declRange = MiniRange(pendingDeclStart ?: start, cc.currentPos())
|
||||||
@ -3839,7 +3899,7 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Register the local name so subsequent identifiers can be emitted as fast locals
|
// Register the local name so subsequent identifiers can be emitted as fast locals
|
||||||
if (!isStatic) declareLocalName(name)
|
if (!isStatic) declareLocalName(name, isMutable)
|
||||||
|
|
||||||
if (isDelegate) {
|
if (isDelegate) {
|
||||||
val declaringClassName = declaringClassNameCaptured
|
val declaringClassName = declaringClassNameCaptured
|
||||||
|
|||||||
@ -56,10 +56,22 @@ class BytecodeBuilder {
|
|||||||
return fallbackStatements.lastIndex
|
return fallbackStatements.lastIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
fun build(name: String, localCount: Int): BytecodeFunction {
|
fun build(
|
||||||
|
name: String,
|
||||||
|
localCount: Int,
|
||||||
|
scopeSlotDepths: IntArray = IntArray(0),
|
||||||
|
scopeSlotIndices: IntArray = IntArray(0),
|
||||||
|
scopeSlotNames: Array<String?> = emptyArray()
|
||||||
|
): BytecodeFunction {
|
||||||
|
val scopeSlotCount = scopeSlotDepths.size
|
||||||
|
require(scopeSlotIndices.size == scopeSlotCount) { "scope slot mapping size mismatch" }
|
||||||
|
require(scopeSlotNames.isEmpty() || scopeSlotNames.size == scopeSlotCount) {
|
||||||
|
"scope slot name mapping size mismatch"
|
||||||
|
}
|
||||||
|
val totalSlots = localCount + scopeSlotCount
|
||||||
val slotWidth = when {
|
val slotWidth = when {
|
||||||
localCount < 256 -> 1
|
totalSlots < 256 -> 1
|
||||||
localCount < 65536 -> 2
|
totalSlots < 65536 -> 2
|
||||||
else -> 4
|
else -> 4
|
||||||
}
|
}
|
||||||
val constIdWidth = if (constPool.size < 65536) 2 else 4
|
val constIdWidth = if (constPool.size < 65536) 2 else 4
|
||||||
@ -102,6 +114,10 @@ class BytecodeBuilder {
|
|||||||
return BytecodeFunction(
|
return BytecodeFunction(
|
||||||
name = name,
|
name = name,
|
||||||
localCount = localCount,
|
localCount = localCount,
|
||||||
|
scopeSlotCount = scopeSlotCount,
|
||||||
|
scopeSlotDepths = scopeSlotDepths,
|
||||||
|
scopeSlotIndices = scopeSlotIndices,
|
||||||
|
scopeSlotNames = if (scopeSlotNames.isEmpty()) Array(scopeSlotCount) { null } else scopeSlotNames,
|
||||||
slotWidth = slotWidth,
|
slotWidth = slotWidth,
|
||||||
ipWidth = ipWidth,
|
ipWidth = ipWidth,
|
||||||
constIdWidth = constIdWidth,
|
constIdWidth = constIdWidth,
|
||||||
@ -135,6 +151,7 @@ class BytecodeBuilder {
|
|||||||
Opcode.CMP_GTE_INT_REAL, Opcode.CMP_GTE_REAL_INT, Opcode.CMP_NEQ_INT_REAL, Opcode.CMP_NEQ_REAL_INT,
|
Opcode.CMP_GTE_INT_REAL, Opcode.CMP_GTE_REAL_INT, Opcode.CMP_NEQ_INT_REAL, Opcode.CMP_NEQ_REAL_INT,
|
||||||
Opcode.CMP_EQ_OBJ, Opcode.CMP_NEQ_OBJ, Opcode.CMP_REF_EQ_OBJ, Opcode.CMP_REF_NEQ_OBJ,
|
Opcode.CMP_EQ_OBJ, Opcode.CMP_NEQ_OBJ, Opcode.CMP_REF_EQ_OBJ, Opcode.CMP_REF_NEQ_OBJ,
|
||||||
Opcode.CMP_LT_OBJ, Opcode.CMP_LTE_OBJ, Opcode.CMP_GT_OBJ, Opcode.CMP_GTE_OBJ,
|
Opcode.CMP_LT_OBJ, Opcode.CMP_LTE_OBJ, Opcode.CMP_GT_OBJ, Opcode.CMP_GTE_OBJ,
|
||||||
|
Opcode.ADD_OBJ, Opcode.SUB_OBJ, Opcode.MUL_OBJ, Opcode.DIV_OBJ, Opcode.MOD_OBJ,
|
||||||
Opcode.AND_BOOL, Opcode.OR_BOOL ->
|
Opcode.AND_BOOL, Opcode.OR_BOOL ->
|
||||||
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
||||||
Opcode.INC_INT, Opcode.DEC_INT, Opcode.RET ->
|
Opcode.INC_INT, Opcode.DEC_INT, Opcode.RET ->
|
||||||
|
|||||||
@ -23,12 +23,21 @@ import net.sergeych.lyng.Statement
|
|||||||
import net.sergeych.lyng.ToBoolStatement
|
import net.sergeych.lyng.ToBoolStatement
|
||||||
import net.sergeych.lyng.obj.*
|
import net.sergeych.lyng.obj.*
|
||||||
|
|
||||||
class BytecodeCompiler {
|
class BytecodeCompiler(
|
||||||
private val builder = BytecodeBuilder()
|
private val allowLocalSlots: Boolean = true,
|
||||||
|
) {
|
||||||
|
private var builder = BytecodeBuilder()
|
||||||
private var nextSlot = 0
|
private var nextSlot = 0
|
||||||
|
private var scopeSlotCount = 0
|
||||||
|
private var scopeSlotDepths = IntArray(0)
|
||||||
|
private var scopeSlotIndices = IntArray(0)
|
||||||
|
private var scopeSlotNames = emptyArray<String?>()
|
||||||
|
private val scopeSlotMap = LinkedHashMap<ScopeSlotKey, Int>()
|
||||||
|
private val scopeSlotNameMap = LinkedHashMap<ScopeSlotKey, String>()
|
||||||
private val slotTypes = mutableMapOf<Int, SlotType>()
|
private val slotTypes = mutableMapOf<Int, SlotType>()
|
||||||
|
|
||||||
fun compileStatement(name: String, stmt: net.sergeych.lyng.Statement): BytecodeFunction? {
|
fun compileStatement(name: String, stmt: net.sergeych.lyng.Statement): BytecodeFunction? {
|
||||||
|
prepareCompilation(stmt)
|
||||||
return when (stmt) {
|
return when (stmt) {
|
||||||
is ExpressionStatement -> compileExpression(name, stmt)
|
is ExpressionStatement -> compileExpression(name, stmt)
|
||||||
is net.sergeych.lyng.IfStatement -> compileIf(name, stmt)
|
is net.sergeych.lyng.IfStatement -> compileIf(name, stmt)
|
||||||
@ -37,10 +46,11 @@ class BytecodeCompiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun compileExpression(name: String, stmt: ExpressionStatement): BytecodeFunction? {
|
fun compileExpression(name: String, stmt: ExpressionStatement): BytecodeFunction? {
|
||||||
|
prepareCompilation(stmt)
|
||||||
val value = compileRefWithFallback(stmt.ref, null, stmt.pos) ?: return null
|
val value = compileRefWithFallback(stmt.ref, null, stmt.pos) ?: return null
|
||||||
builder.emit(Opcode.RET, value.slot)
|
builder.emit(Opcode.RET, value.slot)
|
||||||
val localCount = maxOf(nextSlot, value.slot + 1)
|
val localCount = maxOf(nextSlot, value.slot + 1) - scopeSlotCount
|
||||||
return builder.build(name, localCount)
|
return builder.build(name, localCount, scopeSlotDepths, scopeSlotIndices, scopeSlotNames)
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class CompiledValue(val slot: Int, val type: SlotType)
|
private data class CompiledValue(val slot: Int, val type: SlotType)
|
||||||
@ -51,9 +61,11 @@ class BytecodeCompiler {
|
|||||||
return when (ref) {
|
return when (ref) {
|
||||||
is ConstRef -> compileConst(ref.constValue)
|
is ConstRef -> compileConst(ref.constValue)
|
||||||
is LocalSlotRef -> {
|
is LocalSlotRef -> {
|
||||||
|
if (!allowLocalSlots) return null
|
||||||
|
if (ref.isDelegated) return null
|
||||||
if (ref.name.isEmpty()) return null
|
if (ref.name.isEmpty()) return null
|
||||||
if (refDepth(ref) != 0) return null
|
val mapped = scopeSlotMap[ScopeSlotKey(refDepth(ref), refSlot(ref))] ?: return null
|
||||||
CompiledValue(refSlot(ref), slotTypes[refSlot(ref)] ?: SlotType.UNKNOWN)
|
CompiledValue(mapped, slotTypes[mapped] ?: SlotType.UNKNOWN)
|
||||||
}
|
}
|
||||||
is BinaryOpRef -> compileBinary(ref)
|
is BinaryOpRef -> compileBinary(ref)
|
||||||
is UnaryOpRef -> compileUnary(ref)
|
is UnaryOpRef -> compileUnary(ref)
|
||||||
@ -146,6 +158,7 @@ class BytecodeCompiler {
|
|||||||
CompiledValue(out, SlotType.INT)
|
CompiledValue(out, SlotType.INT)
|
||||||
}
|
}
|
||||||
SlotType.REAL -> compileRealArithmeticWithCoercion(Opcode.ADD_REAL, a, b, out)
|
SlotType.REAL -> compileRealArithmeticWithCoercion(Opcode.ADD_REAL, a, b, out)
|
||||||
|
SlotType.OBJ -> null
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -156,9 +169,15 @@ class BytecodeCompiler {
|
|||||||
CompiledValue(out, SlotType.REAL)
|
CompiledValue(out, SlotType.REAL)
|
||||||
}
|
}
|
||||||
SlotType.INT -> compileRealArithmeticWithCoercion(Opcode.ADD_REAL, a, b, out)
|
SlotType.INT -> compileRealArithmeticWithCoercion(Opcode.ADD_REAL, a, b, out)
|
||||||
|
SlotType.OBJ -> null
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SlotType.OBJ -> {
|
||||||
|
if (b.type != SlotType.OBJ) return null
|
||||||
|
builder.emit(Opcode.ADD_OBJ, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.OBJ)
|
||||||
|
}
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
BinOp.MINUS -> when (a.type) {
|
BinOp.MINUS -> when (a.type) {
|
||||||
@ -169,6 +188,7 @@ class BytecodeCompiler {
|
|||||||
CompiledValue(out, SlotType.INT)
|
CompiledValue(out, SlotType.INT)
|
||||||
}
|
}
|
||||||
SlotType.REAL -> compileRealArithmeticWithCoercion(Opcode.SUB_REAL, a, b, out)
|
SlotType.REAL -> compileRealArithmeticWithCoercion(Opcode.SUB_REAL, a, b, out)
|
||||||
|
SlotType.OBJ -> null
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -179,9 +199,15 @@ class BytecodeCompiler {
|
|||||||
CompiledValue(out, SlotType.REAL)
|
CompiledValue(out, SlotType.REAL)
|
||||||
}
|
}
|
||||||
SlotType.INT -> compileRealArithmeticWithCoercion(Opcode.SUB_REAL, a, b, out)
|
SlotType.INT -> compileRealArithmeticWithCoercion(Opcode.SUB_REAL, a, b, out)
|
||||||
|
SlotType.OBJ -> null
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SlotType.OBJ -> {
|
||||||
|
if (b.type != SlotType.OBJ) return null
|
||||||
|
builder.emit(Opcode.SUB_OBJ, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.OBJ)
|
||||||
|
}
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
BinOp.STAR -> when (a.type) {
|
BinOp.STAR -> when (a.type) {
|
||||||
@ -192,6 +218,7 @@ class BytecodeCompiler {
|
|||||||
CompiledValue(out, SlotType.INT)
|
CompiledValue(out, SlotType.INT)
|
||||||
}
|
}
|
||||||
SlotType.REAL -> compileRealArithmeticWithCoercion(Opcode.MUL_REAL, a, b, out)
|
SlotType.REAL -> compileRealArithmeticWithCoercion(Opcode.MUL_REAL, a, b, out)
|
||||||
|
SlotType.OBJ -> null
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -202,9 +229,15 @@ class BytecodeCompiler {
|
|||||||
CompiledValue(out, SlotType.REAL)
|
CompiledValue(out, SlotType.REAL)
|
||||||
}
|
}
|
||||||
SlotType.INT -> compileRealArithmeticWithCoercion(Opcode.MUL_REAL, a, b, out)
|
SlotType.INT -> compileRealArithmeticWithCoercion(Opcode.MUL_REAL, a, b, out)
|
||||||
|
SlotType.OBJ -> null
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SlotType.OBJ -> {
|
||||||
|
if (b.type != SlotType.OBJ) return null
|
||||||
|
builder.emit(Opcode.MUL_OBJ, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.OBJ)
|
||||||
|
}
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
BinOp.SLASH -> when (a.type) {
|
BinOp.SLASH -> when (a.type) {
|
||||||
@ -215,6 +248,7 @@ class BytecodeCompiler {
|
|||||||
CompiledValue(out, SlotType.INT)
|
CompiledValue(out, SlotType.INT)
|
||||||
}
|
}
|
||||||
SlotType.REAL -> compileRealArithmeticWithCoercion(Opcode.DIV_REAL, a, b, out)
|
SlotType.REAL -> compileRealArithmeticWithCoercion(Opcode.DIV_REAL, a, b, out)
|
||||||
|
SlotType.OBJ -> null
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -225,16 +259,32 @@ class BytecodeCompiler {
|
|||||||
CompiledValue(out, SlotType.REAL)
|
CompiledValue(out, SlotType.REAL)
|
||||||
}
|
}
|
||||||
SlotType.INT -> compileRealArithmeticWithCoercion(Opcode.DIV_REAL, a, b, out)
|
SlotType.INT -> compileRealArithmeticWithCoercion(Opcode.DIV_REAL, a, b, out)
|
||||||
|
SlotType.OBJ -> null
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SlotType.OBJ -> {
|
||||||
|
if (b.type != SlotType.OBJ) return null
|
||||||
|
builder.emit(Opcode.DIV_OBJ, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.OBJ)
|
||||||
|
}
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
BinOp.PERCENT -> {
|
BinOp.PERCENT -> {
|
||||||
if (a.type != SlotType.INT) return null
|
return when (a.type) {
|
||||||
|
SlotType.INT -> {
|
||||||
|
if (b.type != SlotType.INT) return null
|
||||||
builder.emit(Opcode.MOD_INT, a.slot, b.slot, out)
|
builder.emit(Opcode.MOD_INT, a.slot, b.slot, out)
|
||||||
CompiledValue(out, SlotType.INT)
|
CompiledValue(out, SlotType.INT)
|
||||||
}
|
}
|
||||||
|
SlotType.OBJ -> {
|
||||||
|
if (b.type != SlotType.OBJ) return null
|
||||||
|
builder.emit(Opcode.MOD_OBJ, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.OBJ)
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
BinOp.EQ -> {
|
BinOp.EQ -> {
|
||||||
compileCompareEq(a, b, out)
|
compileCompareEq(a, b, out)
|
||||||
}
|
}
|
||||||
@ -522,9 +572,11 @@ class BytecodeCompiler {
|
|||||||
|
|
||||||
private fun compileAssign(ref: AssignRef): CompiledValue? {
|
private fun compileAssign(ref: AssignRef): CompiledValue? {
|
||||||
val target = assignTarget(ref) ?: return null
|
val target = assignTarget(ref) ?: return null
|
||||||
if (refDepth(target) != 0) return null
|
if (!allowLocalSlots) return null
|
||||||
|
if (!target.isMutable || target.isDelegated) return null
|
||||||
|
if (refDepth(target) > 0) return null
|
||||||
val value = compileRef(assignValue(ref)) ?: return null
|
val value = compileRef(assignValue(ref)) ?: return null
|
||||||
val slot = refSlot(target)
|
val slot = scopeSlotMap[ScopeSlotKey(refDepth(target), refSlot(target))] ?: return null
|
||||||
when (value.type) {
|
when (value.type) {
|
||||||
SlotType.INT -> builder.emit(Opcode.MOVE_INT, value.slot, slot)
|
SlotType.INT -> builder.emit(Opcode.MOVE_INT, value.slot, slot)
|
||||||
SlotType.REAL -> builder.emit(Opcode.MOVE_REAL, value.slot, slot)
|
SlotType.REAL -> builder.emit(Opcode.MOVE_REAL, value.slot, slot)
|
||||||
@ -563,8 +615,8 @@ class BytecodeCompiler {
|
|||||||
|
|
||||||
builder.mark(endLabel)
|
builder.mark(endLabel)
|
||||||
builder.emit(Opcode.RET, resultSlot)
|
builder.emit(Opcode.RET, resultSlot)
|
||||||
val localCount = maxOf(nextSlot, resultSlot + 1)
|
val localCount = maxOf(nextSlot, resultSlot + 1) - scopeSlotCount
|
||||||
return builder.build(name, localCount)
|
return builder.build(name, localCount, scopeSlotDepths, scopeSlotIndices, scopeSlotNames)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun compileStatementValue(stmt: Statement): CompiledValue? {
|
private fun compileStatementValue(stmt: Statement): CompiledValue? {
|
||||||
@ -620,4 +672,71 @@ class BytecodeCompiler {
|
|||||||
slotTypes[slot] = type
|
slotTypes[slot] = type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun prepareCompilation(stmt: Statement) {
|
||||||
|
builder = BytecodeBuilder()
|
||||||
|
nextSlot = 0
|
||||||
|
slotTypes.clear()
|
||||||
|
scopeSlotMap.clear()
|
||||||
|
if (allowLocalSlots) {
|
||||||
|
collectScopeSlots(stmt)
|
||||||
|
}
|
||||||
|
scopeSlotCount = scopeSlotMap.size
|
||||||
|
scopeSlotDepths = IntArray(scopeSlotCount)
|
||||||
|
scopeSlotIndices = IntArray(scopeSlotCount)
|
||||||
|
scopeSlotNames = arrayOfNulls(scopeSlotCount)
|
||||||
|
for ((key, index) in scopeSlotMap) {
|
||||||
|
scopeSlotDepths[index] = key.depth
|
||||||
|
scopeSlotIndices[index] = key.slot
|
||||||
|
scopeSlotNames[index] = scopeSlotNameMap[key]
|
||||||
|
}
|
||||||
|
nextSlot = scopeSlotCount
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun collectScopeSlots(stmt: Statement) {
|
||||||
|
when (stmt) {
|
||||||
|
is ExpressionStatement -> collectScopeSlotsRef(stmt.ref)
|
||||||
|
is IfStatement -> {
|
||||||
|
collectScopeSlots(stmt.condition)
|
||||||
|
collectScopeSlots(stmt.ifBody)
|
||||||
|
stmt.elseBody?.let { collectScopeSlots(it) }
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun collectScopeSlotsRef(ref: ObjRef) {
|
||||||
|
when (ref) {
|
||||||
|
is LocalSlotRef -> {
|
||||||
|
val key = ScopeSlotKey(refDepth(ref), refSlot(ref))
|
||||||
|
if (!scopeSlotMap.containsKey(key)) {
|
||||||
|
scopeSlotMap[key] = scopeSlotMap.size
|
||||||
|
}
|
||||||
|
if (!scopeSlotNameMap.containsKey(key)) {
|
||||||
|
scopeSlotNameMap[key] = ref.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is BinaryOpRef -> {
|
||||||
|
collectScopeSlotsRef(binaryLeft(ref))
|
||||||
|
collectScopeSlotsRef(binaryRight(ref))
|
||||||
|
}
|
||||||
|
is UnaryOpRef -> collectScopeSlotsRef(unaryOperand(ref))
|
||||||
|
is AssignRef -> {
|
||||||
|
val target = assignTarget(ref)
|
||||||
|
if (target != null) {
|
||||||
|
val key = ScopeSlotKey(refDepth(target), refSlot(target))
|
||||||
|
if (!scopeSlotMap.containsKey(key)) {
|
||||||
|
scopeSlotMap[key] = scopeSlotMap.size
|
||||||
|
}
|
||||||
|
if (!scopeSlotNameMap.containsKey(key)) {
|
||||||
|
scopeSlotNameMap[key] = target.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
collectScopeSlotsRef(assignValue(ref))
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class ScopeSlotKey(val depth: Int, val slot: Int)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,7 +38,8 @@ object BytecodeDisassembler {
|
|||||||
OperandKind.SLOT -> {
|
OperandKind.SLOT -> {
|
||||||
val v = decoder.readSlot(code, ip)
|
val v = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
operands += "s$v"
|
val name = if (v < fn.scopeSlotCount) fn.scopeSlotNames[v] else null
|
||||||
|
operands += if (name != null) "s$v($name)" else "s$v"
|
||||||
}
|
}
|
||||||
OperandKind.CONST -> {
|
OperandKind.CONST -> {
|
||||||
val v = decoder.readConstId(code, ip, fn.constIdWidth)
|
val v = decoder.readConstId(code, ip, fn.constIdWidth)
|
||||||
@ -103,6 +104,7 @@ object BytecodeDisassembler {
|
|||||||
Opcode.CMP_GTE_INT_REAL, Opcode.CMP_GTE_REAL_INT, Opcode.CMP_NEQ_INT_REAL, Opcode.CMP_NEQ_REAL_INT,
|
Opcode.CMP_GTE_INT_REAL, Opcode.CMP_GTE_REAL_INT, Opcode.CMP_NEQ_INT_REAL, Opcode.CMP_NEQ_REAL_INT,
|
||||||
Opcode.CMP_EQ_OBJ, Opcode.CMP_NEQ_OBJ, Opcode.CMP_REF_EQ_OBJ, Opcode.CMP_REF_NEQ_OBJ,
|
Opcode.CMP_EQ_OBJ, Opcode.CMP_NEQ_OBJ, Opcode.CMP_REF_EQ_OBJ, Opcode.CMP_REF_NEQ_OBJ,
|
||||||
Opcode.CMP_LT_OBJ, Opcode.CMP_LTE_OBJ, Opcode.CMP_GT_OBJ, Opcode.CMP_GTE_OBJ,
|
Opcode.CMP_LT_OBJ, Opcode.CMP_LTE_OBJ, Opcode.CMP_GT_OBJ, Opcode.CMP_GTE_OBJ,
|
||||||
|
Opcode.ADD_OBJ, Opcode.SUB_OBJ, Opcode.MUL_OBJ, Opcode.DIV_OBJ, Opcode.MOD_OBJ,
|
||||||
Opcode.AND_BOOL, Opcode.OR_BOOL ->
|
Opcode.AND_BOOL, Opcode.OR_BOOL ->
|
||||||
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
||||||
Opcode.INC_INT, Opcode.DEC_INT, Opcode.RET ->
|
Opcode.INC_INT, Opcode.DEC_INT, Opcode.RET ->
|
||||||
|
|||||||
@ -19,6 +19,10 @@ package net.sergeych.lyng.bytecode
|
|||||||
data class BytecodeFunction(
|
data class BytecodeFunction(
|
||||||
val name: String,
|
val name: String,
|
||||||
val localCount: Int,
|
val localCount: Int,
|
||||||
|
val scopeSlotCount: Int,
|
||||||
|
val scopeSlotDepths: IntArray,
|
||||||
|
val scopeSlotIndices: IntArray,
|
||||||
|
val scopeSlotNames: Array<String?>,
|
||||||
val slotWidth: Int,
|
val slotWidth: Int,
|
||||||
val ipWidth: Int,
|
val ipWidth: Int,
|
||||||
val constIdWidth: Int,
|
val constIdWidth: Int,
|
||||||
@ -30,5 +34,8 @@ data class BytecodeFunction(
|
|||||||
require(slotWidth == 1 || slotWidth == 2 || slotWidth == 4) { "slotWidth must be 1,2,4" }
|
require(slotWidth == 1 || slotWidth == 2 || slotWidth == 4) { "slotWidth must be 1,2,4" }
|
||||||
require(ipWidth == 2 || ipWidth == 4) { "ipWidth must be 2 or 4" }
|
require(ipWidth == 2 || ipWidth == 4) { "ipWidth must be 2 or 4" }
|
||||||
require(constIdWidth == 2 || constIdWidth == 4) { "constIdWidth must be 2 or 4" }
|
require(constIdWidth == 2 || constIdWidth == 4) { "constIdWidth must be 2 or 4" }
|
||||||
|
require(scopeSlotDepths.size == scopeSlotCount) { "scopeSlotDepths size mismatch" }
|
||||||
|
require(scopeSlotIndices.size == scopeSlotCount) { "scopeSlotIndices size mismatch" }
|
||||||
|
require(scopeSlotNames.size == scopeSlotCount) { "scopeSlotNames size mismatch" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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.bytecode
|
||||||
|
|
||||||
|
import net.sergeych.lyng.Pos
|
||||||
|
import net.sergeych.lyng.Scope
|
||||||
|
import net.sergeych.lyng.Statement
|
||||||
|
import net.sergeych.lyng.obj.Obj
|
||||||
|
|
||||||
|
class BytecodeStatement private constructor(
|
||||||
|
val original: Statement,
|
||||||
|
private val function: BytecodeFunction,
|
||||||
|
) : Statement(original.isStaticConst, original.isConst, original.returnType) {
|
||||||
|
override val pos: Pos = original.pos
|
||||||
|
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
return BytecodeVm().execute(function, scope, emptyList())
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun wrap(statement: Statement, nameHint: String, allowLocalSlots: Boolean): Statement {
|
||||||
|
if (statement is BytecodeStatement) return statement
|
||||||
|
val compiler = BytecodeCompiler(allowLocalSlots = allowLocalSlots)
|
||||||
|
val compiled = compiler.compileStatement(nameHint, statement)
|
||||||
|
val fn = compiled ?: run {
|
||||||
|
val builder = BytecodeBuilder()
|
||||||
|
val slot = 0
|
||||||
|
val id = builder.addFallback(statement)
|
||||||
|
builder.emit(Opcode.EVAL_FALLBACK, id, slot)
|
||||||
|
builder.emit(Opcode.RET, slot)
|
||||||
|
builder.build(nameHint, localCount = 1)
|
||||||
|
}
|
||||||
|
return BytecodeStatement(statement, fn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -47,7 +47,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val c = fn.constants[constId] as? BytecodeConst.IntVal
|
val c = fn.constants[constId] as? BytecodeConst.IntVal
|
||||||
?: error("CONST_INT expects IntVal at $constId")
|
?: error("CONST_INT expects IntVal at $constId")
|
||||||
frame.setInt(dst, c.value)
|
setInt(fn, frame, scope, dst, c.value)
|
||||||
}
|
}
|
||||||
Opcode.CONST_REAL -> {
|
Opcode.CONST_REAL -> {
|
||||||
val constId = decoder.readConstId(code, ip, fn.constIdWidth)
|
val constId = decoder.readConstId(code, ip, fn.constIdWidth)
|
||||||
@ -56,7 +56,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val c = fn.constants[constId] as? BytecodeConst.RealVal
|
val c = fn.constants[constId] as? BytecodeConst.RealVal
|
||||||
?: error("CONST_REAL expects RealVal at $constId")
|
?: error("CONST_REAL expects RealVal at $constId")
|
||||||
frame.setReal(dst, c.value)
|
setReal(fn, frame, scope, dst, c.value)
|
||||||
}
|
}
|
||||||
Opcode.CONST_BOOL -> {
|
Opcode.CONST_BOOL -> {
|
||||||
val constId = decoder.readConstId(code, ip, fn.constIdWidth)
|
val constId = decoder.readConstId(code, ip, fn.constIdWidth)
|
||||||
@ -65,7 +65,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val c = fn.constants[constId] as? BytecodeConst.Bool
|
val c = fn.constants[constId] as? BytecodeConst.Bool
|
||||||
?: error("CONST_BOOL expects Bool at $constId")
|
?: error("CONST_BOOL expects Bool at $constId")
|
||||||
frame.setBool(dst, c.value)
|
setBool(fn, frame, scope, dst, c.value)
|
||||||
}
|
}
|
||||||
Opcode.CONST_OBJ -> {
|
Opcode.CONST_OBJ -> {
|
||||||
val constId = decoder.readConstId(code, ip, fn.constIdWidth)
|
val constId = decoder.readConstId(code, ip, fn.constIdWidth)
|
||||||
@ -76,76 +76,76 @@ class BytecodeVm {
|
|||||||
is BytecodeConst.ObjRef -> {
|
is BytecodeConst.ObjRef -> {
|
||||||
val obj = c.value
|
val obj = c.value
|
||||||
when (obj) {
|
when (obj) {
|
||||||
is ObjInt -> frame.setInt(dst, obj.value)
|
is ObjInt -> setInt(fn, frame, scope, dst, obj.value)
|
||||||
is ObjReal -> frame.setReal(dst, obj.value)
|
is ObjReal -> setReal(fn, frame, scope, dst, obj.value)
|
||||||
is ObjBool -> frame.setBool(dst, obj.value)
|
is ObjBool -> setBool(fn, frame, scope, dst, obj.value)
|
||||||
else -> frame.setObj(dst, obj)
|
else -> setObj(fn, frame, scope, dst, obj)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is BytecodeConst.StringVal -> frame.setObj(dst, ObjString(c.value))
|
is BytecodeConst.StringVal -> setObj(fn, frame, scope, dst, ObjString(c.value))
|
||||||
else -> error("CONST_OBJ expects ObjRef/StringVal at $constId")
|
else -> error("CONST_OBJ expects ObjRef/StringVal at $constId")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Opcode.CONST_NULL -> {
|
Opcode.CONST_NULL -> {
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setObj(dst, ObjNull)
|
setObj(fn, frame, scope, dst, ObjNull)
|
||||||
}
|
}
|
||||||
Opcode.MOVE_INT -> {
|
Opcode.MOVE_INT -> {
|
||||||
val src = decoder.readSlot(code, ip)
|
val src = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setInt(dst, frame.getInt(src))
|
setInt(fn, frame, scope, dst, getInt(fn, frame, scope, src))
|
||||||
}
|
}
|
||||||
Opcode.MOVE_REAL -> {
|
Opcode.MOVE_REAL -> {
|
||||||
val src = decoder.readSlot(code, ip)
|
val src = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setReal(dst, frame.getReal(src))
|
setReal(fn, frame, scope, dst, getReal(fn, frame, scope, src))
|
||||||
}
|
}
|
||||||
Opcode.MOVE_BOOL -> {
|
Opcode.MOVE_BOOL -> {
|
||||||
val src = decoder.readSlot(code, ip)
|
val src = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getBool(src))
|
setBool(fn, frame, scope, dst, getBool(fn, frame, scope, src))
|
||||||
}
|
}
|
||||||
Opcode.MOVE_OBJ -> {
|
Opcode.MOVE_OBJ -> {
|
||||||
val src = decoder.readSlot(code, ip)
|
val src = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setObj(dst, frame.getObj(src))
|
setObj(fn, frame, scope, dst, getObj(fn, frame, scope, src))
|
||||||
}
|
}
|
||||||
Opcode.INT_TO_REAL -> {
|
Opcode.INT_TO_REAL -> {
|
||||||
val src = decoder.readSlot(code, ip)
|
val src = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setReal(dst, frame.getInt(src).toDouble())
|
setReal(fn, frame, scope, dst, getInt(fn, frame, scope, src).toDouble())
|
||||||
}
|
}
|
||||||
Opcode.REAL_TO_INT -> {
|
Opcode.REAL_TO_INT -> {
|
||||||
val src = decoder.readSlot(code, ip)
|
val src = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setInt(dst, frame.getReal(src).toLong())
|
setInt(fn, frame, scope, dst, getReal(fn, frame, scope, src).toLong())
|
||||||
}
|
}
|
||||||
Opcode.BOOL_TO_INT -> {
|
Opcode.BOOL_TO_INT -> {
|
||||||
val src = decoder.readSlot(code, ip)
|
val src = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setInt(dst, if (frame.getBool(src)) 1L else 0L)
|
setInt(fn, frame, scope, dst, if (getBool(fn, frame, scope, src)) 1L else 0L)
|
||||||
}
|
}
|
||||||
Opcode.INT_TO_BOOL -> {
|
Opcode.INT_TO_BOOL -> {
|
||||||
val src = decoder.readSlot(code, ip)
|
val src = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getInt(src) != 0L)
|
setBool(fn, frame, scope, dst, getInt(fn, frame, scope, src) != 0L)
|
||||||
}
|
}
|
||||||
Opcode.ADD_INT -> {
|
Opcode.ADD_INT -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -154,7 +154,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setInt(dst, frame.getInt(a) + frame.getInt(b))
|
setInt(fn, frame, scope, dst, getInt(fn, frame, scope, a) + getInt(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.SUB_INT -> {
|
Opcode.SUB_INT -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -163,7 +163,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setInt(dst, frame.getInt(a) - frame.getInt(b))
|
setInt(fn, frame, scope, dst, getInt(fn, frame, scope, a) - getInt(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.MUL_INT -> {
|
Opcode.MUL_INT -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -172,7 +172,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setInt(dst, frame.getInt(a) * frame.getInt(b))
|
setInt(fn, frame, scope, dst, getInt(fn, frame, scope, a) * getInt(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.DIV_INT -> {
|
Opcode.DIV_INT -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -181,7 +181,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setInt(dst, frame.getInt(a) / frame.getInt(b))
|
setInt(fn, frame, scope, dst, getInt(fn, frame, scope, a) / getInt(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.MOD_INT -> {
|
Opcode.MOD_INT -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -190,24 +190,24 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setInt(dst, frame.getInt(a) % frame.getInt(b))
|
setInt(fn, frame, scope, dst, getInt(fn, frame, scope, a) % getInt(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.NEG_INT -> {
|
Opcode.NEG_INT -> {
|
||||||
val src = decoder.readSlot(code, ip)
|
val src = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setInt(dst, -frame.getInt(src))
|
setInt(fn, frame, scope, dst, -getInt(fn, frame, scope, src))
|
||||||
}
|
}
|
||||||
Opcode.INC_INT -> {
|
Opcode.INC_INT -> {
|
||||||
val slot = decoder.readSlot(code, ip)
|
val slot = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setInt(slot, frame.getInt(slot) + 1L)
|
setInt(fn, frame, scope, slot, getInt(fn, frame, scope, slot) + 1L)
|
||||||
}
|
}
|
||||||
Opcode.DEC_INT -> {
|
Opcode.DEC_INT -> {
|
||||||
val slot = decoder.readSlot(code, ip)
|
val slot = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setInt(slot, frame.getInt(slot) - 1L)
|
setInt(fn, frame, scope, slot, getInt(fn, frame, scope, slot) - 1L)
|
||||||
}
|
}
|
||||||
Opcode.ADD_REAL -> {
|
Opcode.ADD_REAL -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -216,7 +216,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setReal(dst, frame.getReal(a) + frame.getReal(b))
|
setReal(fn, frame, scope, dst, getReal(fn, frame, scope, a) + getReal(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.SUB_REAL -> {
|
Opcode.SUB_REAL -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -225,7 +225,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setReal(dst, frame.getReal(a) - frame.getReal(b))
|
setReal(fn, frame, scope, dst, getReal(fn, frame, scope, a) - getReal(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.MUL_REAL -> {
|
Opcode.MUL_REAL -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -234,7 +234,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setReal(dst, frame.getReal(a) * frame.getReal(b))
|
setReal(fn, frame, scope, dst, getReal(fn, frame, scope, a) * getReal(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.DIV_REAL -> {
|
Opcode.DIV_REAL -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -243,14 +243,14 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setReal(dst, frame.getReal(a) / frame.getReal(b))
|
setReal(fn, frame, scope, dst, getReal(fn, frame, scope, a) / getReal(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.NEG_REAL -> {
|
Opcode.NEG_REAL -> {
|
||||||
val src = decoder.readSlot(code, ip)
|
val src = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setReal(dst, -frame.getReal(src))
|
setReal(fn, frame, scope, dst, -getReal(fn, frame, scope, src))
|
||||||
}
|
}
|
||||||
Opcode.AND_INT -> {
|
Opcode.AND_INT -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -259,7 +259,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setInt(dst, frame.getInt(a) and frame.getInt(b))
|
setInt(fn, frame, scope, dst, getInt(fn, frame, scope, a) and getInt(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.OR_INT -> {
|
Opcode.OR_INT -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -268,7 +268,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setInt(dst, frame.getInt(a) or frame.getInt(b))
|
setInt(fn, frame, scope, dst, getInt(fn, frame, scope, a) or getInt(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.XOR_INT -> {
|
Opcode.XOR_INT -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -277,7 +277,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setInt(dst, frame.getInt(a) xor frame.getInt(b))
|
setInt(fn, frame, scope, dst, getInt(fn, frame, scope, a) xor getInt(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.SHL_INT -> {
|
Opcode.SHL_INT -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -286,7 +286,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setInt(dst, frame.getInt(a) shl frame.getInt(b).toInt())
|
setInt(fn, frame, scope, dst, getInt(fn, frame, scope, a) shl getInt(fn, frame, scope, b).toInt())
|
||||||
}
|
}
|
||||||
Opcode.SHR_INT -> {
|
Opcode.SHR_INT -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -295,7 +295,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setInt(dst, frame.getInt(a) shr frame.getInt(b).toInt())
|
setInt(fn, frame, scope, dst, getInt(fn, frame, scope, a) shr getInt(fn, frame, scope, b).toInt())
|
||||||
}
|
}
|
||||||
Opcode.USHR_INT -> {
|
Opcode.USHR_INT -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -304,14 +304,14 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setInt(dst, frame.getInt(a) ushr frame.getInt(b).toInt())
|
setInt(fn, frame, scope, dst, getInt(fn, frame, scope, a) ushr getInt(fn, frame, scope, b).toInt())
|
||||||
}
|
}
|
||||||
Opcode.INV_INT -> {
|
Opcode.INV_INT -> {
|
||||||
val src = decoder.readSlot(code, ip)
|
val src = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setInt(dst, frame.getInt(src).inv())
|
setInt(fn, frame, scope, dst, getInt(fn, frame, scope, src).inv())
|
||||||
}
|
}
|
||||||
Opcode.CMP_LT_INT -> {
|
Opcode.CMP_LT_INT -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -320,7 +320,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getInt(a) < frame.getInt(b))
|
setBool(fn, frame, scope, dst, getInt(fn, frame, scope, a) < getInt(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.CMP_LTE_INT -> {
|
Opcode.CMP_LTE_INT -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -329,7 +329,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getInt(a) <= frame.getInt(b))
|
setBool(fn, frame, scope, dst, getInt(fn, frame, scope, a) <= getInt(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.CMP_GT_INT -> {
|
Opcode.CMP_GT_INT -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -338,7 +338,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getInt(a) > frame.getInt(b))
|
setBool(fn, frame, scope, dst, getInt(fn, frame, scope, a) > getInt(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.CMP_GTE_INT -> {
|
Opcode.CMP_GTE_INT -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -347,7 +347,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getInt(a) >= frame.getInt(b))
|
setBool(fn, frame, scope, dst, getInt(fn, frame, scope, a) >= getInt(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.CMP_EQ_INT -> {
|
Opcode.CMP_EQ_INT -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -356,7 +356,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getInt(a) == frame.getInt(b))
|
setBool(fn, frame, scope, dst, getInt(fn, frame, scope, a) == getInt(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.CMP_NEQ_INT -> {
|
Opcode.CMP_NEQ_INT -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -365,7 +365,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getInt(a) != frame.getInt(b))
|
setBool(fn, frame, scope, dst, getInt(fn, frame, scope, a) != getInt(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.CMP_EQ_REAL -> {
|
Opcode.CMP_EQ_REAL -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -374,7 +374,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getReal(a) == frame.getReal(b))
|
setBool(fn, frame, scope, dst, getReal(fn, frame, scope, a) == getReal(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.CMP_NEQ_REAL -> {
|
Opcode.CMP_NEQ_REAL -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -383,7 +383,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getReal(a) != frame.getReal(b))
|
setBool(fn, frame, scope, dst, getReal(fn, frame, scope, a) != getReal(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.CMP_LT_REAL -> {
|
Opcode.CMP_LT_REAL -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -392,7 +392,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getReal(a) < frame.getReal(b))
|
setBool(fn, frame, scope, dst, getReal(fn, frame, scope, a) < getReal(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.CMP_LTE_REAL -> {
|
Opcode.CMP_LTE_REAL -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -401,7 +401,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getReal(a) <= frame.getReal(b))
|
setBool(fn, frame, scope, dst, getReal(fn, frame, scope, a) <= getReal(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.CMP_GT_REAL -> {
|
Opcode.CMP_GT_REAL -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -410,7 +410,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getReal(a) > frame.getReal(b))
|
setBool(fn, frame, scope, dst, getReal(fn, frame, scope, a) > getReal(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.CMP_GTE_REAL -> {
|
Opcode.CMP_GTE_REAL -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -419,7 +419,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getReal(a) >= frame.getReal(b))
|
setBool(fn, frame, scope, dst, getReal(fn, frame, scope, a) >= getReal(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.CMP_EQ_BOOL -> {
|
Opcode.CMP_EQ_BOOL -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -428,7 +428,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getBool(a) == frame.getBool(b))
|
setBool(fn, frame, scope, dst, getBool(fn, frame, scope, a) == getBool(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.CMP_NEQ_BOOL -> {
|
Opcode.CMP_NEQ_BOOL -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -437,7 +437,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getBool(a) != frame.getBool(b))
|
setBool(fn, frame, scope, dst, getBool(fn, frame, scope, a) != getBool(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.CMP_EQ_INT_REAL -> {
|
Opcode.CMP_EQ_INT_REAL -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -446,7 +446,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getInt(a).toDouble() == frame.getReal(b))
|
setBool(fn, frame, scope, dst, getInt(fn, frame, scope, a).toDouble() == getReal(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.CMP_EQ_REAL_INT -> {
|
Opcode.CMP_EQ_REAL_INT -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -455,7 +455,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getReal(a) == frame.getInt(b).toDouble())
|
setBool(fn, frame, scope, dst, getReal(fn, frame, scope, a) == getInt(fn, frame, scope, b).toDouble())
|
||||||
}
|
}
|
||||||
Opcode.CMP_LT_INT_REAL -> {
|
Opcode.CMP_LT_INT_REAL -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -464,7 +464,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getInt(a).toDouble() < frame.getReal(b))
|
setBool(fn, frame, scope, dst, getInt(fn, frame, scope, a).toDouble() < getReal(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.CMP_LT_REAL_INT -> {
|
Opcode.CMP_LT_REAL_INT -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -473,7 +473,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getReal(a) < frame.getInt(b).toDouble())
|
setBool(fn, frame, scope, dst, getReal(fn, frame, scope, a) < getInt(fn, frame, scope, b).toDouble())
|
||||||
}
|
}
|
||||||
Opcode.CMP_LTE_INT_REAL -> {
|
Opcode.CMP_LTE_INT_REAL -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -482,7 +482,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getInt(a).toDouble() <= frame.getReal(b))
|
setBool(fn, frame, scope, dst, getInt(fn, frame, scope, a).toDouble() <= getReal(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.CMP_LTE_REAL_INT -> {
|
Opcode.CMP_LTE_REAL_INT -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -491,7 +491,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getReal(a) <= frame.getInt(b).toDouble())
|
setBool(fn, frame, scope, dst, getReal(fn, frame, scope, a) <= getInt(fn, frame, scope, b).toDouble())
|
||||||
}
|
}
|
||||||
Opcode.CMP_GT_INT_REAL -> {
|
Opcode.CMP_GT_INT_REAL -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -500,7 +500,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getInt(a).toDouble() > frame.getReal(b))
|
setBool(fn, frame, scope, dst, getInt(fn, frame, scope, a).toDouble() > getReal(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.CMP_GT_REAL_INT -> {
|
Opcode.CMP_GT_REAL_INT -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -509,7 +509,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getReal(a) > frame.getInt(b).toDouble())
|
setBool(fn, frame, scope, dst, getReal(fn, frame, scope, a) > getInt(fn, frame, scope, b).toDouble())
|
||||||
}
|
}
|
||||||
Opcode.CMP_GTE_INT_REAL -> {
|
Opcode.CMP_GTE_INT_REAL -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -518,7 +518,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getInt(a).toDouble() >= frame.getReal(b))
|
setBool(fn, frame, scope, dst, getInt(fn, frame, scope, a).toDouble() >= getReal(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.CMP_GTE_REAL_INT -> {
|
Opcode.CMP_GTE_REAL_INT -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -527,7 +527,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getReal(a) >= frame.getInt(b).toDouble())
|
setBool(fn, frame, scope, dst, getReal(fn, frame, scope, a) >= getInt(fn, frame, scope, b).toDouble())
|
||||||
}
|
}
|
||||||
Opcode.CMP_NEQ_INT_REAL -> {
|
Opcode.CMP_NEQ_INT_REAL -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -536,7 +536,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getInt(a).toDouble() != frame.getReal(b))
|
setBool(fn, frame, scope, dst, getInt(fn, frame, scope, a).toDouble() != getReal(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.CMP_NEQ_REAL_INT -> {
|
Opcode.CMP_NEQ_REAL_INT -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -545,7 +545,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getReal(a) != frame.getInt(b).toDouble())
|
setBool(fn, frame, scope, dst, getReal(fn, frame, scope, a) != getInt(fn, frame, scope, b).toDouble())
|
||||||
}
|
}
|
||||||
Opcode.CMP_EQ_OBJ -> {
|
Opcode.CMP_EQ_OBJ -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -554,7 +554,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getObj(a).equals(scope, frame.getObj(b)))
|
setBool(fn, frame, scope, dst, getObj(fn, frame, scope, a).equals(scope, getObj(fn, frame, scope, b)))
|
||||||
}
|
}
|
||||||
Opcode.CMP_NEQ_OBJ -> {
|
Opcode.CMP_NEQ_OBJ -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -563,7 +563,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, !frame.getObj(a).equals(scope, frame.getObj(b)))
|
setBool(fn, frame, scope, dst, !getObj(fn, frame, scope, a).equals(scope, getObj(fn, frame, scope, b)))
|
||||||
}
|
}
|
||||||
Opcode.CMP_REF_EQ_OBJ -> {
|
Opcode.CMP_REF_EQ_OBJ -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -572,7 +572,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getObj(a) === frame.getObj(b))
|
setBool(fn, frame, scope, dst, getObj(fn, frame, scope, a) === getObj(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.CMP_REF_NEQ_OBJ -> {
|
Opcode.CMP_REF_NEQ_OBJ -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -581,7 +581,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getObj(a) !== frame.getObj(b))
|
setBool(fn, frame, scope, dst, getObj(fn, frame, scope, a) !== getObj(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.CMP_LT_OBJ -> {
|
Opcode.CMP_LT_OBJ -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -590,7 +590,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getObj(a).compareTo(scope, frame.getObj(b)) < 0)
|
setBool(fn, frame, scope, dst, getObj(fn, frame, scope, a).compareTo(scope, getObj(fn, frame, scope, b)) < 0)
|
||||||
}
|
}
|
||||||
Opcode.CMP_LTE_OBJ -> {
|
Opcode.CMP_LTE_OBJ -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -599,7 +599,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getObj(a).compareTo(scope, frame.getObj(b)) <= 0)
|
setBool(fn, frame, scope, dst, getObj(fn, frame, scope, a).compareTo(scope, getObj(fn, frame, scope, b)) <= 0)
|
||||||
}
|
}
|
||||||
Opcode.CMP_GT_OBJ -> {
|
Opcode.CMP_GT_OBJ -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -608,7 +608,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getObj(a).compareTo(scope, frame.getObj(b)) > 0)
|
setBool(fn, frame, scope, dst, getObj(fn, frame, scope, a).compareTo(scope, getObj(fn, frame, scope, b)) > 0)
|
||||||
}
|
}
|
||||||
Opcode.CMP_GTE_OBJ -> {
|
Opcode.CMP_GTE_OBJ -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -617,14 +617,59 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getObj(a).compareTo(scope, frame.getObj(b)) >= 0)
|
setBool(fn, frame, scope, dst, getObj(fn, frame, scope, a).compareTo(scope, getObj(fn, frame, scope, b)) >= 0)
|
||||||
|
}
|
||||||
|
Opcode.ADD_OBJ -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
setObj(fn, frame, scope, dst, getObj(fn, frame, scope, a).plus(scope, getObj(fn, frame, scope, b)))
|
||||||
|
}
|
||||||
|
Opcode.SUB_OBJ -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
setObj(fn, frame, scope, dst, getObj(fn, frame, scope, a).minus(scope, getObj(fn, frame, scope, b)))
|
||||||
|
}
|
||||||
|
Opcode.MUL_OBJ -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
setObj(fn, frame, scope, dst, getObj(fn, frame, scope, a).mul(scope, getObj(fn, frame, scope, b)))
|
||||||
|
}
|
||||||
|
Opcode.DIV_OBJ -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
setObj(fn, frame, scope, dst, getObj(fn, frame, scope, a).div(scope, getObj(fn, frame, scope, b)))
|
||||||
|
}
|
||||||
|
Opcode.MOD_OBJ -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
setObj(fn, frame, scope, dst, getObj(fn, frame, scope, a).mod(scope, getObj(fn, frame, scope, b)))
|
||||||
}
|
}
|
||||||
Opcode.NOT_BOOL -> {
|
Opcode.NOT_BOOL -> {
|
||||||
val src = decoder.readSlot(code, ip)
|
val src = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, !frame.getBool(src))
|
setBool(fn, frame, scope, dst, !getBool(fn, frame, scope, src))
|
||||||
}
|
}
|
||||||
Opcode.AND_BOOL -> {
|
Opcode.AND_BOOL -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -633,7 +678,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getBool(a) && frame.getBool(b))
|
setBool(fn, frame, scope, dst, getBool(fn, frame, scope, a) && getBool(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.OR_BOOL -> {
|
Opcode.OR_BOOL -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
@ -642,7 +687,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val dst = decoder.readSlot(code, ip)
|
val dst = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getBool(a) || frame.getBool(b))
|
setBool(fn, frame, scope, dst, getBool(fn, frame, scope, a) || getBool(fn, frame, scope, b))
|
||||||
}
|
}
|
||||||
Opcode.JMP -> {
|
Opcode.JMP -> {
|
||||||
val target = decoder.readIp(code, ip, fn.ipWidth)
|
val target = decoder.readIp(code, ip, fn.ipWidth)
|
||||||
@ -653,7 +698,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val target = decoder.readIp(code, ip, fn.ipWidth)
|
val target = decoder.readIp(code, ip, fn.ipWidth)
|
||||||
ip += fn.ipWidth
|
ip += fn.ipWidth
|
||||||
if (!frame.getBool(cond)) {
|
if (!getBool(fn, frame, scope, cond)) {
|
||||||
ip = target
|
ip = target
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -662,7 +707,7 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
val target = decoder.readIp(code, ip, fn.ipWidth)
|
val target = decoder.readIp(code, ip, fn.ipWidth)
|
||||||
ip += fn.ipWidth
|
ip += fn.ipWidth
|
||||||
if (frame.getBool(cond)) {
|
if (getBool(fn, frame, scope, cond)) {
|
||||||
ip = target
|
ip = target
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -675,15 +720,15 @@ class BytecodeVm {
|
|||||||
?: error("Fallback statement not found: $id")
|
?: error("Fallback statement not found: $id")
|
||||||
val result = stmt.execute(scope)
|
val result = stmt.execute(scope)
|
||||||
when (result) {
|
when (result) {
|
||||||
is ObjInt -> frame.setInt(dst, result.value)
|
is ObjInt -> setInt(fn, frame, scope, dst, result.value)
|
||||||
is ObjReal -> frame.setReal(dst, result.value)
|
is ObjReal -> setReal(fn, frame, scope, dst, result.value)
|
||||||
is ObjBool -> frame.setBool(dst, result.value)
|
is ObjBool -> setBool(fn, frame, scope, dst, result.value)
|
||||||
else -> frame.setObj(dst, result)
|
else -> setObj(fn, frame, scope, dst, result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Opcode.RET -> {
|
Opcode.RET -> {
|
||||||
val slot = decoder.readSlot(code, ip)
|
val slot = decoder.readSlot(code, ip)
|
||||||
return slotToObj(frame, slot)
|
return slotToObj(fn, frame, scope, slot)
|
||||||
}
|
}
|
||||||
Opcode.RET_VOID -> return ObjVoid
|
Opcode.RET_VOID -> return ObjVoid
|
||||||
else -> error("Opcode not implemented: $op")
|
else -> error("Opcode not implemented: $op")
|
||||||
@ -692,13 +737,96 @@ class BytecodeVm {
|
|||||||
return ObjVoid
|
return ObjVoid
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun slotToObj(frame: BytecodeFrame, slot: Int): Obj {
|
private fun slotToObj(fn: BytecodeFunction, frame: BytecodeFrame, scope: Scope, slot: Int): Obj {
|
||||||
return when (frame.getSlotTypeCode(slot)) {
|
if (slot < fn.scopeSlotCount) {
|
||||||
SlotType.INT.code -> ObjInt.of(frame.getInt(slot))
|
return resolveScope(scope, fn.scopeSlotDepths[slot]).getSlotRecord(fn.scopeSlotIndices[slot]).value
|
||||||
SlotType.REAL.code -> ObjReal.of(frame.getReal(slot))
|
}
|
||||||
SlotType.BOOL.code -> if (frame.getBool(slot)) ObjTrue else ObjFalse
|
val local = slot - fn.scopeSlotCount
|
||||||
SlotType.OBJ.code -> frame.getObj(slot)
|
return when (frame.getSlotTypeCode(local)) {
|
||||||
|
SlotType.INT.code -> ObjInt.of(frame.getInt(local))
|
||||||
|
SlotType.REAL.code -> ObjReal.of(frame.getReal(local))
|
||||||
|
SlotType.BOOL.code -> if (frame.getBool(local)) ObjTrue else ObjFalse
|
||||||
|
SlotType.OBJ.code -> frame.getObj(local)
|
||||||
else -> ObjVoid
|
else -> ObjVoid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getObj(fn: BytecodeFunction, frame: BytecodeFrame, scope: Scope, slot: Int): Obj {
|
||||||
|
return if (slot < fn.scopeSlotCount) {
|
||||||
|
resolveScope(scope, fn.scopeSlotDepths[slot]).getSlotRecord(fn.scopeSlotIndices[slot]).value
|
||||||
|
} else {
|
||||||
|
frame.getObj(slot - fn.scopeSlotCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setObj(fn: BytecodeFunction, frame: BytecodeFrame, scope: Scope, slot: Int, value: Obj) {
|
||||||
|
if (slot < fn.scopeSlotCount) {
|
||||||
|
setScopeSlotValue(scope, fn.scopeSlotDepths[slot], fn.scopeSlotIndices[slot], value)
|
||||||
|
} else {
|
||||||
|
frame.setObj(slot - fn.scopeSlotCount, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getInt(fn: BytecodeFunction, frame: BytecodeFrame, scope: Scope, slot: Int): Long {
|
||||||
|
return if (slot < fn.scopeSlotCount) {
|
||||||
|
resolveScope(scope, fn.scopeSlotDepths[slot]).getSlotRecord(fn.scopeSlotIndices[slot]).value.toLong()
|
||||||
|
} else {
|
||||||
|
frame.getInt(slot - fn.scopeSlotCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setInt(fn: BytecodeFunction, frame: BytecodeFrame, scope: Scope, slot: Int, value: Long) {
|
||||||
|
if (slot < fn.scopeSlotCount) {
|
||||||
|
setScopeSlotValue(scope, fn.scopeSlotDepths[slot], fn.scopeSlotIndices[slot], ObjInt.of(value))
|
||||||
|
} else {
|
||||||
|
frame.setInt(slot - fn.scopeSlotCount, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getReal(fn: BytecodeFunction, frame: BytecodeFrame, scope: Scope, slot: Int): Double {
|
||||||
|
return if (slot < fn.scopeSlotCount) {
|
||||||
|
resolveScope(scope, fn.scopeSlotDepths[slot]).getSlotRecord(fn.scopeSlotIndices[slot]).value.toDouble()
|
||||||
|
} else {
|
||||||
|
frame.getReal(slot - fn.scopeSlotCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setReal(fn: BytecodeFunction, frame: BytecodeFrame, scope: Scope, slot: Int, value: Double) {
|
||||||
|
if (slot < fn.scopeSlotCount) {
|
||||||
|
setScopeSlotValue(scope, fn.scopeSlotDepths[slot], fn.scopeSlotIndices[slot], ObjReal.of(value))
|
||||||
|
} else {
|
||||||
|
frame.setReal(slot - fn.scopeSlotCount, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getBool(fn: BytecodeFunction, frame: BytecodeFrame, scope: Scope, slot: Int): Boolean {
|
||||||
|
return if (slot < fn.scopeSlotCount) {
|
||||||
|
resolveScope(scope, fn.scopeSlotDepths[slot]).getSlotRecord(fn.scopeSlotIndices[slot]).value.toBool()
|
||||||
|
} else {
|
||||||
|
frame.getBool(slot - fn.scopeSlotCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setBool(fn: BytecodeFunction, frame: BytecodeFrame, scope: Scope, slot: Int, value: Boolean) {
|
||||||
|
if (slot < fn.scopeSlotCount) {
|
||||||
|
setScopeSlotValue(scope, fn.scopeSlotDepths[slot], fn.scopeSlotIndices[slot], if (value) ObjTrue else ObjFalse)
|
||||||
|
} else {
|
||||||
|
frame.setBool(slot - fn.scopeSlotCount, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setScopeSlotValue(scope: Scope, depth: Int, index: Int, value: Obj) {
|
||||||
|
val target = resolveScope(scope, depth)
|
||||||
|
target.setSlotValue(index, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resolveScope(scope: Scope, depth: Int): Scope {
|
||||||
|
if (depth == 0) return scope
|
||||||
|
val next = when (scope) {
|
||||||
|
is net.sergeych.lyng.ClosureScope -> scope.closureScope
|
||||||
|
else -> scope.parent
|
||||||
|
}
|
||||||
|
return next?.let { resolveScope(it, depth - 1) }
|
||||||
|
?: error("Scope depth $depth is out of range")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -95,6 +95,11 @@ enum class Opcode(val code: Int) {
|
|||||||
CMP_LTE_OBJ(0x74),
|
CMP_LTE_OBJ(0x74),
|
||||||
CMP_GT_OBJ(0x75),
|
CMP_GT_OBJ(0x75),
|
||||||
CMP_GTE_OBJ(0x76),
|
CMP_GTE_OBJ(0x76),
|
||||||
|
ADD_OBJ(0x77),
|
||||||
|
SUB_OBJ(0x78),
|
||||||
|
MUL_OBJ(0x79),
|
||||||
|
DIV_OBJ(0x7A),
|
||||||
|
MOD_OBJ(0x7B),
|
||||||
|
|
||||||
JMP(0x80),
|
JMP(0x80),
|
||||||
JMP_IF_TRUE(0x81),
|
JMP_IF_TRUE(0x81),
|
||||||
|
|||||||
@ -2405,6 +2405,8 @@ class LocalSlotRef(
|
|||||||
val name: String,
|
val name: String,
|
||||||
internal val slot: Int,
|
internal val slot: Int,
|
||||||
internal val depth: Int,
|
internal val depth: Int,
|
||||||
|
internal val isMutable: Boolean,
|
||||||
|
internal val isDelegated: Boolean,
|
||||||
private val atPos: Pos,
|
private val atPos: Pos,
|
||||||
) : ObjRef {
|
) : ObjRef {
|
||||||
override fun forEachVariable(block: (String) -> Unit) {
|
override fun forEachVariable(block: (String) -> Unit) {
|
||||||
|
|||||||
@ -210,7 +210,7 @@ class BytecodeVmTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun localSlotTypeTrackingEnablesArithmetic() = kotlinx.coroutines.test.runTest {
|
fun localSlotTypeTrackingEnablesArithmetic() = kotlinx.coroutines.test.runTest {
|
||||||
val slotRef = LocalSlotRef("a", 0, 0, net.sergeych.lyng.Pos.builtIn)
|
val slotRef = LocalSlotRef("a", 0, 0, true, false, net.sergeych.lyng.Pos.builtIn)
|
||||||
val assign = AssignRef(
|
val assign = AssignRef(
|
||||||
slotRef,
|
slotRef,
|
||||||
ConstRef(ObjInt.of(2).asReadonly),
|
ConstRef(ObjInt.of(2).asReadonly),
|
||||||
@ -225,10 +225,32 @@ class BytecodeVmTest {
|
|||||||
net.sergeych.lyng.Pos.builtIn
|
net.sergeych.lyng.Pos.builtIn
|
||||||
)
|
)
|
||||||
val fn = BytecodeCompiler().compileExpression("localSlotAdd", expr) ?: error("bytecode compile failed")
|
val fn = BytecodeCompiler().compileExpression("localSlotAdd", expr) ?: error("bytecode compile failed")
|
||||||
val result = BytecodeVm().execute(fn, Scope(), emptyList())
|
val scope = Scope().apply { applySlotPlan(mapOf("a" to 0)) }
|
||||||
|
val result = BytecodeVm().execute(fn, scope, emptyList())
|
||||||
assertEquals(4, result.toInt())
|
assertEquals(4, result.toInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun parentScopeSlotAccessWorks() = kotlinx.coroutines.test.runTest {
|
||||||
|
val parentRef = LocalSlotRef("a", 0, 1, true, false, net.sergeych.lyng.Pos.builtIn)
|
||||||
|
val expr = ExpressionStatement(
|
||||||
|
BinaryOpRef(
|
||||||
|
BinOp.PLUS,
|
||||||
|
parentRef,
|
||||||
|
ConstRef(ObjInt.of(2).asReadonly)
|
||||||
|
),
|
||||||
|
net.sergeych.lyng.Pos.builtIn
|
||||||
|
)
|
||||||
|
val fn = BytecodeCompiler().compileExpression("parentSlotAdd", expr) ?: error("bytecode compile failed")
|
||||||
|
val parent = Scope().apply {
|
||||||
|
applySlotPlan(mapOf("a" to 0))
|
||||||
|
setSlotValue(0, ObjInt.of(3))
|
||||||
|
}
|
||||||
|
val child = Scope(parent)
|
||||||
|
val result = BytecodeVm().execute(fn, child, emptyList())
|
||||||
|
assertEquals(5, result.toInt())
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun objectEqualityUsesBytecodeOps() = kotlinx.coroutines.test.runTest {
|
fun objectEqualityUsesBytecodeOps() = kotlinx.coroutines.test.runTest {
|
||||||
val expr = ExpressionStatement(
|
val expr = ExpressionStatement(
|
||||||
@ -298,4 +320,19 @@ class BytecodeVmTest {
|
|||||||
val gteResult = BytecodeVm().execute(gteFn, Scope(), emptyList())
|
val gteResult = BytecodeVm().execute(gteFn, Scope(), emptyList())
|
||||||
assertEquals(true, gteResult.toBool())
|
assertEquals(true, gteResult.toBool())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun objectArithmeticUsesBytecodeOps() = kotlinx.coroutines.test.runTest {
|
||||||
|
val expr = ExpressionStatement(
|
||||||
|
BinaryOpRef(
|
||||||
|
BinOp.PLUS,
|
||||||
|
ConstRef(ObjString("a").asReadonly),
|
||||||
|
ConstRef(ObjString("b").asReadonly),
|
||||||
|
),
|
||||||
|
net.sergeych.lyng.Pos.builtIn
|
||||||
|
)
|
||||||
|
val fn = BytecodeCompiler().compileExpression("objPlus", expr) ?: error("bytecode compile failed")
|
||||||
|
val result = BytecodeVm().execute(fn, Scope(), emptyList())
|
||||||
|
assertEquals("ab", (result as ObjString).value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user