-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #202 from modelix/lr1-parser-subconcepts
LR1 parser
- Loading branch information
Showing
81 changed files
with
7,327 additions
and
1,470 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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') | ||
], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
plugins { | ||
kotlin("multiplatform") | ||
`maven-publish` | ||
} | ||
|
||
kotlin { | ||
jvmToolchain(17) | ||
jvm() | ||
js(IR) { | ||
browser {} | ||
nodejs { | ||
testTask { | ||
useMocha { | ||
timeout = "10s" | ||
} | ||
} | ||
} | ||
} | ||
|
||
sourceSets { | ||
val commonMain by getting { | ||
dependencies { | ||
implementation(libs.kotlin.collections.immutable) | ||
implementation(coreLibs.modelix.incremental) | ||
} | ||
} | ||
val commonTest by getting { | ||
dependencies { | ||
implementation(kotlin("test")) | ||
} | ||
} | ||
val jvmMain by getting { | ||
dependencies { | ||
} | ||
} | ||
val jvmTest by getting { | ||
dependencies { | ||
} | ||
} | ||
val jsMain by getting { | ||
dependencies { | ||
} | ||
} | ||
val jsTest by getting { | ||
dependencies { | ||
} | ||
} | ||
} | ||
} |
107 changes: 107 additions & 0 deletions
107
interpreter-vm/src/commonMain/kotlin/org/modelix/interpreter/vm/core/Instructions.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
package org.modelix.interpreter.vm.core | ||
|
||
class CallInstruction(val entryPoint: Instruction, val parameterCount: Int) : Instruction() { | ||
|
||
override fun execute(state: VMState): VMState { | ||
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() : Instruction() { | ||
override fun execute(state: VMState): VMState { | ||
val (newCallStack, currentFrame) = state.callStack.popFrame() | ||
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 PushConstantInstruction<E>(val value: E) : Instruction() { | ||
override fun execute(state: VMState): VMState { | ||
return state.pushOperand(value) | ||
} | ||
} | ||
|
||
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 { | ||
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() : BinaryOperationInstruction<Int, Int, Int>() { | ||
override fun apply(arg1: Int, arg2: Int): Int { | ||
return arg1 + arg2 | ||
} | ||
} | ||
|
||
class MultiplyIntegersInstruction() : BinaryOperationInstruction<Int, Int, Int>() { | ||
override fun apply(arg1: Int, arg2: Int): Int { | ||
return arg1 * arg2 | ||
} | ||
} | ||
|
||
class JumpInstruction(val target: Instruction) : Instruction() { | ||
override fun execute(state: VMState): VMState { | ||
return state.copy(nextInstruction = target) | ||
} | ||
} | ||
|
||
class ConditionalJumpInstruction(val condition: MemoryKey<out Boolean>, val target: Instruction) : Instruction() { | ||
override fun execute(state: VMState): VMState { | ||
return if (state.readMemory(condition)) state.copy(nextInstruction = target) else state | ||
} | ||
} | ||
|
||
class MoveInstruction<E>(val source: MemoryKey<out E>, val target: MemoryKey<in E>) : Instruction() { | ||
override fun execute(state: VMState): VMState { | ||
return state.writeMemory(target, state.readMemory(source)) | ||
} | ||
} | ||
|
||
class NoOpInstruction : Instruction() { | ||
override fun execute(state: VMState): VMState { | ||
return state | ||
} | ||
} | ||
|
||
data class NamedGlobalVarKey<E>(val name: String) : MemoryKey<E>(MemoryType.GLOBAL, name) | ||
data class NamedLocalVarKey<E>(val name: String) : MemoryKey<E>(MemoryType.LOCAL, name) | ||
data class ParameterKey<E>(val index: Int) : MemoryKey<E>(MemoryType.LOCAL, "parameter" + index) | ||
data class ReturnValueKey<E>(val index: Int) : MemoryKey<E>(MemoryType.LOCAL, "returnValue" + index) |
117 changes: 117 additions & 0 deletions
117
interpreter-vm/src/commonMain/kotlin/org/modelix/interpreter/vm/core/InterpreterVM.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
package org.modelix.interpreter.vm.core | ||
|
||
import kotlinx.collections.immutable.ImmutableList | ||
import kotlinx.collections.immutable.ImmutableMap | ||
import kotlinx.collections.immutable.PersistentList | ||
import kotlinx.collections.immutable.PersistentMap | ||
import kotlinx.collections.immutable.persistentHashMapOf | ||
import kotlinx.collections.immutable.persistentListOf | ||
import kotlinx.collections.immutable.plus | ||
import org.modelix.incremental.AtomicLong | ||
|
||
class InterpreterVM(entryPoint: Instruction) { | ||
private var state: VMState = VMState(nextInstruction = entryPoint) | ||
|
||
fun isTerminated() = state.nextInstruction == null | ||
fun run(): VMState { | ||
while (!isTerminated()) { | ||
singleStep() | ||
} | ||
return state | ||
} | ||
|
||
fun singleStep(): VMState { | ||
val instruction = checkNotNull(state.nextInstruction) { "No more instructions to execute" } | ||
return instruction.execute(state.copy(nextInstruction = instruction.next)).also { state = it } | ||
} | ||
|
||
fun <T> writeMemory(key: MemoryKey<in T>, value: T) { | ||
state = state.writeMemory(key, value) | ||
} | ||
} | ||
|
||
data class VMState( | ||
val nextInstruction: Instruction?, | ||
val globalMemory: Memory = Memory(), | ||
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()) { | ||
fun hasKey(key: MemoryKey<*>): Boolean = data.containsKey(key) | ||
fun <T> read(key: MemoryKey<out T>): T { | ||
check(hasKey(key)) { "Uninitialized read: $key" } | ||
return data[key] as T | ||
} | ||
fun <T> write(key: MemoryKey<in T>, value: T): Memory = Memory(data.put(key, value)) | ||
fun getEntries(): ImmutableMap<MemoryKey<*>, Any?> = data | ||
} | ||
|
||
private val variableIdSequence = AtomicLong() | ||
open class MemoryKey<E>(val memoryType: MemoryType, val description: String = "var" + variableIdSequence.incrementAndGet()) { | ||
override fun toString() = description | ||
} | ||
|
||
abstract class MemoryType { | ||
abstract fun getMemory(state: VMState): Memory | ||
abstract fun setMemory(state: VMState, memory: Memory): VMState | ||
|
||
companion object { | ||
val GLOBAL: MemoryType = object : MemoryType() { | ||
override fun getMemory(state: VMState): Memory { | ||
return state.globalMemory | ||
} | ||
|
||
override fun setMemory(state: VMState, memory: Memory): VMState { | ||
return state.copy(globalMemory = memory) | ||
} | ||
} | ||
val LOCAL: MemoryType = object : MemoryType() { | ||
override fun getMemory(state: VMState): Memory { | ||
return state.callStack.currentFrame().localMemory | ||
} | ||
|
||
override fun setMemory(state: VMState, memory: Memory): VMState { | ||
return state.copy(callStack = state.callStack.updateCurrentFrame(state.callStack.currentFrame().copy(localMemory = memory))) | ||
} | ||
} | ||
} | ||
} | ||
|
||
data class CallStack(val frames: PersistentList<StackFrame> = persistentListOf()) { | ||
fun pushFrame(frame: StackFrame) = CallStack(frames + frame) | ||
fun popFrame(): Pair<CallStack, StackFrame> = CallStack(frames.removeAt(frames.lastIndex)) to frames.last() | ||
fun currentFrame() = frames.last() | ||
fun updateCurrentFrame(newFrame: StackFrame) = CallStack(frames.set(frames.lastIndex, newFrame)) | ||
fun getFrames(): ImmutableList<StackFrame> = frames | ||
fun size() = frames.size | ||
} | ||
|
||
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 { | ||
var next: Instruction? = null | ||
abstract fun execute(state: VMState): VMState | ||
} |
55 changes: 55 additions & 0 deletions
55
interpreter-vm/src/commonMain/kotlin/org/modelix/interpreter/vm/core/ProgramBuilder.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package org.modelix.interpreter.vm.core | ||
|
||
import kotlin.reflect.KProperty | ||
|
||
class ProgramBuilder { | ||
|
||
private val functions: MutableMap<Any, FunctionBuilder> = HashMap() | ||
|
||
fun getFunction(key: Any): FunctionBuilder { | ||
return checkNotNull(functions[key]) { "Function doesn't exist: $key" } | ||
} | ||
|
||
fun getOrBuildFunction(key: Any, body: FunctionBuilder.() -> Unit): FunctionBuilder { | ||
return functions[key] ?: buildFunction(key, body) | ||
} | ||
|
||
fun buildFunction(key: Any, body: FunctionBuilder.() -> Unit): FunctionBuilder { | ||
check(functions[key] == null) { "Function already exists: $key" } | ||
val builder = FunctionBuilder() | ||
functions[key] = builder | ||
body(builder) | ||
return builder | ||
} | ||
|
||
fun <T> variable(type: MemoryType = MemoryType.LOCAL) = O<T>(type) | ||
} | ||
|
||
class O<T>(private val type: MemoryType) { | ||
private var instance: MemoryKey<T>? = null | ||
operator fun getValue(thisRef: Nothing?, property: KProperty<*>): MemoryKey<T> { | ||
return instance ?: MemoryKey<T>(type, property.name).also { instance = it } | ||
} | ||
} | ||
|
||
class FunctionBuilder { | ||
private var firstInstruction: Instruction? = null | ||
private var lastInstruction: Instruction? = null | ||
|
||
fun addInstruction(instruction: Instruction) { | ||
lastInstruction?.next = instruction | ||
lastInstruction = instruction | ||
if (firstInstruction == null) { | ||
firstInstruction = instruction | ||
} | ||
} | ||
|
||
fun getEntryPoint(): Instruction { | ||
return firstInstruction ?: NoOpInstruction().also { firstInstruction = it } | ||
} | ||
|
||
fun <T> load(value: T, variable: MemoryKey<T>) { | ||
addInstruction(PushConstantInstruction(value)) | ||
addInstruction(StoreInstruction(variable)) | ||
} | ||
} |
Oops, something went wrong.