Skip to content

Commit

Permalink
Fix upvalues
Browse files Browse the repository at this point in the history
  • Loading branch information
Seggan committed Dec 7, 2023
1 parent 18f2eaf commit dab222b
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 19 deletions.
5 changes: 5 additions & 0 deletions docs/index.papyri
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ This will create a JAR file in `metis-app/build/libs` that can be run with `java

@h2 Changelog

@h2 { 0.3.0 }
[
{Fixed upvalues having the wrong value when the function is passed as an argument},
]

@h2 { 0.2.0 }
[
{Fixed a bug where reporting errors would break because the lexer kept a global position, instead of a local one},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import io.github.seggan.metis.runtime.chunk.Insn
import io.github.seggan.metis.runtime.chunk.Upvalue
import io.github.seggan.metis.util.pop
import io.github.seggan.metis.util.push
import java.util.*
import kotlin.collections.ArrayDeque

/**
* A compiler for Metis code. You may not call [compileCode] more than once.
Expand All @@ -32,7 +34,7 @@ class Compiler private constructor(
private val loopStack = ArrayDeque<LoopInfo>()
private val errorScopeStack = ArrayDeque<Int>()

private val depth: Int = enclosingCompiler?.depth ?: 0
private val id = UUID.randomUUID()

private var scope = 0

Expand All @@ -56,7 +58,7 @@ class Compiler private constructor(
backpatch(compiled, marker.first as Insn.Label)
}
val (insns, spans) = compiled.unzip()
return Chunk(name, insns, Arity(args.size, args.firstOrNull() == "self"), upvalues, spans)
return Chunk(name, insns, Arity(args.size, args.firstOrNull() == "self"), upvalues, id, spans)
}

private fun backpatch(insns: MutableList<FullInsn>, label: Insn.Label) {
Expand Down Expand Up @@ -427,7 +429,7 @@ class Compiler private constructor(
val local = enclosingCompiler.resolveLocal(name)
if (local != null) {
if (local.capturing == null) {
local.capturing = Upvalue(name, local.index, enclosingCompiler.depth)
local.capturing = Upvalue(name, local.index, enclosingCompiler.id)
}
val upvalue = local.capturing!!
upvalues.add(upvalue)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,4 @@ class Debugger(private val state: State, private val sourceName: String) {
}
}

private val locationRegex = """(?<name>[a-zA-Z0-9_.]+:)?(?<line>[0-9]+)""".toRegex()
private val locationRegex = """(?:(?<name>[a-zA-Z0-9_.]+):)?(?<line>[0-9]+)""".toRegex()
31 changes: 26 additions & 5 deletions metis-lang/src/main/kotlin/io/github/seggan/metis/runtime/State.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,20 @@ import io.github.seggan.metis.runtime.chunk.Chunk
import io.github.seggan.metis.runtime.chunk.StepResult
import io.github.seggan.metis.runtime.chunk.Upvalue
import io.github.seggan.metis.runtime.intrinsics.*
import io.github.seggan.metis.util.*
import io.github.seggan.metis.util.Stack
import io.github.seggan.metis.util.getFromTop
import io.github.seggan.metis.util.peek
import io.github.seggan.metis.util.pop
import io.github.seggan.metis.util.push
import java.io.InputStream
import java.io.OutputStream
import java.nio.file.FileSystem
import java.nio.file.FileSystems
import java.util.*
import kotlin.collections.ArrayDeque
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.collections.set

/**
* Represents the state of a Metis virtual machine. This is the class that is used to run Metis code.
Expand Down Expand Up @@ -220,7 +229,7 @@ class State(parentState: State? = null) {
break
}
}
val (executor, bottom, span) = callStack.peek()
val (executor, bottom, id, span) = callStack.peek()
if (span != null) {
err.addStackFrame(span)
}
Expand All @@ -239,7 +248,7 @@ class State(parentState: State? = null) {
}
for (i in stack.lastIndex downTo bottom) {
val upvalue = openUpvalues.firstOrNull {
it.template.callDepth == callStack.size && it.template.index == i
it.template.function == id && it.template.index == i
}
if (upvalue != null) {
upvalue.close(this)
Expand Down Expand Up @@ -428,7 +437,14 @@ class State(parentState: State? = null) {
}
)
}
callStack.push(CallFrame(executor, stackBottom, span))
callStack.push(
CallFrame(
executor,
stackBottom,
(value as? Chunk.Instance)?.id,
span
)
)
}
}

Expand Down Expand Up @@ -511,7 +527,12 @@ class State(parentState: State? = null) {
}
}

internal data class CallFrame(val executing: CallableValue.Executor, val stackBottom: Int, val span: Span?)
internal data class CallFrame(
val executing: CallableValue.Executor,
val stackBottom: Int,
val id: UUID?,
val span: Span?
)

private val metatableString = Value.String("metatable")
private val indexString = Value.String("__index__")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,24 @@ import io.github.seggan.metis.parsing.Span
import io.github.seggan.metis.runtime.*
import io.github.seggan.metis.runtime.intrinsics.initChunk
import io.github.seggan.metis.util.*
import java.util.*
import kotlin.collections.ArrayDeque
import kotlin.collections.set

/**
* A chunk is a compiled piece of code.
*
* @property name The name of the chunk.
* @property insns The instructions of the chunk.
* @property arity The arity of the chunk.
* @property upvalues The upvalues of the chunk.
* @property spans The spans of the chunk. Must be the same size as [insns].
*/
class Chunk(
val name: String,
val insns: List<Insn>,
val arity: Arity,
val upvalues: List<Upvalue>,
private val upvalues: List<Upvalue>,
private val id: UUID,
val spans: List<Span>
) {

Expand Down Expand Up @@ -60,6 +63,8 @@ class Chunk(

override val arity = this@Chunk.arity

val id = this@Chunk.id

/**
* The upvalue instances of the chunk instance.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@ import io.github.seggan.metis.runtime.State
import io.github.seggan.metis.runtime.Value
import io.github.seggan.metis.util.pop
import io.github.seggan.metis.util.push
import java.util.*

/**
* An upvalue. Used in the implementation of closures.
*
* @property name The name of the upvalue.
* @property index The index of the upvalue's local variable.
* @property callDepth The call depth of the upvalue's function.
* @property function The [UUID] of the function that owns the upvalue.
*/
data class Upvalue(
val name: String,
val index: Int,
val callDepth: Int
val function: UUID
) {

/**
Expand All @@ -30,7 +31,8 @@ data class Upvalue(
return upvalue
}
}
val instance = Instance(null)
val bottom = state.callStack.first { it.id == function }.stackBottom
val instance = Instance(null, bottom)
state.openUpvalues.addFirst(instance)
return instance
}
Expand All @@ -40,21 +42,20 @@ data class Upvalue(
*
* @see Upvalue.newInstance
*/
inner class Instance internal constructor(internal var value: Value?) {
inner class Instance internal constructor(internal var value: Value?, private val stackBottom: Int) {

val template: Upvalue
get() = this@Upvalue
val template: Upvalue = this@Upvalue

fun get(state: State) {
state.stack.push(value ?: state.stack[state.callStack[callDepth].stackBottom + index])
state.stack.push(value ?: state.stack[stackBottom + index])
}

fun set(state: State) {
val toSet = state.stack.pop()
if (value != null) {
value = toSet
} else {
state.stack[state.callStack[callDepth].stackBottom + index] = toSet
state.stack[stackBottom + index] = toSet
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,9 +271,29 @@ internal fun initList() = buildTable { table ->
self.listValue().removeAt(index.intValue())
}
table["slice"] = threeArgFunction(true) { self, start, end ->
if (start.intValue() < 0) {
throw MetisRuntimeException(
"ValueError",
"Start index cannot be negative",
buildTable { table ->
table["start"] = start
table["end"] = end
}
)
}
if (end == Value.Null) {
self.listValue().subList(start.intValue(), self.listValue().size).metisValue()
} else {
if (end.intValue() > self.listValue().size) {
throw MetisRuntimeException(
"ValueError",
"End index cannot be greater than list size",
buildTable { table ->
table["start"] = start
table["end"] = end
}
)
}
self.listValue().subList(start.intValue(), end.intValue()).metisValue()
}
}
Expand Down

0 comments on commit dab222b

Please sign in to comment.