diff --git a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluatorProvider.kt b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluatorProvider.kt index ff6a6c3..53381c4 100644 --- a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluatorProvider.kt +++ b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluatorProvider.kt @@ -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 { diff --git a/examples/bad.pork b/examples/bad.pork new file mode 100644 index 0000000..966078c --- /dev/null +++ b/examples/bad.pork @@ -0,0 +1,15 @@ +func depth(i: int32) { + println("Depth: ", i) +} + +export func main() { + if true { + false + } + + if false { + true + } + + return println("uhm, bad") +} diff --git a/execution/src/main/kotlin/gay/pizza/pork/execution/ExecutionContextProvider.kt b/execution/src/main/kotlin/gay/pizza/pork/execution/ExecutionContextProvider.kt index 34f20b1..1340650 100644 --- a/execution/src/main/kotlin/gay/pizza/pork/execution/ExecutionContextProvider.kt +++ b/execution/src/main/kotlin/gay/pizza/pork/execution/ExecutionContextProvider.kt @@ -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 } diff --git a/execution/src/main/kotlin/gay/pizza/pork/execution/ExecutionOptions.kt b/execution/src/main/kotlin/gay/pizza/pork/execution/ExecutionOptions.kt new file mode 100644 index 0000000..bbf31bb --- /dev/null +++ b/execution/src/main/kotlin/gay/pizza/pork/execution/ExecutionOptions.kt @@ -0,0 +1,6 @@ +package gay.pizza.pork.execution + +class ExecutionOptions( + val nativeRegistry: NativeRegistry, + val debug: Boolean = false, +) diff --git a/minimal/src/main/kotlin/gay/pizza/pork/minimal/Tool.kt b/minimal/src/main/kotlin/gay/pizza/pork/minimal/Tool.kt index 0c85f37..75bcf9a 100644 --- a/minimal/src/main/kotlin/gay/pizza/pork/minimal/Tool.kt +++ b/minimal/src/main/kotlin/gay/pizza/pork/minimal/Tool.kt @@ -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 { diff --git a/minimal/src/main/kotlin/gay/pizza/pork/minimal/main.kt b/minimal/src/main/kotlin/gay/pizza/pork/minimal/main.kt index 365f167..01b44a6 100644 --- a/minimal/src/main/kotlin/gay/pizza/pork/minimal/main.kt +++ b/minimal/src/main/kotlin/gay/pizza/pork/minimal/main.kt @@ -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) { val main = tool.createExecutionContext( ExecutionType.Evaluator, Symbol("main"), - nativeRegistry + ExecutionOptions(nativeRegistry = nativeRegistry) ) main.execute() } diff --git a/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/PsiBuilderMarkAttribution.kt b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/PsiBuilderMarkAttribution.kt index 17bcca2..49c22a0 100644 --- a/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/PsiBuilderMarkAttribution.kt +++ b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/PsiBuilderMarkAttribution.kt @@ -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 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()) { diff --git a/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/resolution/PorkReferenceResolution.kt b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/resolution/PorkReferenceResolution.kt index d85f139..5528d73 100644 --- a/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/resolution/PorkReferenceResolution.kt +++ b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/resolution/PorkReferenceResolution.kt @@ -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() if (name != null) { val fileFoundDefinition = definitions.firstOrNull { diff --git a/tool/src/main/kotlin/gay/pizza/pork/tool/RunCommand.kt b/tool/src/main/kotlin/gay/pizza/pork/tool/RunCommand.kt index ac3896f..4bb61d2 100644 --- a/tool/src/main/kotlin/gay/pizza/pork/tool/RunCommand.kt +++ b/tool/src/main/kotlin/gay/pizza/pork/tool/RunCommand.kt @@ -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 { 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() } diff --git a/vm/src/main/kotlin/gay/pizza/pork/vm/InternalMachine.kt b/vm/src/main/kotlin/gay/pizza/pork/vm/InternalMachine.kt index fae67d2..9beade8 100644 --- a/vm/src/main/kotlin/gay/pizza/pork/vm/InternalMachine.kt +++ b/vm/src/main/kotlin/gay/pizza/pork/vm/InternalMachine.kt @@ -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) { +class InternalMachine(val world: CompiledWorld, val nativeRegistry: NativeRegistry, val handlers: List, 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 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) diff --git a/vm/src/main/kotlin/gay/pizza/pork/vm/VirtualMachine.kt b/vm/src/main/kotlin/gay/pizza/pork/vm/VirtualMachine.kt index 3a4c008..18465f1 100644 --- a/vm/src/main/kotlin/gay/pizza/pork/vm/VirtualMachine.kt +++ b/vm/src/main/kotlin/gay/pizza/pork/vm/VirtualMachine.kt @@ -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() { diff --git a/vm/src/main/kotlin/gay/pizza/pork/vm/VirtualMachineProvider.kt b/vm/src/main/kotlin/gay/pizza/pork/vm/VirtualMachineProvider.kt index bd3b98e..3af39d7 100644 --- a/vm/src/main/kotlin/gay/pizza/pork/vm/VirtualMachineProvider.kt +++ b/vm/src/main/kotlin/gay/pizza/pork/vm/VirtualMachineProvider.kt @@ -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) } }