add debug mode

This commit is contained in:
Alex Zenla
2025-07-26 00:47:32 -07:00
parent 8d8866c26c
commit ff2aaabd93
12 changed files with 109 additions and 19 deletions

View File

@ -3,14 +3,15 @@ package gay.pizza.pork.evaluator
import gay.pizza.pork.ast.gen.Symbol import gay.pizza.pork.ast.gen.Symbol
import gay.pizza.pork.execution.ExecutionContext import gay.pizza.pork.execution.ExecutionContext
import gay.pizza.pork.execution.ExecutionContextProvider import gay.pizza.pork.execution.ExecutionContextProvider
import gay.pizza.pork.execution.ExecutionOptions
import gay.pizza.pork.execution.NativeRegistry import gay.pizza.pork.execution.NativeRegistry
import gay.pizza.pork.frontend.ImportLocator import gay.pizza.pork.frontend.ImportLocator
import gay.pizza.pork.frontend.World import gay.pizza.pork.frontend.World
class EvaluatorProvider(val world: World) : ExecutionContextProvider { class EvaluatorProvider(val world: World) : ExecutionContextProvider {
override fun prepare(importLocator: ImportLocator, entryPointSymbol: Symbol, nativeRegistry: NativeRegistry): ExecutionContext { override fun prepare(importLocator: ImportLocator, entryPointSymbol: Symbol, options: ExecutionOptions): ExecutionContext {
val evaluator = Evaluator(world) val evaluator = Evaluator(world)
nativeRegistry.forEachProvider { form, provider -> options.nativeRegistry.forEachProvider { form, provider ->
if (provider is ExpandedNativeProvider) { if (provider is ExpandedNativeProvider) {
evaluator.addNativeProvider(form, provider) evaluator.addNativeProvider(form, provider)
} else { } else {

15
examples/bad.pork Normal file
View File

@ -0,0 +1,15 @@
func depth(i: int32) {
println("Depth: ", i)
}
export func main() {
if true {
false
}
if false {
true
}
return println("uhm, bad")
}

View File

@ -4,5 +4,5 @@ import gay.pizza.pork.ast.gen.Symbol
import gay.pizza.pork.frontend.ImportLocator import gay.pizza.pork.frontend.ImportLocator
interface ExecutionContextProvider { interface ExecutionContextProvider {
fun prepare(importLocator: ImportLocator, entryPointSymbol: Symbol, nativeRegistry: NativeRegistry): ExecutionContext fun prepare(importLocator: ImportLocator, entryPointSymbol: Symbol, options: ExecutionOptions): ExecutionContext
} }

View File

@ -0,0 +1,6 @@
package gay.pizza.pork.execution
class ExecutionOptions(
val nativeRegistry: NativeRegistry,
val debug: Boolean = false,
)

View File

@ -7,6 +7,7 @@ import gay.pizza.pork.ast.gen.visit
import gay.pizza.pork.evaluator.* import gay.pizza.pork.evaluator.*
import gay.pizza.pork.execution.ExecutionContext import gay.pizza.pork.execution.ExecutionContext
import gay.pizza.pork.execution.ExecutionContextProvider import gay.pizza.pork.execution.ExecutionContextProvider
import gay.pizza.pork.execution.ExecutionOptions
import gay.pizza.pork.execution.InternalNativeProvider import gay.pizza.pork.execution.InternalNativeProvider
import gay.pizza.pork.execution.NativeRegistry import gay.pizza.pork.execution.NativeRegistry
import gay.pizza.pork.ffi.FfiNativeProvider import gay.pizza.pork.ffi.FfiNativeProvider
@ -44,9 +45,9 @@ abstract class Tool {
fun createExecutionContextProvider(type: ExecutionType): ExecutionContextProvider = fun createExecutionContextProvider(type: ExecutionType): ExecutionContextProvider =
type.create(buildWorld()) type.create(buildWorld())
fun createExecutionContext(type: ExecutionType, symbol: Symbol, nativeRegistry: NativeRegistry): ExecutionContext { fun createExecutionContext(type: ExecutionType, symbol: Symbol, options: ExecutionOptions): ExecutionContext {
val executionContextProvider = createExecutionContextProvider(type) val executionContextProvider = createExecutionContextProvider(type)
return executionContextProvider.prepare(rootImportLocator, symbol, nativeRegistry) return executionContextProvider.prepare(rootImportLocator, symbol, options)
} }
fun buildWorld(): World { fun buildWorld(): World {

View File

@ -3,6 +3,7 @@ package gay.pizza.pork.minimal
import gay.pizza.dough.fs.PlatformFsProvider import gay.pizza.dough.fs.PlatformFsProvider
import gay.pizza.pork.ast.gen.Symbol import gay.pizza.pork.ast.gen.Symbol
import gay.pizza.pork.evaluator.Scope import gay.pizza.pork.evaluator.Scope
import gay.pizza.pork.execution.ExecutionOptions
import gay.pizza.pork.execution.InternalNativeProvider import gay.pizza.pork.execution.InternalNativeProvider
import gay.pizza.pork.execution.NativeRegistry import gay.pizza.pork.execution.NativeRegistry
import kotlin.system.exitProcess import kotlin.system.exitProcess
@ -19,7 +20,7 @@ fun main(args: Array<String>) {
val main = tool.createExecutionContext( val main = tool.createExecutionContext(
ExecutionType.Evaluator, ExecutionType.Evaluator,
Symbol("main"), Symbol("main"),
nativeRegistry ExecutionOptions(nativeRegistry = nativeRegistry)
) )
main.execute() main.execute()
} }

View File

@ -5,6 +5,7 @@ import gay.pizza.pork.ast.gen.Node
import gay.pizza.pork.ast.gen.NodeType import gay.pizza.pork.ast.gen.NodeType
import gay.pizza.pork.parser.ParseError import gay.pizza.pork.parser.ParseError
import gay.pizza.pork.parser.ParserNodeAttribution import gay.pizza.pork.parser.ParserNodeAttribution
import gay.pizza.pork.tokenizer.ExpectedTokenError
class PsiBuilderMarkAttribution(val builder: PsiBuilder) : ParserNodeAttribution() { class PsiBuilderMarkAttribution(val builder: PsiBuilder) : ParserNodeAttribution() {
override fun <T : Node> produce(type: NodeType, block: () -> T): T { override fun <T : Node> produce(type: NodeType, block: () -> T): T {
@ -19,6 +20,12 @@ class PsiBuilderMarkAttribution(val builder: PsiBuilder) : ParserNodeAttribution
builder.advanceLexer() builder.advanceLexer()
} }
throw PorkParser.ExitParser() throw PorkParser.ExitParser()
} catch (e: ExpectedTokenError) {
marker.error("${e.message}")
while (!builder.eof()) {
builder.advanceLexer()
}
throw PorkParser.ExitParser()
} catch (e: ParseError) { } catch (e: ParseError) {
marker.error(e.error) marker.error(e.error)
while (!builder.eof()) { while (!builder.eof()) {

View File

@ -177,7 +177,8 @@ object PorkReferenceResolution {
for (file in getRelevantFiles(containingFile)) { for (file in getRelevantFiles(containingFile)) {
val definitions = PsiTreeUtil.collectElements(file.file) { element -> val definitions = PsiTreeUtil.collectElements(file.file) { element ->
element is FunctionDefinitionElement || element is FunctionDefinitionElement ||
element is LetDefinitionElement element is LetDefinitionElement ||
element is TypeDefinitionElement
}.filterIsInstance<PorkNamedElement>() }.filterIsInstance<PorkNamedElement>()
if (name != null) { if (name != null) {
val fileFoundDefinition = definitions.firstOrNull { val fileFoundDefinition = definitions.firstOrNull {

View File

@ -9,6 +9,7 @@ import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.types.enum import com.github.ajalt.clikt.parameters.types.enum
import gay.pizza.dough.fs.PlatformFsProvider import gay.pizza.dough.fs.PlatformFsProvider
import gay.pizza.pork.ast.gen.Symbol import gay.pizza.pork.ast.gen.Symbol
import gay.pizza.pork.execution.ExecutionOptions
import gay.pizza.pork.execution.InternalNativeProvider import gay.pizza.pork.execution.InternalNativeProvider
import gay.pizza.pork.execution.NativeRegistry import gay.pizza.pork.execution.NativeRegistry
import gay.pizza.pork.ffi.FfiNativeProvider import gay.pizza.pork.ffi.FfiNativeProvider
@ -20,6 +21,7 @@ class RunCommand : CliktCommand("run") {
val loop by option("--loop", help = "Loop Program").flag() val loop by option("--loop", help = "Loop Program").flag()
val measure by option("--measure", help = "Measure Time").flag() val measure by option("--measure", help = "Measure Time").flag()
val quiet by option("--quiet", help = "Silence Prints").flag() val quiet by option("--quiet", help = "Silence Prints").flag()
val debug by option("--debug", help = "Debug Mode").flag()
val executionType by option("--execution-type", "-E") val executionType by option("--execution-type", "-E")
.enum<ExecutionType> { it.id } .enum<ExecutionType> { it.id }
.default(ExecutionType.VirtualMachine) .default(ExecutionType.VirtualMachine)
@ -33,7 +35,10 @@ class RunCommand : CliktCommand("run") {
nativeRegistry.add("internal", InternalNativeProvider(quiet = quiet)) nativeRegistry.add("internal", InternalNativeProvider(quiet = quiet))
nativeRegistry.add("java", JavaNativeProvider()) nativeRegistry.add("java", JavaNativeProvider())
nativeRegistry.add("ffi", FfiNativeProvider()) nativeRegistry.add("ffi", FfiNativeProvider())
val main = tool.createExecutionContext(executionType, Symbol("main"), nativeRegistry) val main = tool.createExecutionContext(executionType, Symbol("main"), ExecutionOptions(
nativeRegistry = nativeRegistry,
debug = debug,
))
maybeLoopAndMeasure(loop, measure) { maybeLoopAndMeasure(loop, measure) {
main.execute() main.execute()
} }

View File

@ -4,7 +4,7 @@ import gay.pizza.pork.bytecode.CompiledWorld
import gay.pizza.pork.bytecode.ConstantTag import gay.pizza.pork.bytecode.ConstantTag
import gay.pizza.pork.execution.NativeRegistry import gay.pizza.pork.execution.NativeRegistry
class InternalMachine(val world: CompiledWorld, val nativeRegistry: NativeRegistry, val handlers: List<OpHandler>) { class InternalMachine(val world: CompiledWorld, val nativeRegistry: NativeRegistry, val handlers: List<OpHandler>, val debug: Boolean = false) {
private val inlined = world.code.map { op -> private val inlined = world.code.map { op ->
val handler = handlers.firstOrNull { it.code == op.code } val handler = handlers.firstOrNull { it.code == op.code }
?: throw VirtualMachineException("Opcode ${op.code.name} does not have a handler.") ?: throw VirtualMachineException("Opcode ${op.code.name} does not have a handler.")
@ -23,6 +23,11 @@ class InternalMachine(val world: CompiledWorld, val nativeRegistry: NativeRegist
fun step(): Boolean { fun step(): Boolean {
val (op, handler) = inlined[inst.toInt()] val (op, handler) = inlined[inst.toInt()]
if (debug) {
val frame = frame(inst)
println("vm: step: in ${frame?.symbolInfo?.commonSymbolIdentity ?: "unknown"}: $inst ${op.code}${if (op.args.isEmpty()) "" else " " + op.args.joinToString(" ")}")
}
handler.handle(this, op) handler.handle(this, op)
if (autoNextInst) { if (autoNextInst) {
inst++ inst++
@ -32,19 +37,29 @@ class InternalMachine(val world: CompiledWorld, val nativeRegistry: NativeRegist
} }
fun pushScope() { fun pushScope() {
if (debug) {
println(" vm: push scope")
}
locals.add(LocalSlots()) locals.add(LocalSlots())
} }
fun popScope() { fun popScope() {
if (debug) {
println(" vm: pop scope")
}
locals.removeLast() locals.removeLast()
} }
fun loadConstant(id: UInt) { fun loadConstant(id: UInt) {
val constant = world.constantPool.constants[id.toInt()] val constant = world.constantPool.constants[id.toInt()]
when (constant.tag) { val value = when (constant.tag) {
ConstantTag.String -> push(String(constant.value)) ConstantTag.String -> String(constant.value)
else -> throw VirtualMachineException("Unknown constant tag: ${constant.tag.name}") else -> throw VirtualMachineException("Unknown constant tag: ${constant.tag.name}")
} }
if (debug) {
println(" vm: load constant: ${constant.id} ${constant.tag.name} $value")
}
push(value)
} }
fun localAt(id: UInt): Any { fun localAt(id: UInt): Any {
@ -53,35 +68,57 @@ class InternalMachine(val world: CompiledWorld, val nativeRegistry: NativeRegist
} }
fun loadLocal(id: UInt) { fun loadLocal(id: UInt) {
push(localAt(id)) val value = localAt(id)
if (debug) {
println(" vm: load local: $id $value")
}
push(value)
} }
fun storeLocal(id: UInt) { fun storeLocal(id: UInt) {
val localSet = locals.last() val localSet = locals.last()
val value = popAnyValue() val value = popAnyValue()
if (debug) {
println(" vm: store local: $id = $value")
}
localSet.store(id, value) localSet.store(id, value)
} }
fun setNextInst(value: UInt) { fun setNextInst(value: UInt) {
inst = value inst = value
autoNextInst = false autoNextInst = false
if (debug) {
println(" vm: next instruction: $value")
}
} }
fun pushReturnAddress(value: UInt) { fun pushReturnAddress(value: UInt) {
if (debug) {
println(" vm: push return address: $value")
}
returnAddressStack.add(value) returnAddressStack.add(value)
} }
fun pushCallStack(value: UInt) { fun pushCallStack(value: UInt) {
if (debug) {
println(" vm: push call stack: $value")
}
callStack.add(value) callStack.add(value)
} }
fun popCallStack() { fun popCallStack() {
callStack.removeLast() val call = callStack.removeLast()
if (debug) {
println(" vm: pop call stack: $call")
}
} }
fun armReturnAddressIfSet() { fun armReturnAddressIfSet() {
val returnAddress = returnAddressStack.removeLastOrNull() val returnAddress = returnAddressStack.removeLastOrNull()
if (returnAddress != null) { if (returnAddress != null) {
if (debug) {
println(" vm: arm return address: $returnAddress")
}
setNextInst(returnAddress) setNextInst(returnAddress)
} else { } else {
exit() exit()
@ -89,18 +126,33 @@ class InternalMachine(val world: CompiledWorld, val nativeRegistry: NativeRegist
} }
fun push(item: Any) { fun push(item: Any) {
if (debug) {
println(" vm: push stack: $item")
}
stack.add(item) stack.add(item)
} }
fun popAnyValue(): Any = stack.removeLast() fun popAnyValue(): Any {
val value = stack.removeLast()
if (debug) {
println(" vm: pop stack: $value")
}
return value
}
inline fun <reified T> pop(): T = popAnyValue() as T inline fun <reified T> pop(): T = popAnyValue() as T
fun exit() { fun exit() {
if (debug) {
println(" vm: exit")
}
exitFlag = true exitFlag = true
} }
fun reset() { fun reset() {
if (debug) {
println("vm: reset")
}
stack.clear() stack.clear()
callStack.clear() callStack.clear()
callStack.add(0u) callStack.add(0u)

View File

@ -4,11 +4,12 @@ import gay.pizza.pork.bytecode.CompiledWorld
import gay.pizza.pork.execution.ExecutionContext import gay.pizza.pork.execution.ExecutionContext
import gay.pizza.pork.execution.NativeRegistry import gay.pizza.pork.execution.NativeRegistry
class VirtualMachine(world: CompiledWorld, nativeRegistry: NativeRegistry) : ExecutionContext { class VirtualMachine(world: CompiledWorld, nativeRegistry: NativeRegistry, debug: Boolean = false) : ExecutionContext {
private val internal = InternalMachine( private val internal = InternalMachine(
world = world, world = world,
nativeRegistry = nativeRegistry, nativeRegistry = nativeRegistry,
handlers = StandardOpHandlers handlers = StandardOpHandlers,
debug = debug,
) )
override fun execute() { override fun execute() {

View File

@ -4,18 +4,18 @@ import gay.pizza.pork.ast.gen.Symbol
import gay.pizza.pork.compiler.Compiler import gay.pizza.pork.compiler.Compiler
import gay.pizza.pork.execution.ExecutionContext import gay.pizza.pork.execution.ExecutionContext
import gay.pizza.pork.execution.ExecutionContextProvider import gay.pizza.pork.execution.ExecutionContextProvider
import gay.pizza.pork.execution.NativeRegistry import gay.pizza.pork.execution.ExecutionOptions
import gay.pizza.pork.frontend.ImportLocator import gay.pizza.pork.frontend.ImportLocator
import gay.pizza.pork.frontend.World import gay.pizza.pork.frontend.World
class VirtualMachineProvider(val world: World) : ExecutionContextProvider { class VirtualMachineProvider(val world: World) : ExecutionContextProvider {
override fun prepare(importLocator: ImportLocator, entryPointSymbol: Symbol, nativeRegistry: NativeRegistry): ExecutionContext { override fun prepare(importLocator: ImportLocator, entryPointSymbol: Symbol, options: ExecutionOptions): ExecutionContext {
val compiler = Compiler(world) val compiler = Compiler(world)
val slab = world.load(importLocator) val slab = world.load(importLocator)
val compilableSlab = compiler.compilableSlabs.of(slab) val compilableSlab = compiler.compilableSlabs.of(slab)
val compilableSymbol = compilableSlab.resolve(entryPointSymbol) ?: val compilableSymbol = compilableSlab.resolve(entryPointSymbol) ?:
throw RuntimeException("Unable to find compilable symbol for entry point '${entryPointSymbol.id}'") throw RuntimeException("Unable to find compilable symbol for entry point '${entryPointSymbol.id}'")
val compiledWorld = compiler.compile(compilableSymbol) val compiledWorld = compiler.compile(compilableSymbol)
return VirtualMachine(compiledWorld, nativeRegistry) return VirtualMachine(world = compiledWorld, nativeRegistry = options.nativeRegistry, debug = options.debug)
} }
} }