Skip to content

Commit

Permalink
fix(interpreter): introduced operand stack
Browse files Browse the repository at this point in the history
  • Loading branch information
slisson committed Oct 21, 2024
1 parent 453af55 commit d164f1b
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 64 deletions.
35 changes: 18 additions & 17 deletions commitlint.config.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
module.exports = {
extends: ["@commitlint/config-conventional"],
rules: {
"scope-enum": [
2,
"always",
[
"deps",
"projectional-editor",
"mps-plugin",
],
extends: ["@commitlint/config-conventional"],
rules: {
"scope-enum": [
2,
"always",
[
"deps",
"projectional-editor",
"mps-plugin",
"interpreter"
],
],
"subject-case": [0, 'never'],
"body-max-line-length": [0, 'always'],
"footer-max-line-length": [0, 'always']
},
ignores: [
(message) => message.includes('skip-lint')
],
"subject-case": [0, 'never'],
"body-max-line-length": [0, 'always'],
"footer-max-line-length": [0, 'always']
},
ignores: [
(message) => message.includes('skip-lint')
],
};
Original file line number Diff line number Diff line change
@@ -1,53 +1,77 @@
package org.modelix.interpreter.vm.core

class CallInstruction(val entryPoint: Instruction, val parameterSourceKey: List<MemoryKey<*>>, val resultTargetKeys: List<MemoryKey<*>>) : Instruction() {
class CallInstruction(val entryPoint: Instruction, val parameterCount: Int) : Instruction() {

override fun execute(state: VMState): VMState {
val newFrame = StackFrame(returnTo = next, resultTargetKeys = resultTargetKeys)
var newState = state.copy(callStack = state.callStack.pushFrame(newFrame), nextInstruction = entryPoint)
parameterSourceKey.forEachIndexed { index, key ->
newState = newState.writeMemory(ParameterKey(index), state.readMemory(key))
var newFrame = StackFrame(returnTo = next)
var newState = state
for (i in 0 until parameterCount) {
newState.popOperand().let {
newState = it.second
newFrame = newFrame.writeLocalMemory(ParameterKey<Any?>(i), it.first)
}
}
newState = newState.copy(callStack = newState.callStack.pushFrame(newFrame), nextInstruction = entryPoint)
return newState
}
}

class ReturnInstruction(val returnValues: List<MemoryKey<*>>) : Instruction() {
class ReturnInstruction() : Instruction() {
override fun execute(state: VMState): VMState {
val (newCallStack, currentFrame) = state.callStack.popFrame()
check(currentFrame.resultTargetKeys.size == returnValues.size) {
"Caller expected ${currentFrame.resultTargetKeys.size} return values, but function returns ${returnValues.size} values"
}
var newState = state.copy(nextInstruction = currentFrame.returnTo, callStack = newCallStack)
returnValues.forEachIndexed { index, key ->
newState = newState.writeMemory(currentFrame.resultTargetKeys[index] as MemoryKey<Any?>, state.readMemory(key))
}
return newState
check(currentFrame.operandStack.size == 1) { "Operand stack should contain a single value, but was: " + currentFrame.operandStack }
return state
.copy(nextInstruction = currentFrame.returnTo, callStack = newCallStack)
.pushOperand(currentFrame.operandStack.single())
}
}

class LoadConstantInstruction<E>(val value: E, val target: MemoryKey<in E>) : Instruction() {
class PushConstantInstruction<E>(val value: E) : Instruction() {
override fun execute(state: VMState): VMState {
return state.writeMemory(target, value)
return state.pushOperand(value)
}
}

abstract class BinaryOperationInstruction<Arg1, Arg2, Result>(val arg1: MemoryKey<out Arg1>, val arg2: MemoryKey<out Arg2>, val result: MemoryKey<in Result>) : Instruction() {
class StoreInstruction<E>(val target: MemoryKey<in E>) : Instruction() {
override fun execute(state: VMState): VMState {
return state.popOperand().let { (value, newState) -> newState.writeMemory(target, value as E) }
}
}

class LoadInstruction<E>(val source: MemoryKey<out E>) : Instruction() {
override fun execute(state: VMState): VMState {
return state.pushOperand(state.readMemory(source))
}
}

abstract class BinaryOperationInstruction<Arg1, Arg2, Result>() : Instruction() {

abstract fun apply(arg1: Arg1, arg2: Arg2): Result

override fun execute(state: VMState): VMState {
return state.writeMemory(result, apply(state.readMemory(arg1), state.readMemory(arg2)))
var newState: VMState = state
var arg1: Arg1
var arg2: Arg2
newState.popOperand().let {
arg2 = it.first as Arg2
newState = it.second
}
newState.popOperand().let {
arg1 = it.first as Arg1
newState = it.second
}
newState = newState.pushOperand(apply(arg1, arg2))
return newState
}
}

class AddIntegersInstruction(arg1: MemoryKey<out Int>, arg2: MemoryKey<out Int>, result: MemoryKey<in Int>) : BinaryOperationInstruction<Int, Int, Int>(arg1, arg2, result) {
class AddIntegersInstruction() : BinaryOperationInstruction<Int, Int, Int>() {
override fun apply(arg1: Int, arg2: Int): Int {
return arg1 + arg2
}
}

class MultiplyIntegersInstruction(arg1: MemoryKey<out Int>, arg2: MemoryKey<out Int>, result: MemoryKey<in Int>) : BinaryOperationInstruction<Int, Int, Int>(arg1, arg2, result) {
class MultiplyIntegersInstruction() : BinaryOperationInstruction<Int, Int, Int>() {
override fun apply(arg1: Int, arg2: Int): Int {
return arg1 * arg2
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,22 @@ class InterpreterVM(entryPoint: Instruction) {
data class VMState(
val nextInstruction: Instruction?,
val globalMemory: Memory = Memory(),
val callStack: CallStack = CallStack().pushFrame(StackFrame(returnTo = null, resultTargetKeys = listOf())),
val callStack: CallStack = CallStack().pushFrame(StackFrame(returnTo = null)),
) {
fun <T> readMemory(key: MemoryKey<out T>): T = key.memoryType.getMemory(this).read(key)
fun <T> writeMemory(key: MemoryKey<in T>, value: T): VMState {
return key.memoryType.setMemory(this, key.memoryType.getMemory(this).write(key, value))
}
fun updateCurrentFrame(body: (StackFrame) -> StackFrame): VMState {
return replaceCurrentFrame(body(callStack.currentFrame()))
}
fun replaceCurrentFrame(newFrame: StackFrame): VMState {
return copy(callStack = callStack.updateCurrentFrame(newFrame))
}
fun pushOperand(value: Any?): VMState = updateCurrentFrame { it.pushOperand(value) }
fun popOperand(): Pair<Any?, VMState> {
return callStack.currentFrame().popOperand().let { (value, newFrame) -> value to replaceCurrentFrame(newFrame) }
}
}

data class Memory(private val data: PersistentMap<MemoryKey<*>, Any?> = persistentHashMapOf()) {
Expand Down Expand Up @@ -91,8 +101,14 @@ data class CallStack(val frames: PersistentList<StackFrame> = persistentListOf()
fun size() = frames.size
}

data class StackFrame(val returnTo: Instruction?, val localMemory: Memory = Memory(), val resultTargetKeys: List<MemoryKey<*>>) {
data class StackFrame(
val returnTo: Instruction?,
val localMemory: Memory = Memory(),
val operandStack: PersistentList<Any?> = persistentListOf(),
) {
fun <T> writeLocalMemory(key: MemoryKey<T>, value: T): StackFrame = copy(localMemory = localMemory.write(key, value))
fun pushOperand(value: Any?): StackFrame = copy(operandStack = operandStack.add(value))
fun popOperand(): Pair<Any?, StackFrame> = operandStack.last() to copy(operandStack = operandStack.removeAt(operandStack.lastIndex))
}

abstract class Instruction {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class FunctionBuilder {
}

fun <T> load(value: T, variable: MemoryKey<T>) {
addInstruction(LoadConstantInstruction(value, variable))
addInstruction(PushConstantInstruction(value))
addInstruction(StoreInstruction(variable))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@ class InterpreterTest {

@Test
fun addition() {
val a = MemoryKey<Int>(MemoryType.LOCAL, "a")
val b = MemoryKey<Int>(MemoryType.LOCAL, "b")
val c = MemoryKey<Int>(MemoryType.GLOBAL, "c")

val entryPoint = ProgramBuilder().buildFunction("main") {
addInstruction(LoadConstantInstruction(10, a))
addInstruction(LoadConstantInstruction(20, b))
addInstruction(AddIntegersInstruction(a, b, c))
addInstruction(PushConstantInstruction(10))
addInstruction(PushConstantInstruction(20))
addInstruction(AddIntegersInstruction())
addInstruction(StoreInstruction(c))
}.getEntryPoint()

val finalState = InterpreterVM(entryPoint).run()
Expand All @@ -30,7 +29,10 @@ class InterpreterTest {
val c = MemoryKey<Int>(MemoryType.GLOBAL, "c")

val entryPoint = ProgramBuilder().buildFunction("main") {
addInstruction(AddIntegersInstruction(a, b, c))
addInstruction(LoadInstruction(ParameterKey<Int>(1)))
addInstruction(LoadInstruction(ParameterKey<Int>(0)))
addInstruction(AddIntegersInstruction())
addInstruction(StoreInstruction<Int>(c))
}.getEntryPoint()

val vm = InterpreterVM(entryPoint)
Expand All @@ -46,28 +48,24 @@ class InterpreterTest {
fun functionCall() {
val entryPoint = ProgramBuilder().run {
val plusFunction = buildFunction("plus") {
val r = NamedLocalVarKey<Int>("result")
addInstruction(AddIntegersInstruction(ParameterKey<Int>(0), ParameterKey<Int>(1), r))
addInstruction(ReturnInstruction(listOf(r)))
addInstruction(LoadInstruction(ParameterKey<Int>(0)))
addInstruction(LoadInstruction(ParameterKey<Int>(1)))
addInstruction(AddIntegersInstruction())
addInstruction(ReturnInstruction())
}
val mulFunction = buildFunction("mul") {
val r = NamedLocalVarKey<Int>("result")
addInstruction(MultiplyIntegersInstruction(ParameterKey<Int>(0), ParameterKey<Int>(1), r))
addInstruction(ReturnInstruction(listOf(r)))
addInstruction(LoadInstruction(ParameterKey<Int>(0)))
addInstruction(LoadInstruction(ParameterKey<Int>(1)))
addInstruction(MultiplyIntegersInstruction())
addInstruction(ReturnInstruction())
}
buildFunction("main") {
val a by variable<Int>()
val b by variable<Int>()
val c by variable<Int>()
val product by variable<Int>()
val sum by variable<Int>()

load(7, a)
load(13, b)
load(31, c)
addInstruction(CallInstruction(plusFunction.getEntryPoint(), listOf(a, b), listOf(sum)))
addInstruction(CallInstruction(mulFunction.getEntryPoint(), listOf(sum, c), listOf(product)))
addInstruction(MoveInstruction<Int>(product, NamedGlobalVarKey<Int>("finalResult")))
addInstruction(PushConstantInstruction(31))
addInstruction(PushConstantInstruction(13))
addInstruction(PushConstantInstruction(7))
addInstruction(CallInstruction(plusFunction.getEntryPoint(), 2))
addInstruction(CallInstruction(mulFunction.getEntryPoint(), 2))
addInstruction(StoreInstruction<Int>(NamedGlobalVarKey<Int>("finalResult")))
}.getEntryPoint()
}

Expand Down

0 comments on commit d164f1b

Please sign in to comment.