diff --git a/build.gradle.kts b/build.gradle.kts index 8af6856..77412d9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,11 +6,12 @@ plugins { } group = "net.adriantodt" -version = "0.2.0" +version = "0.3.0" repositories { mavenCentral() maven { url = uri("https://maven.cafeteria.dev/releases") } + maven { url = uri("https://maven.adriantodt.net/releases") } } kotlin { diff --git a/src/commonMain/kotlin/net/adriantodt/leanvm/context/FunctionSetupContext.kt b/src/commonMain/kotlin/net/adriantodt/leanvm/context/FunctionSetupContext.kt index b81dd98..fb828c8 100644 --- a/src/commonMain/kotlin/net/adriantodt/leanvm/context/FunctionSetupContext.kt +++ b/src/commonMain/kotlin/net/adriantodt/leanvm/context/FunctionSetupContext.kt @@ -64,10 +64,10 @@ public class FunctionSetupContext( control, function.source, function.name ?: "", + thisValue, runtime, scope, paramBody, - thisValue ) ) return @@ -84,10 +84,10 @@ public class FunctionSetupContext( control, function.source, function.name ?: "", + thisValue, runtime, Scope(scope), body, - thisValue ) ) } diff --git a/src/commonMain/kotlin/net/adriantodt/leanvm/context/NodeExecutionContext.kt b/src/commonMain/kotlin/net/adriantodt/leanvm/context/NodeExecutionContext.kt index a4af79c..227312d 100644 --- a/src/commonMain/kotlin/net/adriantodt/leanvm/context/NodeExecutionContext.kt +++ b/src/commonMain/kotlin/net/adriantodt/leanvm/context/NodeExecutionContext.kt @@ -3,34 +3,33 @@ package net.adriantodt.leanvm.context import net.adriantodt.leanvm.Scope import net.adriantodt.leanvm.StackTrace import net.adriantodt.leanvm.bytecode.LeanCode -import net.adriantodt.leanvm.bytecode.LeanInsn.Opcode -import net.adriantodt.leanvm.bytecode.LeanInsn.ParameterlessCode import net.adriantodt.leanvm.bytecode.LeanNode import net.adriantodt.leanvm.exceptions.LeanIndexOutOfBoundsException import net.adriantodt.leanvm.exceptions.LeanNullPointerException +import net.adriantodt.leanvm.exceptions.LeanRuntimeException import net.adriantodt.leanvm.exceptions.MalformedBytecodeException import net.adriantodt.leanvm.types.* import net.adriantodt.leanvm.utils.Comparison +import net.adriantodt.leanvm.utils.InstructionDecoder +import kotlin.math.sign public class NodeExecutionContext( - private val control: LeanMachineControl, + protected override val control: LeanMachineControl, private val code: LeanCode, private val functionName: String = "
", - override val runtime: LeanRuntime = LeanRuntime(), + private val thisValue: LAny? = null, + public override val runtime: LeanRuntime = LeanRuntime(), private var scope: Scope = runtime.rootScope(), private val node: LeanNode = code.nodeArr.getOrElse(0) { - throw MalformedBytecodeException( - "Code does not contain an executable node.", - control.stackTrace() - ) + throw MalformedBytecodeException("Code does not contain an executable node.", control.stackTrace()) }, - private val thisValue: LAny? = null, -) : LeanContext { +) : InstructionDecoder(), LeanContext { private var next: Int = 0 private val stack: MutableList = mutableListOf() private val exceptionHandlers: MutableList = mutableListOf() + private var customReturn: ((value: LAny) -> Unit)? = null - override fun step() { + public override fun step() { if (next >= node.insnArr.size) { control.onReturn(stack.removeLastOrNull() ?: LNull) return @@ -41,307 +40,624 @@ public class NodeExecutionContext( control.stackTrace() ) } + handle(insn) + } - when (Opcode.values()[insn.opcode]) { - Opcode.PARAMETERLESS -> when (ParameterlessCode.values()[insn.immediate]) { - ParameterlessCode.ARRAY_INSERT -> { - val value = popStack() - val array = peekStack() - if (array !is LArray) { - throw MalformedBytecodeException( - "Tried to arrayInsert value '$value' into '$array' which is of type '${array.type}'.", + public override fun onReturn(value: LAny) { + val handler = this.customReturn + if (handler != null) { + customReturn = null + handler(value) + } else { + stack.add(value) + } + } + + public override fun onThrow(value: LAny) { + val handler = exceptionHandlers.removeLastOrNull() + if (handler == null) { + control.onThrow(value) + return + } + if (handler.keepOnStack < stack.size) { + println("WTF? Stack is missing ${handler.keepOnStack - stack.size} items!! This is probably a bug!") + } else if (handler.keepOnStack > stack.size) { + repeat(handler.keepOnStack - stack.size) { popStack() } + } + next = handler.onException + stack.add(value) + } + + public override fun trace(): StackTrace? { + val label = node.findSect(next - 1) ?: return null + val section = code.sectArr.getOrElse(label.index) { return null } + val s = code.sConstArr.getOrElse(section.nameConst) { + throw MalformedBytecodeException( + "Tried to load string constant ${section.nameConst} which wasn't defined.", + control.stackTrace() + ) + } + return StackTrace(functionName, s, section.line, section.column) + } + + private fun popStack(): LAny { + return stack.removeLastOrNull() ?: throw MalformedBytecodeException( + "Tried to remove an item from the stack, but the stack is empty.", + control.stackTrace() + ) + } + + private fun peekStack(): LAny { + return stack.lastOrNull() ?: throw MalformedBytecodeException( + "Tried to get the last item from the stack, but the stack is empty.", + control.stackTrace() + ) + } + + private fun setupInvocation(thisValue: LAny?, function: LAny, args: List = emptyList()) { + if (function is LFunction) { + control.push(function.setupContext(control, thisValue, args, runtime)) + return + } + if (function is LMetaObject) { + val metaInvoke = function.access(LString("invoke")) + if (metaInvoke is LFunction) { + control.push(metaInvoke.setupContext(control, function, args, runtime)) + return + } + } + runtime.customInvocation(control, thisValue, function, args) + } + + protected override fun handleArrayInsert() { + val value = popStack() + val array = peekStack() + if (array !is LArray) { + throw MalformedBytecodeException( + "Tried to arrayInsert value '$value' into '$array' which is of type '${array.type}'.", + control.stackTrace() + ) + } + array.value.add(value) + } + + protected override fun handleDup() { + stack.add(peekStack()) + } + + protected override fun handleNewArray() { + stack.add(LArray()) + } + + protected override fun handleNewObject() { + stack.add(LObject()) + } + + protected override fun handleObjectInsert() { + val value = popStack() + val key = popStack() + val obj = peekStack() + if (obj !is LObject) { + throw MalformedBytecodeException( + "Tried to objectInsert key '$key' and value '$value' into '$obj' which is of type '${obj.type}'.", + control.stackTrace() + ) + } + obj.value[key] = value + } + + protected override fun handlePop() { + popStack() + } + + protected override fun handlePopScope() { + scope = scope.parent ?: throw MalformedBytecodeException( + "Tried to pop scope but encountered a root scope.", + control.stackTrace() + ) + } + + protected override fun handlePopExceptionHandling() { + exceptionHandlers.removeLastOrNull() ?: throw MalformedBytecodeException( + "Tried execute POP_EXCEPTION_HANDLING instruction but found no exception handler.", + control.stackTrace() + ) + } + + protected override fun handlePushNull() { + stack.add(LNull) + } + + protected override fun handlePushScope() { + scope = Scope(scope) + } + + protected override fun handlePushThis() { + stack.add(thisValue ?: runtime.customThisValue(control)) + } + + protected override fun handleReturn() { + control.onReturn(popStack()) + } + + protected override fun handleThrow() { + onThrow(popStack()) + } + + protected override fun handleTypeof() { + stack.add(LString(popStack().type)) + } + + protected override fun handlePushBoolean(b: Boolean) { + stack.add(LBoolean.of(b)) + } + + protected override fun handlePositive() { + val target = popStack() + if (target is LNumber) { + stack.add(+target) + return + } + if (target is LMetaObject) { + val property = target.access(LString("unaryPlus")) + if (property is LFunction) { + control.push(property.setupContext(control, target, emptyList(), runtime)) + return + } + if (property != null) { + stack.add(property) + return + } + } + runtime.customPositiveOperation(control, target) + } + + protected override fun handleNegative() { + val target = popStack() + if (target is LNumber) { + stack.add(-target) + return + } + if (target is LMetaObject) { + val property = target.access(LString("unaryMinus")) + if (property is LFunction) { + control.push(property.setupContext(control, target, emptyList(), runtime)) + return + } + if (property != null) { + stack.add(property) + return + } + } + runtime.customNegativeOperation(control, target) + } + + protected override fun handleTruth() { + val target = popStack() + if (target is LMetaObject) { + val property = target.access(LString("truth")) + if (property is LFunction) { + customReturn = { + if (it is LMetaObject) { + throw LeanRuntimeException( + "Meta object '$target' returned a meta object as 'truth'.", control.stackTrace() ) } - array.value.add(value) + stack.add(LBoolean.of(it.truth())) } - ParameterlessCode.DUP -> { - stack.add(peekStack()) - } - ParameterlessCode.NEW_ARRAY -> { - stack.add(LArray()) - } - ParameterlessCode.NEW_OBJECT -> { - stack.add(LObject()) + control.push(property.setupContext(control, target, emptyList(), runtime)) + return + } else if (property != null) { + if (property is LMetaObject) { + throw LeanRuntimeException( + "Meta object '$target' has a meta object as 'truth'.", + control.stackTrace() + ) } - ParameterlessCode.OBJECT_INSERT -> { - val value = popStack() - val key = popStack() - val obj = peekStack() - if (obj !is LObject) { - throw MalformedBytecodeException( - "Tried to objectInsert key '$key' and value '$value' into '$obj' which is of type '${obj.type}'.", + stack.add(LBoolean.of(property.truth())) + return + } + } + } + + protected override fun handleNot() { + val target = popStack() + if (target is LMetaObject) { + val property = target.access(LString("truth")) + if (property is LFunction) { + customReturn = { + if (it is LMetaObject) { + throw LeanRuntimeException( + "Meta object '$target' returned a meta object as 'truth'.", control.stackTrace() ) } - obj.value[key] = value - } - ParameterlessCode.POP -> { - popStack() - } - ParameterlessCode.POP_SCOPE -> { - scope = scope.parent ?: throw MalformedBytecodeException( - "Tried to pop scope but encountered a root scope.", - control.stackTrace() - ) - } - ParameterlessCode.POP_EXCEPTION_HANDLING -> { - exceptionHandlers.removeLastOrNull() ?: throw MalformedBytecodeException( - "Tried execute POP_EXCEPTION_HANDLING instruction but found no exception handler.", - control.stackTrace() - ) - } - ParameterlessCode.PUSH_NULL -> { - stack.add(LNull) - } - ParameterlessCode.PUSH_SCOPE -> { - scope = Scope(scope) - } - ParameterlessCode.PUSH_THIS -> { - stack.add(thisValue ?: runtime.customThisValue(control)) - } - ParameterlessCode.RETURN -> { - control.onReturn(popStack()) - } - ParameterlessCode.THROW -> { - onThrow(popStack()) - } - ParameterlessCode.TYPEOF -> { - stack.add(LString(popStack().type)) + stack.add(LBoolean.of(!it.truth())) } - ParameterlessCode.PUSH_TRUE -> { - stack.add(LBoolean.True) - } - ParameterlessCode.PUSH_FALSE -> { - stack.add(LBoolean.False) - } - ParameterlessCode.POSITIVE -> handlePositiveOperation() - ParameterlessCode.NEGATIVE -> handleNegativeOperation() - ParameterlessCode.TRUTH -> { - stack.add(LBoolean.of(popStack().truth())) - // TODO handle metaobjects - // Difficulty: easy (adding a function context will be enough) - } - ParameterlessCode.NOT -> { - stack.add(LBoolean.of(!popStack().truth())) - // TODO handle metaobjects - // Difficulty: easy (adding a function context will be enough) - } - ParameterlessCode.ADD -> handleAddOperation() - ParameterlessCode.SUBTRACT -> handleSubtractOperation() - ParameterlessCode.MULTIPLY -> handleMultiplyOperation() - ParameterlessCode.DIVIDE -> handleDivideOperation() - ParameterlessCode.REMAINING -> handleRemainingOperation() - ParameterlessCode.EQUALS -> { - val right = popStack() - val left = popStack() - stack.add(LBoolean.of(right == left)) - // TODO handle metaobjects - // Difficulty: easy (adding a function context will be enough) - } - ParameterlessCode.NOT_EQUALS -> { - val right = popStack() - val left = popStack() - stack.add(LBoolean.of(right != left)) - // TODO handle metaobjects - // Difficulty: easy (adding a function context will be enough) - } - ParameterlessCode.LT -> handleComparison(Comparison.LT) - ParameterlessCode.LTE -> handleComparison(Comparison.LTE) - ParameterlessCode.GT -> handleComparison(Comparison.GT) - ParameterlessCode.GTE -> handleComparison(Comparison.GTE) - ParameterlessCode.IN -> handleInOperation() - ParameterlessCode.RANGE -> handleRangeOperation() - } - Opcode.ASSIGN -> { - val s = code.sConstArr.getOrElse(insn.immediate) { - throw MalformedBytecodeException( - "Tried to load string constant ${insn.immediate} which wasn't defined.", - control.stackTrace() - ) - } - scope.set(s, popStack()) - } - Opcode.BRANCH_IF_FALSE -> handleBranchIf(false, insn.immediate) - Opcode.BRANCH_IF_TRUE -> handleBranchIf(true, insn.immediate) - Opcode.DECLARE_VARIABLE_IMMUTABLE -> { - val s = code.sConstArr.getOrElse(insn.immediate) { - throw MalformedBytecodeException( - "Tried to load string constant ${insn.immediate} which wasn't defined.", + control.push(property.setupContext(control, target, emptyList(), runtime)) + return + } else if (property != null) { + if (property is LMetaObject) { + throw LeanRuntimeException( + "Meta object '$target' has a meta object as 'truth'.", control.stackTrace() ) } - scope.define(s, false) + stack.add(LBoolean.of(!property.truth())) + return } - Opcode.DECLARE_VARIABLE_MUTABLE -> { - val s = code.sConstArr.getOrElse(insn.immediate) { - throw MalformedBytecodeException( - "Tried to load string constant ${insn.immediate} which wasn't defined.", - control.stackTrace() - ) - } - scope.define(s, true) - } - Opcode.GET_MEMBER_PROPERTY -> handleGetMemberProperty(insn.immediate) - Opcode.GET_SUBSCRIPT -> handleGetSubscript(insn.immediate) - Opcode.GET_VARIABLE -> { - val s = code.sConstArr.getOrElse(insn.immediate) { - throw MalformedBytecodeException( - "Tried to load string constant ${insn.immediate} which wasn't defined.", - control.stackTrace() - ) - } - stack.add(scope.get(s)) - } - Opcode.INVOKE -> handleInvoke(insn.immediate) - Opcode.INVOKE_LOCAL -> handleInvokeLocal(insn.immediate) - Opcode.INVOKE_MEMBER -> handleInvokeMember(insn.immediate) - Opcode.INVOKE_EXTENSION -> handleInvokeExtension(insn.immediate) - Opcode.JUMP -> { - next = node.findJump(insn.immediate)?.at ?: throw MalformedBytecodeException( - "Tried to jump to label ${insn.immediate} which wasn't defined.", - control.stackTrace() - ) + } + } + + protected override fun handleAdd() { + val right = popStack() + val left = popStack() + if (left is LString || right is LString) { + stack.add(LString(left.toString() + right.toString())) + return + } + if (left is LArray && right is LArray) { + stack.add(LArray((left.value + right.value).toMutableList())) + return + } + if (left is LNumber && right is LNumber) { + stack.add(left + right) + return + } + if (left is LMetaObject) { + val property = left.access(LString("add")) + if (property is LFunction) { + control.push(property.setupContext(control, left, listOf(right), runtime)) + return } - Opcode.LOAD_DECIMAL -> { - val l = code.lConstArr.getOrElse(insn.immediate) { - throw MalformedBytecodeException( - "Tried to load number constant ${insn.immediate} which wasn't defined.", - control.stackTrace() - ) - } - stack.add(LDecimal(Double.fromBits(l))) + if (property != null) { + stack.add(property) + return } - Opcode.LOAD_INTEGER -> { - val i = code.lConstArr.getOrElse(insn.immediate) { - throw MalformedBytecodeException( - "Tried to load number constant ${insn.immediate} which wasn't defined.", - control.stackTrace() - ) + } + stack.add(runtime.customAddOperation(control, left, right)) + } + + protected override fun handleSubtract() { + val right = popStack() + val left = popStack() + if (left is LNumber && right is LNumber) { + stack.add(left - right) + return + } + if (left is LMetaObject) { + val property = left.access(LString("subtract")) + if (property is LFunction) { + control.push(property.setupContext(control, left, listOf(right), runtime)) + return + } + if (property != null) { + stack.add(property) + return + } + } + stack.add(runtime.customSubtractOperation(control, left, right)) + } + + protected override fun handleMultiply() { + val right = popStack() + val left = popStack() + if (left is LString && right is LInteger) { + stack.add(LString(left.value.repeat(right.value.toInt()))) + } + if (left is LNumber && right is LNumber) { + stack.add(left * right) + return + } + if (left is LMetaObject) { + val property = left.access(LString("multiply")) + if (property is LFunction) { + control.push(property.setupContext(control, left, listOf(right), runtime)) + return + } + if (property != null) { + stack.add(property) + return + } + } + stack.add(runtime.customMultiplyOperation(control, left, right)) + } + + protected override fun handleDivide() { + val right = popStack() + val left = popStack() + if (left is LNumber && right is LNumber) { + stack.add(left / right) + return + } + if (left is LMetaObject) { + val property = left.access(LString("divide")) + if (property is LFunction) { + control.push(property.setupContext(control, left, listOf(right), runtime)) + return + } + if (property != null) { + stack.add(property) + return + } + } + stack.add(runtime.customDivideOperation(control, left, right)) + } + + protected override fun handleRemaining() { + val right = popStack() + val left = popStack() + if (left is LNumber && right is LNumber) { + stack.add(left % right) + return + } + if (left is LMetaObject) { + val property = left.access(LString("remaining")) + if (property is LFunction) { + control.push(property.setupContext(control, left, listOf(right), runtime)) + return + } + if (property != null) { + stack.add(property) + return + } + } + stack.add(runtime.customRemainingOperation(control, left, right)) + } + + protected override fun handleEquals() { + val right = popStack() + val left = popStack() + if (left is LMetaObject) { + val metaEquals = left.value.getOrElse(LString("equals")) { + stack.add(LBoolean.of(left.value == right)) + return + } + if (metaEquals is LFunction) { + this.customReturn = { + if (it is LMetaObject) { + throw LeanRuntimeException( + "Meta object '$left' returned a meta object as 'equals'.", + control.stackTrace() + ) + } + stack.add(LBoolean.of(it.truth())) } - stack.add(LInteger(i)) + control.push(metaEquals.setupContext(control, left, listOf(right), runtime)) + return } - Opcode.LOAD_STRING -> { - val s = code.sConstArr.getOrElse(insn.immediate) { - throw MalformedBytecodeException( - "Tried to load string constant ${insn.immediate} which wasn't defined.", - control.stackTrace() - ) + if (metaEquals is LMetaObject) { + throw LeanRuntimeException("Meta object '$left' has a 'equals' meta object.", control.stackTrace()) + } + stack.add(LBoolean.of(metaEquals == right)) + return + } + if (right is LMetaObject) { + val metaEquals = right.value.getOrElse(LString("equals")) { + stack.add(LBoolean.of(left == right.value)) + return + } + if (metaEquals is LFunction) { + this.customReturn = { + if (it is LMetaObject) { + throw LeanRuntimeException( + "Meta object '$right' returned a meta object as 'equals'.", + control.stackTrace() + ) + } + stack.add(LBoolean.of(it.truth())) } - stack.add(LString(s)) + control.push(metaEquals.setupContext(control, right, listOf(left), runtime)) + return } - Opcode.NEW_FUNCTION -> { - val f = code.funcArr.getOrElse(insn.immediate) { - throw MalformedBytecodeException( - "Tried to load function ${insn.immediate} which wasn't defined.", - control.stackTrace() - ) + if (metaEquals is LMetaObject) { + throw LeanRuntimeException("Meta object '$right' has a 'equals' meta object.", control.stackTrace()) + } + stack.add(LBoolean.of(metaEquals == left)) + return + } + stack.add(LBoolean.of(right == left)) + } + + protected override fun handleNotEquals() { + val right = popStack() + val left = popStack() + if (left is LMetaObject) { + val metaEquals = left.value.getOrElse(LString("equals")) { + stack.add(LBoolean.of(left.value != right)) + return + } + if (metaEquals is LFunction) { + this.customReturn = { + if (it is LMetaObject) { + throw LeanRuntimeException( + "Meta object '$left' returned a meta object as 'equals'.", + control.stackTrace() + ) + } + stack.add(LBoolean.of(!it.truth())) } - stack.add(LCompiledFunction(code, f, runtime, scope)) - } - Opcode.PUSH_CHAR -> { - val value = insn.immediate.toChar() - if (value != (-1).toChar()) { - stack.add(LString(value.toString())) - } else { - stack.add(LString("")) + control.push(metaEquals.setupContext(control, left, listOf(right), runtime)) + return + } + if (metaEquals is LMetaObject) { + throw LeanRuntimeException("Meta object '$left' has a 'equals' meta object.", control.stackTrace()) + } + stack.add(LBoolean.of(metaEquals != right)) + return + } + if (right is LMetaObject) { + val metaEquals = right.value.getOrElse(LString("equals")) { + stack.add(LBoolean.of(left != right.value)) + return + } + if (metaEquals is LFunction) { + this.customReturn = { + if (it is LMetaObject) { + throw LeanRuntimeException( + "Meta object '$right' returned a meta object as 'equals'.", + control.stackTrace() + ) + } + stack.add(LBoolean.of(!it.truth())) } + control.push(metaEquals.setupContext(control, right, listOf(left), runtime)) + return } - Opcode.PUSH_DECIMAL -> { - stack.add(LDecimal(insn.immediate.toDouble())) + if (metaEquals is LMetaObject) { + throw LeanRuntimeException("Meta object '$right' has a 'equals' meta object.", control.stackTrace()) } - Opcode.PUSH_INTEGER -> { - stack.add(LInteger(insn.immediate.toLong())) + stack.add(LBoolean.of(metaEquals != left)) + return + } + stack.add(LBoolean.of(right != left)) + } + + protected override fun handleComparison(comparison: Comparison) { + val right = popStack() + val left = popStack() + if (left is LString && right is LString) { + stack.add(LBoolean.of(comparison.block(left.value.compareTo(right.value)))) + return + } + if (left is LNumber && right is LNumber) { + stack.add(LBoolean.of(comparison.block(left.compareTo(right)))) + return + } + if (left is LMetaObject) { + val operatorProperty = left.access(LString(comparison.name.lowercase())) + if (operatorProperty is LFunction) { + control.push(operatorProperty.setupContext(control, left, listOf(right), runtime)) + return + } else if (operatorProperty != null) { + stack.add(operatorProperty) + return } - Opcode.PUSH_EXCEPTION_HANDLING -> { - exceptionHandlers.add( - ExceptionHandler( - stack.size, - node.findJump(insn.immediate)?.at ?: throw MalformedBytecodeException( - "Tried to compute value of exception handling's catch label ${insn.immediate} which wasn't defined.", + + val comparingProperty = left.access(LString("compareTo")) + if (comparingProperty is LFunction) { + customReturn = { + if (it is LMetaObject) { + throw LeanRuntimeException( + "Meta object '$left' returned a meta object as 'compareTo'.", + control.stackTrace() + ) + } + if (it !is LNumber) { + throw LeanRuntimeException( + "Meta object '$left' returned a non-number as 'compareTo'.", control.stackTrace() ) - ) - ) - } - Opcode.SET_MEMBER_PROPERTY -> handleSetMemberProperty(insn.immediate) - Opcode.SET_SUBSCRIPT -> handleSetSubscript(insn.immediate) - Opcode.SET_VARIABLE -> { - val s = code.sConstArr.getOrElse(insn.immediate) { - throw MalformedBytecodeException( - "Tried to load string constant ${insn.immediate} which wasn't defined.", - control.stackTrace() - ) + } + stack.add(LBoolean.of(comparison.block(it.decimalValue.sign.toInt()))) } - scope.set(s, popStack()) + control.push(comparingProperty.setupContext(control, left, listOf(right), runtime)) + return } } + stack.add(runtime.customComparison(control, comparison, left, right)) } - override fun onReturn(value: LAny) { - stack.add(value) + protected override fun handleIn() { + val right = popStack() + val left = popStack() + if (right is LArray) { + stack.add(LBoolean.of(left in right.value)) + return + } + if (right is LObject) { + stack.add(LBoolean.of(left in right.value)) + return + } + if (right is LMetaObject) { + val property = right.access(LString("contains")) + if (property is LFunction) { + control.push(property.setupContext(control, right, listOf(left), runtime)) + return + } + } + stack.add(LBoolean.of(runtime.customInOperation(control, left, right))) } - override fun onThrow(value: LAny) { - val handler = exceptionHandlers.removeLastOrNull() - if (handler == null) { - control.onThrow(value) + protected override fun handleRange() { + val right = popStack() + val left = popStack() + if (left is LInteger && right is LInteger) { + stack.add(left..right) return } - if (handler.keepOnStack < stack.size) { - println("WTF? Stack is missing ${handler.keepOnStack - stack.size} items!! This is probably a bug!") - } else if (handler.keepOnStack > stack.size) { - repeat(handler.keepOnStack - stack.size) { popStack() } + if (left is LMetaObject) { + val property = left.access(LString("rangeTo")) + if (property is LFunction) { + control.push(property.setupContext(control, left, listOf(right), runtime)) + return + } } - next = handler.onException - stack.add(value) + stack.add(runtime.customRangeOperation(control, left, right)) } - override fun trace(): StackTrace? { - val label = node.findSect(next - 1) ?: return null - val section = code.sectArr.getOrElse(label.index) { return null } - val s = code.sConstArr.getOrElse(section.nameConst) { + protected override fun handleAssign(immediate: Int) { + val s = code.sConstArr.getOrElse(immediate) { throw MalformedBytecodeException( - "Tried to load string constant ${section.nameConst} which wasn't defined.", + "Tried to load string constant $immediate which wasn't defined.", control.stackTrace() ) } - return StackTrace(functionName, s, section.line, section.column) - } - - public data class ExceptionHandler(val keepOnStack: Int, val onException: Int) - - private fun popStack(): LAny { - return stack.removeLastOrNull() ?: throw MalformedBytecodeException( - "Tried to remove an item from the stack, but the stack is empty.", - control.stackTrace() - ) + scope.set(s, popStack()) } - private fun peekStack(): LAny { - return stack.lastOrNull() ?: throw MalformedBytecodeException( - "Tried to get the last item from the stack, but the stack is empty.", + protected override fun handleBranchIf(b: Boolean, labelCode: Int) { + val target = popStack() + val i = node.findJump(labelCode)?.at ?: throw MalformedBytecodeException( + "Tried to branch to label $labelCode which wasn't defined.", control.stackTrace() ) + if (target is LMetaObject) { + val property = target.access(LString("truth")) + if (property is LFunction) { + customReturn = { + if (it is LMetaObject) { + throw LeanRuntimeException( + "Meta object '$target' returned a meta object as 'truth'.", + control.stackTrace() + ) + } + if (it.truth() == b) { + next = i + } + } + control.push(property.setupContext(control, target, emptyList(), runtime)) + return + } else if (property != null) { + if (property is LMetaObject) { + throw LeanRuntimeException( + "Meta object '$target' has a meta object as 'truth'.", + control.stackTrace() + ) + } + if (property.truth() == b) { + next = i + } + return + } + } + if (target.truth() == b) { + next = i + } } - // handlers - - private fun handleBranchIf(value: Boolean, labelCode: Int) { - // TODO handle metaobjects - // Difficulty: hard (post-function call logic is required) - // Fix: create a BranchExecutionContext that can be used to handle this. - // Other fix: replace the LeanContext onReturn/onThrow logic with a per-case. - // Possible workaround: would decrementing the PC once (making this be re-executed) make the VM handle this? - if (popStack().truth() == value) { - next = node.findJump(labelCode)?.at ?: throw MalformedBytecodeException( - "Tried to branch to label $labelCode which wasn't defined.", + protected override fun handleDeclareVariable(mutable: Boolean, immediate: Int) { + val s = code.sConstArr.getOrElse(immediate) { + throw MalformedBytecodeException( + "Tried to load string constant $immediate which wasn't defined.", control.stackTrace() ) } + scope.define(s, mutable) } - private fun handleGetMemberProperty(nameConst: Int) { + protected override fun handleGetMemberProperty(nameConst: Int) { val target = popStack() val name = code.sConstArr.getOrElse(nameConst) { throw MalformedBytecodeException( @@ -354,15 +670,24 @@ public class NodeExecutionContext( "Tried to access member '$name' of null target.", control.stackTrace() ) } - - // TODO handle metaobjects - // Difficulty: easy (adding a function context will be enough) + if (target is LMetaObject) { + val property = target.access(LString("getMember")) + if (property is LFunction) { + control.push(property.setupContext(control, target, listOf(LString(name)), runtime)) + return + } + } stack.add(runtime.getMemberProperty(control, target, name)) } - private fun handleGetSubscript(size: Int) { + protected override fun handleGetSubscript(size: Int) { val arguments = List(size) { popStack() }.reversed() val parent = popStack() + if (parent is LNull) { + throw LeanNullPointerException( + "Tried to access subscript of null target.", control.stackTrace() + ) + } if (parent is LArray && size == 1) { val arg = arguments.first() val list = parent.value @@ -461,22 +786,33 @@ public class NodeExecutionContext( return } } - - // TODO handle metaobjects - // Difficulty: easy (adding a function context will be enough) + if (parent is LMetaObject) { + val property = parent.access(LString("getSubscript")) + if (property is LFunction) { + control.push(property.setupContext(control, parent, arguments, runtime)) + return + } + } stack.add(runtime.customGetSubscript(control, parent, arguments)) } - private fun handleInvoke(size: Int) { + protected override fun handleGetVariable(immediate: Int) { + val s = code.sConstArr.getOrElse(immediate) { + throw MalformedBytecodeException( + "Tried to load string constant $immediate which wasn't defined.", + control.stackTrace() + ) + } + stack.add(scope.get(s)) + } + + protected override fun handleInvoke(size: Int) { val arguments = List(size) { popStack() }.reversed() val function = popStack() - invocation(null, function, arguments) + setupInvocation(null, function, arguments) } - private fun handleInvokeLocal(immediate: Int) { - val nameConst: Int = immediate shr 16 - val size: Int = immediate and 0xff - + protected override fun handleInvokeLocal(nameConst: Int, size: Int) { val arguments = List(size) { popStack() }.reversed() val s = code.sConstArr.getOrElse(nameConst) { throw MalformedBytecodeException( @@ -484,13 +820,10 @@ public class NodeExecutionContext( control.stackTrace() ) } - invocation(null, scope.get(s), arguments) + setupInvocation(null, scope.get(s), arguments) } - private fun handleInvokeMember(immediate: Int) { - val nameConst: Int = immediate shr 16 - val size: Int = immediate and 0xff - + protected override fun handleInvokeMember(nameConst: Int, size: Int) { val arguments = List(size) { popStack() }.reversed() val parent = popStack() val s = code.sConstArr.getOrElse(nameConst) { @@ -499,199 +832,155 @@ public class NodeExecutionContext( control.stackTrace() ) } - invocation(parent, runtime.getMemberProperty(control, parent, s), arguments) + setupInvocation(parent, runtime.getMemberProperty(control, parent, s), arguments) } - private fun handleInvokeExtension(size: Int) { + protected override fun handleInvokeExtension(size: Int) { val arguments = List(size) { popStack() }.reversed() val function = popStack() val target = popStack() - invocation(target, function, arguments) + setupInvocation(target, function, arguments) } - private fun handleSetMemberProperty(nameConst: Int) { - val value = popStack() - val name = code.sConstArr.getOrElse(nameConst) { + protected override fun handleJump(immediate: Int) { + next = node.findJump(immediate)?.at ?: throw MalformedBytecodeException( + "Tried to jump to label $immediate which wasn't defined.", + control.stackTrace() + ) + } + + protected override fun handleLoadDecimal(immediate: Int) { + val l = code.lConstArr.getOrElse(immediate) { throw MalformedBytecodeException( - "Tried to load string constant $nameConst which wasn't defined.", + "Tried to load number constant $immediate which wasn't defined.", control.stackTrace() ) } - val parent = popStack() - if (parent is LObject) { - parent.value[LString(name)] = value - } - - // TODO handle metaobjects - // Difficulty: easy (adding a function context will be enough) - runtime.setMemberProperty(control, parent, name, value) + stack.add(LDecimal(Double.fromBits(l))) } - private fun handleSetSubscript(size: Int) { - val value = popStack() - val arguments = List(size) { popStack() }.reversed() - val parent = popStack() - if (parent is LArray && size == 1) { - val arg = arguments.first() - if (arg is LInteger) { - parent.value[arg.value.toInt()] = value - return - } - } - if (parent is LObject && size == 1) { - val arg = arguments.first() - parent.value[arg] = value - return + protected override fun handleLoadInteger(immediate: Int) { + val i = code.lConstArr.getOrElse(immediate) { + throw MalformedBytecodeException( + "Tried to load number constant $immediate which wasn't defined.", + control.stackTrace() + ) } - // TODO handle metaobjects - // Difficulty: easy (adding a function context will be enough) - runtime.customSetSubscript(control, parent, arguments, value) + stack.add(LInteger(i)) } - private fun handleAddOperation() { - val right = popStack() - val left = popStack() - if (left is LString || right is LString) { - stack.add(LString(left.toString() + right.toString())) - return - } - if (left is LArray && right is LArray) { - stack.add(LArray((left.value + right.value).toMutableList())) - return - } - if (left is LNumber && right is LNumber) { - stack.add(left + right) - return + protected override fun handleLoadString(immediate: Int) { + val s = code.sConstArr.getOrElse(immediate) { + throw MalformedBytecodeException( + "Tried to load string constant $immediate which wasn't defined.", + control.stackTrace() + ) } - // TODO handle metaobjects - // Difficulty: easy (adding a function context will be enough) - stack.add(runtime.customAddOperation(control, left, right)) + stack.add(LString(s)) } - private fun handleDivideOperation() { - val right = popStack() - val left = popStack() - if (left is LNumber && right is LNumber) { - stack.add(left / right) - return + protected override fun handleNewFunction(immediate: Int) { + val f = code.funcArr.getOrElse(immediate) { + throw MalformedBytecodeException( + "Tried to load function $immediate which wasn't defined.", + control.stackTrace() + ) } - // TODO handle metaobjects - // Difficulty: easy (adding a function context will be enough) - stack.add(runtime.customDivideOperation(control, left, right)) + stack.add(LCompiledFunction(code, f, runtime, scope)) } - private fun handleMultiplyOperation() { - val right = popStack() - val left = popStack() - if (left is LString && right is LInteger) { - stack.add(LString(left.value.repeat(right.value.toInt()))) - } - if (left is LNumber && right is LNumber) { - stack.add(left * right) - return - } - // TODO handle metaobjects - // Difficulty: easy (adding a function context will be enough) - stack.add(runtime.customMultiplyOperation(control, left, right)) + protected override fun handlePushChar(immediate: Int) { + val value = immediate.toChar() + stack.add(LString(if (value != (-1).toChar()) value.toString() else "")) } - private fun handleRangeOperation() { - val right = popStack() - val left = popStack() - if (left is LInteger && right is LInteger) { - stack.add(left..right) - return - } - // TODO handle metaobjects - // Difficulty: easy (adding a function context will be enough) - stack.add(runtime.customRangeOperation(control, left, right)) + protected override fun handlePushDecimal(immediate: Int) { + stack.add(LDecimal(immediate.toDouble())) } - private fun handleRemainingOperation() { - val right = popStack() - val left = popStack() - if (left is LNumber && right is LNumber) { - stack.add(left % right) - return - } - // TODO handle metaobjects - // Difficulty: easy (adding a function context will be enough) - stack.add(runtime.customRemainingOperation(control, left, right)) + protected override fun handlePushInteger(immediate: Int) { + stack.add(LInteger(immediate.toLong())) } - private fun handleSubtractOperation() { - val right = popStack() - val left = popStack() - if (left is LNumber && right is LNumber) { - stack.add(left - right) - return - } - // TODO handle metaobjects - // Difficulty: easy (adding a function context will be enough) - stack.add(runtime.customSubtractOperation(control, left, right)) + protected override fun handlePushExceptionHandling(immediate: Int) { + exceptionHandlers.add( + ExceptionHandler( + stack.size, + node.findJump(immediate)?.at ?: throw MalformedBytecodeException( + "Tried to compute value of exception handling's catch label $immediate which wasn't defined.", + control.stackTrace() + ) + ) + ) } - private fun handleComparison(comparison: Comparison) { - val right = popStack() - val left = popStack() - if (left is LString && right is LString) { - stack.add(LBoolean.of(comparison.block(left.value.compareTo(right.value)))) - return + protected override fun handleSetMemberProperty(nameConst: Int) { + val value = popStack() + val name = code.sConstArr.getOrElse(nameConst) { + throw MalformedBytecodeException( + "Tried to load string constant $nameConst which wasn't defined.", + control.stackTrace() + ) } - if (left is LNumber && right is LNumber) { - stack.add(LBoolean.of(comparison.block(left.compareTo(right)))) - return + val parent = popStack() + if (parent is LNull) { + throw LeanNullPointerException( + "Tried to access member '$name' of null target.", control.stackTrace() + ) } - // TODO handle metaobjects - // Difficulty: easy (adding a function context will be enough) - stack.add(runtime.customComparison(control, comparison, left, right)) - } - - private fun handleInOperation() { - val right = popStack() - val left = popStack() - if (right is LArray) { - stack.add(LBoolean.of(left in right.value)) - return + if (parent is LObject) { + parent.value[LString(name)] = value } - if (right is LObject) { - stack.add(LBoolean.of(left in right.value)) - return + if (parent is LMetaObject) { + val property = parent.access(LString("setMember")) + if (property is LFunction) { + control.push(property.setupContext(control, parent, listOf(LString(name), value), runtime)) + return + } } - // TODO handle metaobjects - // Difficulty: easy (adding a function context will be enough) - stack.add(LBoolean.of(runtime.customInOperation(control, left, right))) + runtime.setMemberProperty(control, parent, name, value) } - private fun handleNegativeOperation() { - val target = popStack() - if (target is LNumber) { - stack.add(-target) - return + protected override fun handleSetSubscript(size: Int) { + val value = popStack() + val arguments = List(size) { popStack() }.reversed() + val parent = popStack() + if (parent is LNull) { + throw LeanNullPointerException( + "Tried to access subscript of null target.", control.stackTrace() + ) } - // TODO handle metaobjects - // Difficulty: easy (adding a function context will be enough) - stack.add(runtime.customNegativeOperation(control, target)) - } - - private fun handlePositiveOperation() { - val target = popStack() - if (target is LNumber) { - stack.add(+target) + if (parent is LArray && size == 1) { + val arg = arguments.first() + if (arg is LInteger) { + parent.value[arg.value.toInt()] = value + return + } + } + if (parent is LObject && size == 1) { + val arg = arguments.first() + parent.value[arg] = value return } - // TODO handle metaobjects - // Difficulty: easy (adding a function context will be enough) - stack.add(runtime.customPositiveOperation(control, target)) + if (parent is LMetaObject) { + val property = parent.access(LString("setSubscript")) + if (property is LFunction) { + control.push(property.setupContext(control, parent, arguments + value, runtime)) + return + } + } + runtime.customSetSubscript(control, parent, arguments, value) } - private fun invocation(thisValue: LAny?, function: LAny, args: List) { - if (function is LFunction) { - control.push(function.setupContext(control, thisValue, args, runtime)) - return + protected override fun handleSetVariable(immediate: Int) { + val s = code.sConstArr.getOrElse(immediate) { + throw MalformedBytecodeException( + "Tried to load string constant $immediate which wasn't defined.", + control.stackTrace() + ) } - // TODO handle metaobjects - // Difficulty: easy (adding a function context will be enough) - runtime.customInvocation(control, thisValue, function, args) + scope.set(s, popStack()) } + + public data class ExceptionHandler(val keepOnStack: Int, val onException: Int) } diff --git a/src/commonMain/kotlin/net/adriantodt/leanvm/runtimes/DefaultLeanRuntime.kt b/src/commonMain/kotlin/net/adriantodt/leanvm/runtimes/DefaultLeanRuntime.kt index 65d378b..9275687 100644 --- a/src/commonMain/kotlin/net/adriantodt/leanvm/runtimes/DefaultLeanRuntime.kt +++ b/src/commonMain/kotlin/net/adriantodt/leanvm/runtimes/DefaultLeanRuntime.kt @@ -2,7 +2,9 @@ package net.adriantodt.leanvm.runtimes import net.adriantodt.leanvm.Scope import net.adriantodt.leanvm.context.LeanRuntime +import net.adriantodt.leanvm.runtimes.functions.FnCreateMetaObject import net.adriantodt.leanvm.runtimes.functions.FnEmpty +import net.adriantodt.leanvm.runtimes.functions.FnNotEmpty import net.adriantodt.leanvm.runtimes.functions.FnSize public class DefaultLeanRuntime : LeanRuntime() { @@ -10,7 +12,8 @@ public class DefaultLeanRuntime : LeanRuntime() { val scope = Scope() scope.define("size", false, FnSize) scope.define("empty", false, FnEmpty) - scope.define("notEmpty", false, FnEmpty) + scope.define("notEmpty", false, FnNotEmpty) + scope.define("createMetaObject", false, FnCreateMetaObject) return scope } } diff --git a/src/commonMain/kotlin/net/adriantodt/leanvm/runtimes/functions/FnCreateMetaObject.kt b/src/commonMain/kotlin/net/adriantodt/leanvm/runtimes/functions/FnCreateMetaObject.kt index b6bba98..be7d84d 100644 --- a/src/commonMain/kotlin/net/adriantodt/leanvm/runtimes/functions/FnCreateMetaObject.kt +++ b/src/commonMain/kotlin/net/adriantodt/leanvm/runtimes/functions/FnCreateMetaObject.kt @@ -1,6 +1,7 @@ package net.adriantodt.leanvm.runtimes.functions import net.adriantodt.leanvm.types.LAny +import net.adriantodt.leanvm.types.LMetaObject import net.adriantodt.leanvm.types.LNativeFunction import net.adriantodt.leanvm.types.LObject @@ -26,6 +27,6 @@ internal object FnCreateMetaObject : LNativeFunction("createMetaObject") { throw IllegalArgumentException("createMetaObject() can only be called on objects.") } - TODO("Implement meta objects") + return LMetaObject(value.value) } } diff --git a/src/commonMain/kotlin/net/adriantodt/leanvm/types/LDecimal.kt b/src/commonMain/kotlin/net/adriantodt/leanvm/types/LDecimal.kt index d4bf907..a1278e2 100644 --- a/src/commonMain/kotlin/net/adriantodt/leanvm/types/LDecimal.kt +++ b/src/commonMain/kotlin/net/adriantodt/leanvm/types/LDecimal.kt @@ -4,7 +4,6 @@ public data class LDecimal(val value: Double) : LNumber() { override val type: String get() = "decimal" override val decimalValue: Double get() = value - override fun compareTo(other: LNumber): Int { return value.compareTo(other.decimalValue) } diff --git a/src/commonMain/kotlin/net/adriantodt/leanvm/types/LMetaObject.kt b/src/commonMain/kotlin/net/adriantodt/leanvm/types/LMetaObject.kt new file mode 100644 index 0000000..97d76e0 --- /dev/null +++ b/src/commonMain/kotlin/net/adriantodt/leanvm/types/LMetaObject.kt @@ -0,0 +1,37 @@ +package net.adriantodt.leanvm.types + +public data class LMetaObject(val value: MutableMap = mutableMapOf()) : LAny() { + override val type: String get() = "object" + + public constructor(vararg pairs: Pair) : this(pairs.toMap(mutableMapOf())) + + public fun access(key: LAny): LAny? = value[key] + + override fun truth(): Boolean { + val metaTruth = value.getOrElse(LString("truth")) { + return value.isNotEmpty() + } + if (metaTruth is LFunction) { + val truth = metaTruth.call(this, emptyList()) + return truth.truth() + } + return metaTruth.truth() + } + + override fun toString(): String { + val metaToString = value.getOrElse(LString("toString")) { + return value.toString() + } + if (metaToString is LFunction) { + val toString = metaToString.call(this, emptyList()) + return toString.toString() + } + return metaToString.toString() + } + + public companion object { + public fun of(vararg pairs: Pair): LMetaObject { + return LMetaObject(pairs.toMap(mutableMapOf())) + } + } +} diff --git a/src/commonMain/kotlin/net/adriantodt/leanvm/types/LObject.kt b/src/commonMain/kotlin/net/adriantodt/leanvm/types/LObject.kt index 6f9afb0..8099c3c 100644 --- a/src/commonMain/kotlin/net/adriantodt/leanvm/types/LObject.kt +++ b/src/commonMain/kotlin/net/adriantodt/leanvm/types/LObject.kt @@ -19,3 +19,4 @@ public data class LObject(val value: MutableMap = mutableMapOf()) : } } } + diff --git a/src/commonMain/kotlin/net/adriantodt/leanvm/utils/InstructionDecoder.kt b/src/commonMain/kotlin/net/adriantodt/leanvm/utils/InstructionDecoder.kt new file mode 100644 index 0000000..43855f9 --- /dev/null +++ b/src/commonMain/kotlin/net/adriantodt/leanvm/utils/InstructionDecoder.kt @@ -0,0 +1,143 @@ +package net.adriantodt.leanvm.utils + +import net.adriantodt.leanvm.bytecode.LeanInsn +import net.adriantodt.leanvm.bytecode.LeanInsn.Opcode +import net.adriantodt.leanvm.bytecode.LeanInsn.ParameterlessCode +import net.adriantodt.leanvm.context.LeanMachineControl +import net.adriantodt.leanvm.exceptions.MalformedBytecodeException + +public abstract class InstructionDecoder { + protected abstract val control: LeanMachineControl + + protected open fun handle(insn: LeanInsn) { + val opcodes = Opcode.values() + val parameterlessCodes = ParameterlessCode.values() + + if (insn.opcode !in opcodes.indices) { + throw MalformedBytecodeException("Invalid opcode ${insn.opcode}", control.stackTrace()) + } + when (opcodes[insn.opcode]) { + Opcode.PARAMETERLESS -> { + if (insn.immediate !in parameterlessCodes.indices) { + throw MalformedBytecodeException( + "Invalid parameterless code ${insn.immediate}", + control.stackTrace() + ) + } + + when (parameterlessCodes[insn.immediate]) { + ParameterlessCode.ARRAY_INSERT -> handleArrayInsert() + ParameterlessCode.DUP -> handleDup() + ParameterlessCode.NEW_ARRAY -> handleNewArray() + ParameterlessCode.NEW_OBJECT -> handleNewObject() + ParameterlessCode.OBJECT_INSERT -> handleObjectInsert() + ParameterlessCode.POP -> handlePop() + ParameterlessCode.POP_SCOPE -> handlePopScope() + ParameterlessCode.POP_EXCEPTION_HANDLING -> handlePopExceptionHandling() + ParameterlessCode.PUSH_NULL -> handlePushNull() + ParameterlessCode.PUSH_SCOPE -> handlePushScope() + ParameterlessCode.PUSH_THIS -> handlePushThis() + ParameterlessCode.RETURN -> handleReturn() + ParameterlessCode.THROW -> handleThrow() + ParameterlessCode.TYPEOF -> handleTypeof() + ParameterlessCode.PUSH_TRUE -> handlePushBoolean(true) + ParameterlessCode.PUSH_FALSE -> handlePushBoolean(false) + ParameterlessCode.POSITIVE -> handlePositive() + ParameterlessCode.NEGATIVE -> handleNegative() + ParameterlessCode.TRUTH -> handleTruth() + ParameterlessCode.NOT -> handleNot() + ParameterlessCode.ADD -> handleAdd() + ParameterlessCode.SUBTRACT -> handleSubtract() + ParameterlessCode.MULTIPLY -> handleMultiply() + ParameterlessCode.DIVIDE -> handleDivide() + ParameterlessCode.REMAINING -> handleRemaining() + ParameterlessCode.EQUALS -> handleEquals() + ParameterlessCode.NOT_EQUALS -> handleNotEquals() + ParameterlessCode.LT -> handleComparison(Comparison.LT) + ParameterlessCode.LTE -> handleComparison(Comparison.LTE) + ParameterlessCode.GT -> handleComparison(Comparison.GT) + ParameterlessCode.GTE -> handleComparison(Comparison.GTE) + ParameterlessCode.IN -> handleIn() + ParameterlessCode.RANGE -> handleRange() + } + } + + Opcode.ASSIGN -> handleAssign(insn.immediate) + Opcode.BRANCH_IF_FALSE -> handleBranchIf(false, insn.immediate) + Opcode.BRANCH_IF_TRUE -> handleBranchIf(true, insn.immediate) + Opcode.DECLARE_VARIABLE_IMMUTABLE -> handleDeclareVariable(false, insn.immediate) + Opcode.DECLARE_VARIABLE_MUTABLE -> handleDeclareVariable(true, insn.immediate) + Opcode.GET_MEMBER_PROPERTY -> handleGetMemberProperty(insn.immediate) + Opcode.GET_SUBSCRIPT -> handleGetSubscript(insn.immediate) + Opcode.GET_VARIABLE -> handleGetVariable(insn.immediate) + Opcode.INVOKE -> handleInvoke(insn.immediate) + Opcode.INVOKE_LOCAL -> handleInvokeLocal(insn.immediate shr 16, insn.immediate and 0xff) + Opcode.INVOKE_MEMBER -> handleInvokeMember(insn.immediate shr 16, insn.immediate and 0xff) + Opcode.INVOKE_EXTENSION -> handleInvokeExtension(insn.immediate) + Opcode.JUMP -> handleJump(insn.immediate) + Opcode.LOAD_DECIMAL -> handleLoadDecimal(insn.immediate) + Opcode.LOAD_INTEGER -> handleLoadInteger(insn.immediate) + Opcode.LOAD_STRING -> handleLoadString(insn.immediate) + Opcode.NEW_FUNCTION -> handleNewFunction(insn.immediate) + Opcode.PUSH_CHAR -> handlePushChar(insn.immediate) + Opcode.PUSH_DECIMAL -> handlePushDecimal(insn.immediate) + Opcode.PUSH_INTEGER -> handlePushInteger(insn.immediate) + Opcode.PUSH_EXCEPTION_HANDLING -> handlePushExceptionHandling(insn.immediate) + Opcode.SET_MEMBER_PROPERTY -> handleSetMemberProperty(insn.immediate) + Opcode.SET_SUBSCRIPT -> handleSetSubscript(insn.immediate) + Opcode.SET_VARIABLE -> handleSetVariable(insn.immediate) + } + } + + protected abstract fun handleArrayInsert() + protected abstract fun handleDup() + protected abstract fun handleNewArray() + protected abstract fun handleNewObject() + protected abstract fun handleObjectInsert() + protected abstract fun handlePop() + protected abstract fun handlePopScope() + protected abstract fun handlePopExceptionHandling() + protected abstract fun handlePushNull() + protected abstract fun handlePushScope() + protected abstract fun handlePushThis() + protected abstract fun handleReturn() + protected abstract fun handleThrow() + protected abstract fun handleTypeof() + protected abstract fun handlePushBoolean(b: Boolean) + protected abstract fun handlePositive() + protected abstract fun handleNegative() + protected abstract fun handleTruth() + protected abstract fun handleNot() + protected abstract fun handleAdd() + protected abstract fun handleSubtract() + protected abstract fun handleMultiply() + protected abstract fun handleDivide() + protected abstract fun handleRemaining() + protected abstract fun handleEquals() + protected abstract fun handleNotEquals() + protected abstract fun handleComparison(comparison: Comparison) + protected abstract fun handleIn() + protected abstract fun handleRange() + protected abstract fun handleAssign(immediate: Int) + protected abstract fun handleBranchIf(b: Boolean, labelCode: Int) + protected abstract fun handleDeclareVariable(mutable: Boolean, immediate: Int) + protected abstract fun handleGetMemberProperty(nameConst: Int) + protected abstract fun handleGetSubscript(size: Int) + protected abstract fun handleGetVariable(immediate: Int) + protected abstract fun handleInvoke(size: Int) + protected abstract fun handleInvokeLocal(nameConst: Int, size: Int) + protected abstract fun handleInvokeMember(nameConst: Int, size: Int) + protected abstract fun handleInvokeExtension(size: Int) + protected abstract fun handleJump(immediate: Int) + protected abstract fun handleLoadDecimal(immediate: Int) + protected abstract fun handleLoadInteger(immediate: Int) + protected abstract fun handleLoadString(immediate: Int) + protected abstract fun handleNewFunction(immediate: Int) + protected abstract fun handlePushChar(immediate: Int) + protected abstract fun handlePushDecimal(immediate: Int) + protected abstract fun handlePushInteger(immediate: Int) + protected abstract fun handlePushExceptionHandling(immediate: Int) + protected abstract fun handleSetMemberProperty(nameConst: Int) + protected abstract fun handleSetSubscript(size: Int) + protected abstract fun handleSetVariable(immediate: Int) +} diff --git a/src/commonTest/kotlin/net/adriantodt/leanvm/bytecode/LeanCodeTest.kt b/src/commonTest/kotlin/net/adriantodt/leanvm/bytecode/LeanCodeTest.kt index 653b3b6..8145feb 100644 --- a/src/commonTest/kotlin/net/adriantodt/leanvm/bytecode/LeanCodeTest.kt +++ b/src/commonTest/kotlin/net/adriantodt/leanvm/bytecode/LeanCodeTest.kt @@ -21,7 +21,7 @@ class LeanCodeTest { val builder = LeanCodeBuilder() val node = builder.newNodeBuilder() node.pushIntegerInsn(1L) - repeat(2) { + repeat(200) { node.pushIntegerInsn(1L) node.addInsn() node.pushIntegerInsn(1L)