add debug mode

This commit is contained in:
Alex Zenla 2025-07-26 00:47:32 -07:00
parent 8d8866c26c
commit ff2aaabd93
No known key found for this signature in database
GPG Key ID: 067B238899B51269
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.execution.ExecutionContext
import gay.pizza.pork.execution.ExecutionContextProvider
import gay.pizza.pork.execution.ExecutionOptions
import gay.pizza.pork.execution.NativeRegistry
import gay.pizza.pork.frontend.ImportLocator
import gay.pizza.pork.frontend.World
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)
nativeRegistry.forEachProvider { form, provider ->
options.nativeRegistry.forEachProvider { form, provider ->
if (provider is ExpandedNativeProvider) {
evaluator.addNativeProvider(form, provider)
} 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
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.execution.ExecutionContext
import gay.pizza.pork.execution.ExecutionContextProvider
import gay.pizza.pork.execution.ExecutionOptions
import gay.pizza.pork.execution.InternalNativeProvider
import gay.pizza.pork.execution.NativeRegistry
import gay.pizza.pork.ffi.FfiNativeProvider
@ -44,9 +45,9 @@ abstract class Tool {
fun createExecutionContextProvider(type: ExecutionType): ExecutionContextProvider =
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)
return executionContextProvider.prepare(rootImportLocator, symbol, nativeRegistry)
return executionContextProvider.prepare(rootImportLocator, symbol, options)
}
fun buildWorld(): World {

View File

@ -3,6 +3,7 @@ package gay.pizza.pork.minimal
import gay.pizza.dough.fs.PlatformFsProvider
import gay.pizza.pork.ast.gen.Symbol
import gay.pizza.pork.evaluator.Scope
import gay.pizza.pork.execution.ExecutionOptions
import gay.pizza.pork.execution.InternalNativeProvider
import gay.pizza.pork.execution.NativeRegistry
import kotlin.system.exitProcess
@ -19,7 +20,7 @@ fun main(args: Array<String>) {
val main = tool.createExecutionContext(
ExecutionType.Evaluator,
Symbol("main"),
nativeRegistry
ExecutionOptions(nativeRegistry = nativeRegistry)
)
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.parser.ParseError
import gay.pizza.pork.parser.ParserNodeAttribution
import gay.pizza.pork.tokenizer.ExpectedTokenError
class PsiBuilderMarkAttribution(val builder: PsiBuilder) : ParserNodeAttribution() {
override fun <T : Node> produce(type: NodeType, block: () -> T): T {
@ -19,6 +20,12 @@ class PsiBuilderMarkAttribution(val builder: PsiBuilder) : ParserNodeAttribution
builder.advanceLexer()
}
throw PorkParser.ExitParser()
} catch (e: ExpectedTokenError) {
marker.error("${e.message}")
while (!builder.eof()) {
builder.advanceLexer()
}
throw PorkParser.ExitParser()
} catch (e: ParseError) {
marker.error(e.error)
while (!builder.eof()) {

View File

@ -177,7 +177,8 @@ object PorkReferenceResolution {
for (file in getRelevantFiles(containingFile)) {
val definitions = PsiTreeUtil.collectElements(file.file) { element ->
element is FunctionDefinitionElement ||
element is LetDefinitionElement
element is LetDefinitionElement ||
element is TypeDefinitionElement
}.filterIsInstance<PorkNamedElement>()
if (name != null) {
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 gay.pizza.dough.fs.PlatformFsProvider
import gay.pizza.pork.ast.gen.Symbol
import gay.pizza.pork.execution.ExecutionOptions
import gay.pizza.pork.execution.InternalNativeProvider
import gay.pizza.pork.execution.NativeRegistry
import gay.pizza.pork.ffi.FfiNativeProvider
@ -20,6 +21,7 @@ class RunCommand : CliktCommand("run") {
val loop by option("--loop", help = "Loop Program").flag()
val measure by option("--measure", help = "Measure Time").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")
.enum<ExecutionType> { it.id }
.default(ExecutionType.VirtualMachine)
@ -33,7 +35,10 @@ class RunCommand : CliktCommand("run") {
nativeRegistry.add("internal", InternalNativeProvider(quiet = quiet))
nativeRegistry.add("java", JavaNativeProvider())
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) {
main.execute()
}

View File

@ -4,7 +4,7 @@ import gay.pizza.pork.bytecode.CompiledWorld
import gay.pizza.pork.bytecode.ConstantTag
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 ->
val handler = handlers.firstOrNull { it.code == op.code }
?: 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 {
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)
if (autoNextInst) {
inst++
@ -32,19 +37,29 @@ class InternalMachine(val world: CompiledWorld, val nativeRegistry: NativeRegist
}
fun pushScope() {
if (debug) {
println(" vm: push scope")
}
locals.add(LocalSlots())
}
fun popScope() {
if (debug) {
println(" vm: pop scope")
}
locals.removeLast()
}
fun loadConstant(id: UInt) {
val constant = world.constantPool.constants[id.toInt()]
when (constant.tag) {
ConstantTag.String -> push(String(constant.value))
val value = when (constant.tag) {
ConstantTag.String -> String(constant.value)
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 {
@ -53,35 +68,57 @@ class InternalMachine(val world: CompiledWorld, val nativeRegistry: NativeRegist
}
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) {
val localSet = locals.last()
val value = popAnyValue()
if (debug) {
println(" vm: store local: $id = $value")
}
localSet.store(id, value)
}
fun setNextInst(value: UInt) {
inst = value
autoNextInst = false
if (debug) {
println(" vm: next instruction: $value")
}
}
fun pushReturnAddress(value: UInt) {
if (debug) {
println(" vm: push return address: $value")
}
returnAddressStack.add(value)
}
fun pushCallStack(value: UInt) {
if (debug) {
println(" vm: push call stack: $value")
}
callStack.add(value)
}
fun popCallStack() {
callStack.removeLast()
val call = callStack.removeLast()
if (debug) {
println(" vm: pop call stack: $call")
}
}
fun armReturnAddressIfSet() {
val returnAddress = returnAddressStack.removeLastOrNull()
if (returnAddress != null) {
if (debug) {
println(" vm: arm return address: $returnAddress")
}
setNextInst(returnAddress)
} else {
exit()
@ -89,18 +126,33 @@ class InternalMachine(val world: CompiledWorld, val nativeRegistry: NativeRegist
}
fun push(item: Any) {
if (debug) {
println(" vm: push stack: $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
fun exit() {
if (debug) {
println(" vm: exit")
}
exitFlag = true
}
fun reset() {
if (debug) {
println("vm: reset")
}
stack.clear()
callStack.clear()
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.NativeRegistry
class VirtualMachine(world: CompiledWorld, nativeRegistry: NativeRegistry) : ExecutionContext {
class VirtualMachine(world: CompiledWorld, nativeRegistry: NativeRegistry, debug: Boolean = false) : ExecutionContext {
private val internal = InternalMachine(
world = world,
nativeRegistry = nativeRegistry,
handlers = StandardOpHandlers
handlers = StandardOpHandlers,
debug = debug,
)
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.execution.ExecutionContext
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.World
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 slab = world.load(importLocator)
val compilableSlab = compiler.compilableSlabs.of(slab)
val compilableSymbol = compilableSlab.resolve(entryPointSymbol) ?:
throw RuntimeException("Unable to find compilable symbol for entry point '${entryPointSymbol.id}'")
val compiledWorld = compiler.compile(compilableSymbol)
return VirtualMachine(compiledWorld, nativeRegistry)
return VirtualMachine(world = compiledWorld, nativeRegistry = options.nativeRegistry, debug = options.debug)
}
}