mirror of
https://github.com/GayPizzaSpecifications/pork.git
synced 2025-08-02 12:50:55 +00:00
compiler: full support for IR based compilation
This commit is contained in:
parent
2d88666f05
commit
8951c3cd60
@ -1,10 +1,10 @@
|
||||
package gay.pizza.pork.bir
|
||||
|
||||
data class IrCall(
|
||||
val target: IrSymbol,
|
||||
override val target: IrSymbol,
|
||||
val arguments: List<IrCodeElement>,
|
||||
val variableArguments: List<IrCodeElement>?
|
||||
) : IrCodeElement {
|
||||
) : IrCodeElement, IrSymbolUser {
|
||||
override fun crawl(block: (IrElement) -> Unit) {
|
||||
block(target)
|
||||
arguments.forEach(block)
|
||||
|
@ -1,3 +1,7 @@
|
||||
package gay.pizza.pork.bir
|
||||
|
||||
data class IrCodeBlock(val items: List<IrCodeElement>) : IrCodeElement
|
||||
data class IrCodeBlock(val items: List<IrCodeElement>) : IrCodeElement {
|
||||
override fun crawl(block: (IrElement) -> Unit) {
|
||||
items.forEach(block)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package gay.pizza.pork.bir
|
||||
|
||||
data class IrContinue(val target: IrSymbol) : IrCodeElement {
|
||||
data class IrContinue(override val target: IrSymbol) : IrCodeElement, IrSymbolUser {
|
||||
override fun crawl(block: (IrElement) -> Unit) {
|
||||
block(target)
|
||||
}
|
||||
|
@ -1,12 +1,14 @@
|
||||
package gay.pizza.pork.bir
|
||||
|
||||
data class IrDefinition(
|
||||
val symbol: IrSymbol,
|
||||
override val symbol: IrSymbol,
|
||||
val type: IrDefinitionType,
|
||||
val arguments: List<IrFunctionArgument>,
|
||||
val code: IrCodeBlock
|
||||
) : IrElement {
|
||||
) : IrElement, IrSymbolOwner {
|
||||
override fun crawl(block: (IrElement) -> Unit) {
|
||||
block(symbol)
|
||||
arguments.forEach(block)
|
||||
block(code)
|
||||
}
|
||||
}
|
||||
|
@ -2,5 +2,6 @@ package gay.pizza.pork.bir
|
||||
|
||||
enum class IrDefinitionType {
|
||||
Variable,
|
||||
Function
|
||||
CodeFunction,
|
||||
NativeFunction
|
||||
}
|
||||
|
@ -0,0 +1,5 @@
|
||||
package gay.pizza.pork.bir
|
||||
|
||||
data class IrFunctionArgument(override val symbol: IrSymbol) : IrSymbolOwner {
|
||||
override fun crawl(block: (IrElement) -> Unit) {}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package gay.pizza.pork.bir
|
||||
|
||||
data class IrLoad(val symbol: IrSymbol) : IrCodeElement {
|
||||
data class IrLoad(override val target: IrSymbol) : IrCodeElement, IrSymbolUser {
|
||||
override fun crawl(block: (IrElement) -> Unit) {
|
||||
block(symbol)
|
||||
block(target)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,10 @@
|
||||
package gay.pizza.pork.bir
|
||||
|
||||
data class IrLoop(val symbol: IrSymbol, val condition: IrCodeElement, val inner: IrCodeElement) : IrCodeElement {
|
||||
data class IrLoop(
|
||||
override val symbol: IrSymbol,
|
||||
val condition: IrCodeElement,
|
||||
val inner: IrCodeElement
|
||||
) : IrCodeElement, IrSymbolOwner {
|
||||
override fun crawl(block: (IrElement) -> Unit) {
|
||||
block(symbol)
|
||||
block(condition)
|
||||
|
@ -0,0 +1,5 @@
|
||||
package gay.pizza.pork.bir
|
||||
|
||||
data class IrNativeDefinition(val form: String, val definitions: List<String>) : IrCodeElement {
|
||||
override fun crawl(block: (IrElement) -> Unit) {}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package gay.pizza.pork.bir
|
||||
|
||||
data class IrStore(val symbol: IrSymbol, val value: IrCodeElement) : IrCodeElement {
|
||||
data class IrStore(override val target: IrSymbol, val value: IrCodeElement) : IrCodeElement, IrSymbolUser {
|
||||
override fun crawl(block: (IrElement) -> Unit) {
|
||||
value.crawl(block)
|
||||
}
|
||||
|
7
bir/src/main/kotlin/gay/pizza/pork/bir/IrSuffix.kt
Normal file
7
bir/src/main/kotlin/gay/pizza/pork/bir/IrSuffix.kt
Normal file
@ -0,0 +1,7 @@
|
||||
package gay.pizza.pork.bir
|
||||
|
||||
data class IrSuffix(val op: IrSuffixOp, override val target: IrSymbol) : IrCodeElement, IrSymbolUser {
|
||||
override fun crawl(block: (IrElement) -> Unit) {
|
||||
block(target)
|
||||
}
|
||||
}
|
6
bir/src/main/kotlin/gay/pizza/pork/bir/IrSuffixOp.kt
Normal file
6
bir/src/main/kotlin/gay/pizza/pork/bir/IrSuffixOp.kt
Normal file
@ -0,0 +1,6 @@
|
||||
package gay.pizza.pork.bir
|
||||
|
||||
enum class IrSuffixOp {
|
||||
Increment,
|
||||
Decrement
|
||||
}
|
40
bir/src/main/kotlin/gay/pizza/pork/bir/IrSymbolGraph.kt
Normal file
40
bir/src/main/kotlin/gay/pizza/pork/bir/IrSymbolGraph.kt
Normal file
@ -0,0 +1,40 @@
|
||||
package gay.pizza.pork.bir
|
||||
|
||||
class IrSymbolGraph {
|
||||
private val edges = mutableSetOf<Pair<IrSymbolUser, IrSymbolOwner>>()
|
||||
|
||||
private fun crawlForKnown(known: MutableMap<IrSymbol, IrSymbolOwner>, root: IrElement) {
|
||||
if (root is IrSymbolOwner) {
|
||||
known[root.symbol] = root
|
||||
}
|
||||
|
||||
root.crawl { item ->
|
||||
crawlForKnown(known, item)
|
||||
}
|
||||
}
|
||||
|
||||
private fun crawlForAssociations(known: Map<IrSymbol, IrSymbolOwner>, root: IrElement) {
|
||||
if (root is IrSymbolUser) {
|
||||
val what = known[root.target]
|
||||
if (what != null) {
|
||||
edges.add(root to what)
|
||||
}
|
||||
}
|
||||
|
||||
root.crawl { item ->
|
||||
crawlForAssociations(known, item)
|
||||
}
|
||||
}
|
||||
|
||||
fun crawl(root: IrElement) {
|
||||
val known = mutableMapOf<IrSymbol, IrSymbolOwner>()
|
||||
crawlForKnown(known, root)
|
||||
crawlForAssociations(known, root)
|
||||
}
|
||||
|
||||
fun forEachEdge(block: (IrSymbolUser, IrSymbolOwner) -> Unit) {
|
||||
for ((from, to) in edges) {
|
||||
block(from, to)
|
||||
}
|
||||
}
|
||||
}
|
5
bir/src/main/kotlin/gay/pizza/pork/bir/IrSymbolOwner.kt
Normal file
5
bir/src/main/kotlin/gay/pizza/pork/bir/IrSymbolOwner.kt
Normal file
@ -0,0 +1,5 @@
|
||||
package gay.pizza.pork.bir
|
||||
|
||||
sealed interface IrSymbolOwner : IrElement {
|
||||
val symbol: IrSymbol
|
||||
}
|
5
bir/src/main/kotlin/gay/pizza/pork/bir/IrSymbolUser.kt
Normal file
5
bir/src/main/kotlin/gay/pizza/pork/bir/IrSymbolUser.kt
Normal file
@ -0,0 +1,5 @@
|
||||
package gay.pizza.pork.bir
|
||||
|
||||
sealed interface IrSymbolUser : IrElement {
|
||||
val target: IrSymbol
|
||||
}
|
59
bir/src/main/kotlin/gay/pizza/pork/bir/IrVisitor.kt
Normal file
59
bir/src/main/kotlin/gay/pizza/pork/bir/IrVisitor.kt
Normal file
@ -0,0 +1,59 @@
|
||||
package gay.pizza.pork.bir
|
||||
|
||||
interface IrVisitor<T> {
|
||||
fun visitIrSlab(ir: IrSlab): T
|
||||
fun visitIrSlabLocation(ir: IrSlabLocation): T
|
||||
fun visitIrDefinition(ir: IrDefinition): T
|
||||
fun visitIrSymbol(ir: IrSymbol): T
|
||||
fun visitIrBeak(ir: IrBreak): T
|
||||
fun visitIrCall(ir: IrCall): T
|
||||
fun visitIrCodeBlock(ir: IrCodeBlock): T
|
||||
fun visitIrConditional(ir: IrConditional): T
|
||||
fun visitIrBooleanConstant(ir: IrBooleanConstant): T
|
||||
fun visitIrIntegerConstant(ir: IrIntegerConstant): T
|
||||
fun visitIrLongConstant(ir: IrLongConstant): T
|
||||
fun visitIrDoubleConstant(ir: IrDoubleConstant): T
|
||||
fun visitIrStringConstant(ir: IrStringConstant): T
|
||||
fun visitIrNoneConstant(ir: IrNoneConstant): T
|
||||
fun visitIrContinue(ir: IrContinue): T
|
||||
fun visitIrInfix(ir: IrInfix): T
|
||||
fun visitIrList(ir: IrList): T
|
||||
fun visitIrLoad(ir: IrLoad): T
|
||||
fun visitIrLoop(ir: IrLoop): T
|
||||
fun visitIrPrefix(ir: IrPrefix): T
|
||||
fun visitIrReturn(ir: IrReturn): T
|
||||
fun visitIrStore(ir: IrStore): T
|
||||
fun visitIrSuffix(ir: IrSuffix): T
|
||||
fun visitIrWorld(ir: IrWorld): T
|
||||
fun visitIrNativeDefinition(ir: IrNativeDefinition): T
|
||||
fun visitIrFunctionArgument(ir: IrFunctionArgument): T
|
||||
|
||||
fun visit(ir: IrElement): T = when (ir) {
|
||||
is IrBreak -> visitIrBeak(ir)
|
||||
is IrCall -> visitIrCall(ir)
|
||||
is IrCodeBlock -> visitIrCodeBlock(ir)
|
||||
is IrConditional -> visitIrConditional(ir)
|
||||
is IrBooleanConstant -> visitIrBooleanConstant(ir)
|
||||
is IrDoubleConstant -> visitIrDoubleConstant(ir)
|
||||
is IrIntegerConstant -> visitIrIntegerConstant(ir)
|
||||
is IrLongConstant -> visitIrLongConstant(ir)
|
||||
is IrNoneConstant -> visitIrNoneConstant(ir)
|
||||
is IrStringConstant -> visitIrStringConstant(ir)
|
||||
is IrContinue -> visitIrContinue(ir)
|
||||
is IrInfix -> visitIrInfix(ir)
|
||||
is IrList -> visitIrList(ir)
|
||||
is IrLoad -> visitIrLoad(ir)
|
||||
is IrLoop -> visitIrLoop(ir)
|
||||
is IrPrefix -> visitIrPrefix(ir)
|
||||
is IrReturn -> visitIrReturn(ir)
|
||||
is IrStore -> visitIrStore(ir)
|
||||
is IrSuffix -> visitIrSuffix(ir)
|
||||
is IrDefinition -> visitIrDefinition(ir)
|
||||
is IrSlab -> visitIrSlab(ir)
|
||||
is IrSlabLocation -> visitIrSlabLocation(ir)
|
||||
is IrSymbol -> visitIrSymbol(ir)
|
||||
is IrWorld -> visitIrWorld(ir)
|
||||
is IrNativeDefinition -> visitIrNativeDefinition(ir)
|
||||
is IrFunctionArgument -> visitIrFunctionArgument(ir)
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package gay.pizza.pork.bir
|
||||
|
||||
data class IrAccess(val target: IrSymbol) : IrCodeElement {
|
||||
data class IrWorld(val slabs: List<IrSlab>) : IrElement {
|
||||
override fun crawl(block: (IrElement) -> Unit) {
|
||||
block(target)
|
||||
slabs.forEach(block)
|
||||
}
|
||||
}
|
@ -1,3 +1,3 @@
|
||||
package gay.pizza.pork.bytecode
|
||||
|
||||
class MutableRel(var rel: UInt)
|
||||
data class MutableRel(var rel: UInt)
|
||||
|
@ -1,3 +0,0 @@
|
||||
package gay.pizza.pork.bytecode
|
||||
|
||||
class Ops(val ops: List<Op>)
|
@ -1,9 +1,14 @@
|
||||
package gay.pizza.pork.compiler
|
||||
|
||||
import gay.pizza.pork.ast.gen.Symbol
|
||||
import gay.pizza.pork.bir.IrDefinition
|
||||
import gay.pizza.pork.bir.IrSlab
|
||||
import gay.pizza.pork.bir.IrSlabLocation
|
||||
import gay.pizza.pork.frontend.Slab
|
||||
|
||||
class CompilableSlab(val compiler: Compiler, val slab: Slab) {
|
||||
val compiledIrSlab: IrSlab by lazy { compileIrSlab() }
|
||||
|
||||
val compilableSymbols: List<CompilableSymbol> by lazy {
|
||||
slab.scope.internalSymbols.map { symbol ->
|
||||
CompilableSymbol(this, symbol)
|
||||
@ -18,4 +23,13 @@ class CompilableSlab(val compiler: Compiler, val slab: Slab) {
|
||||
val scopeSymbol = slab.scope.resolve(symbol) ?: return null
|
||||
return compiler.resolveOrNull(scopeSymbol)
|
||||
}
|
||||
|
||||
private fun compileIrSlab(): IrSlab {
|
||||
val definitions = mutableListOf<IrDefinition>()
|
||||
for (compilableSymbol in compilableSymbols) {
|
||||
definitions.add(compilableSymbol.compiledIrDefinition)
|
||||
}
|
||||
val irSlabLocation = IrSlabLocation(slab.location.form, slab.location.filePath)
|
||||
return IrSlab(irSlabLocation, definitions)
|
||||
}
|
||||
}
|
||||
|
@ -2,29 +2,62 @@ package gay.pizza.pork.compiler
|
||||
|
||||
import gay.pizza.pork.ast.gen.FunctionDefinition
|
||||
import gay.pizza.pork.ast.gen.LetDefinition
|
||||
import gay.pizza.pork.ast.gen.NativeFunctionDescriptor
|
||||
import gay.pizza.pork.ast.gen.visit
|
||||
import gay.pizza.pork.bir.IrCodeBlock
|
||||
import gay.pizza.pork.bir.IrDefinition
|
||||
import gay.pizza.pork.bir.IrDefinitionType
|
||||
import gay.pizza.pork.bir.IrSymbolTag
|
||||
import gay.pizza.pork.frontend.scope.ScopeSymbol
|
||||
|
||||
class CompilableSymbol(val compilableSlab: CompilableSlab, val scopeSymbol: ScopeSymbol) {
|
||||
val compiledIrDefinition: IrDefinition by lazy { compileIrDefinition() }
|
||||
val compiledStubOps: CompiledSymbolResult by lazy { compile() }
|
||||
|
||||
val usedSymbols: List<ScopeSymbol>
|
||||
get() = scopeSymbol.scope.usedSymbols
|
||||
|
||||
private fun compile(): CompiledSymbolResult {
|
||||
val emitter = StubOpEmitter(compilableSlab.compiler, this)
|
||||
emitter.enter()
|
||||
val code = CodeBuilder(this)
|
||||
val ir = compiledIrDefinition
|
||||
val emitter = IrStubOpEmitter(ir, code)
|
||||
emitter.visit(ir.code)
|
||||
emitter.final()
|
||||
return emitter.code.build()
|
||||
}
|
||||
|
||||
private fun compileIrDefinition(): IrDefinition {
|
||||
val compiler = compilableSlab.compiler
|
||||
val functionSymbol = compiler.irSymbolWorld.create(scopeSymbol, IrSymbolTag.Function)
|
||||
val irCodeEmitter = IrCodeEmitter(
|
||||
self = functionSymbol,
|
||||
irSymbolWorld = compiler.irSymbolWorld,
|
||||
irSymbolAssignment = compiler.irSymbolAssignment,
|
||||
scope = compilableSlab.slab.scope
|
||||
)
|
||||
irCodeEmitter.enterLocalScope()
|
||||
val what = if (scopeSymbol.definition is FunctionDefinition) {
|
||||
val functionDefinition = scopeSymbol.definition as FunctionDefinition
|
||||
emitter.allocateOuterScope(functionDefinition)
|
||||
irCodeEmitter.createFunctionArguments(functionDefinition)
|
||||
functionDefinition.block ?: functionDefinition.nativeFunctionDescriptor!!
|
||||
} else {
|
||||
val letDefinition = scopeSymbol.definition as LetDefinition
|
||||
letDefinition.value
|
||||
}
|
||||
emitter.visit(what)
|
||||
emitter.exit()
|
||||
return emitter.code.build()
|
||||
val type = if (what is NativeFunctionDescriptor) {
|
||||
IrDefinitionType.NativeFunction
|
||||
} else IrDefinitionType.CodeFunction
|
||||
val irCodeElement = irCodeEmitter.visit(what)
|
||||
val irCodeBlock = if (irCodeElement is IrCodeBlock) {
|
||||
irCodeElement
|
||||
} else IrCodeBlock(listOf(irCodeElement))
|
||||
irCodeEmitter.exitLocalScope()
|
||||
return IrDefinition(
|
||||
symbol = functionSymbol,
|
||||
type = type,
|
||||
arguments = irCodeEmitter.functionArguments,
|
||||
code = irCodeBlock
|
||||
)
|
||||
}
|
||||
|
||||
val id: String
|
||||
|
@ -1,5 +1,6 @@
|
||||
package gay.pizza.pork.compiler
|
||||
|
||||
import gay.pizza.pork.bir.IrSymbolAssignment
|
||||
import gay.pizza.pork.bytecode.CompiledWorld
|
||||
import gay.pizza.pork.bytecode.MutableConstantPool
|
||||
import gay.pizza.pork.frontend.Slab
|
||||
@ -11,6 +12,9 @@ class Compiler {
|
||||
CompilableSlab(this, slab)
|
||||
}
|
||||
|
||||
val irSymbolAssignment: IrSymbolAssignment = IrSymbolAssignment()
|
||||
val irSymbolWorld: IrSymbolWorld<Any> = IrSymbolWorld(irSymbolAssignment)
|
||||
|
||||
fun resolveOrNull(scopeSymbol: ScopeSymbol): CompilableSymbol? {
|
||||
val compiledSlab = compilableSlabs.of(scopeSymbol.slabScope.slab)
|
||||
return compiledSlab.resolve(scopeSymbol.symbol)
|
||||
|
@ -8,12 +8,23 @@ import gay.pizza.pork.frontend.scope.SlabScope
|
||||
|
||||
class IrCodeEmitter(
|
||||
val self: IrSymbol,
|
||||
val irSymbolWorld: IrSymbolWorld,
|
||||
val irSymbolWorld: IrSymbolWorld<Any>,
|
||||
val irSymbolAssignment: IrSymbolAssignment,
|
||||
val scope: SlabScope
|
||||
) : FunctionLevelVisitor<IrCodeElement>() {
|
||||
private val loopSymbols = mutableListOf<IrSymbol>()
|
||||
private val localVariables = mutableListOf<MutableList<LocalVariable>>()
|
||||
private val localVariables = mutableListOf<MutableMap<String, LocalVariable>>()
|
||||
|
||||
var functionArguments: List<IrFunctionArgument> = emptyList()
|
||||
|
||||
fun createFunctionArguments(functionDefinition: FunctionDefinition) {
|
||||
val functionSymbols = mutableListOf<IrFunctionArgument>()
|
||||
for (arg in functionDefinition.arguments) {
|
||||
val symbol = createLocalVariable(arg.symbol)
|
||||
functionSymbols.add(IrFunctionArgument(symbol))
|
||||
}
|
||||
functionArguments = functionSymbols
|
||||
}
|
||||
|
||||
private fun startLoop(): IrSymbol {
|
||||
val symbol = irSymbolAssignment.next(IrSymbolTag.Loop)
|
||||
@ -34,19 +45,24 @@ class IrCodeEmitter(
|
||||
}
|
||||
}
|
||||
|
||||
private fun enterBlockScope() {
|
||||
val locals = mutableListOf<LocalVariable>()
|
||||
fun enterLocalScope() {
|
||||
val locals = mutableMapOf<String, LocalVariable>()
|
||||
localVariables.add(locals)
|
||||
}
|
||||
|
||||
private fun exitBlockScope() {
|
||||
fun exitLocalScope() {
|
||||
localVariables.removeLast()
|
||||
}
|
||||
|
||||
private fun createLocalVariable(name: Symbol): IrSymbol {
|
||||
val symbol = irSymbolAssignment.next(IrSymbolTag.Local)
|
||||
val variable = LocalVariable(symbol, name)
|
||||
localVariables.last().add(variable)
|
||||
val variables = localVariables.last()
|
||||
val existing = variables[name.id]
|
||||
if (existing != null) {
|
||||
throw CompileError("Unable to define local variable '${name.id}' within this scope, it already exists", name)
|
||||
}
|
||||
variables[name.id] = variable
|
||||
return symbol
|
||||
}
|
||||
|
||||
@ -58,10 +74,10 @@ class IrCodeEmitter(
|
||||
}
|
||||
|
||||
private fun lookupLocalVariable(name: Symbol): IrSymbol? {
|
||||
for (i in 0..localVariables.size) {
|
||||
val b = localVariables.size - i - 1
|
||||
for (i in 1..localVariables.size) {
|
||||
val b = localVariables.size - i
|
||||
val scope = localVariables[b]
|
||||
val found = scope.firstOrNull { it.name == name }
|
||||
val found = scope[name.id]
|
||||
if (found != null) {
|
||||
return found.symbol
|
||||
}
|
||||
@ -76,20 +92,20 @@ class IrCodeEmitter(
|
||||
}
|
||||
val scoped = scope.resolve(name)
|
||||
if (scoped != null) {
|
||||
return irSymbolWorld.lookup(scoped, scopeSymbolToTag(scoped))
|
||||
return irSymbolWorld.create(scoped, scopeSymbolToTag(scoped))
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun lookupFunction(name: Symbol): Pair<ScopeSymbol, IrSymbol>? {
|
||||
val scoped = scope.resolve(name) ?: return null
|
||||
return scoped to irSymbolWorld.lookup(scoped, scopeSymbolToTag(scoped))
|
||||
return scoped to irSymbolWorld.create(scoped, scopeSymbolToTag(scoped))
|
||||
}
|
||||
|
||||
override fun visitBlock(node: Block): IrCodeBlock {
|
||||
enterBlockScope()
|
||||
enterLocalScope()
|
||||
val block = IrCodeBlock(node.expressions.map { it.visit(this) })
|
||||
exitBlockScope()
|
||||
exitLocalScope()
|
||||
return block
|
||||
}
|
||||
|
||||
@ -149,7 +165,7 @@ class IrCodeEmitter(
|
||||
}
|
||||
}
|
||||
|
||||
if (functionDefinition.arguments.any { it.multiple }) {
|
||||
if (variableArguments == null && functionDefinition.arguments.any { it.multiple }) {
|
||||
variableArguments = mutableListOf()
|
||||
}
|
||||
|
||||
@ -234,7 +250,13 @@ class IrCodeEmitter(
|
||||
IrStringConstant(node.text)
|
||||
|
||||
override fun visitSuffixOperation(node: SuffixOperation): IrCodeElement {
|
||||
TODO("Not yet implemented")
|
||||
val op = when (node.op) {
|
||||
SuffixOperator.Increment -> IrSuffixOp.Increment
|
||||
SuffixOperator.Decrement -> IrSuffixOp.Decrement
|
||||
}
|
||||
val symbol = lookup(node.reference.symbol) ?: throw CompileError(
|
||||
"Unable to find symbol for suffix operation '${node.reference.symbol.id}'", node)
|
||||
return IrSuffix(op, symbol)
|
||||
}
|
||||
|
||||
override fun visitSymbolReference(node: SymbolReference): IrCodeElement {
|
||||
@ -255,4 +277,9 @@ class IrCodeEmitter(
|
||||
inner = node.block.visit(this)
|
||||
)
|
||||
}
|
||||
|
||||
override fun visitNativeFunctionDescriptor(node: NativeFunctionDescriptor): IrCodeElement = IrNativeDefinition(
|
||||
form = node.form.id,
|
||||
definitions = node.definitions.map { it.text }
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,33 @@
|
||||
package gay.pizza.pork.compiler
|
||||
|
||||
import gay.pizza.pork.bir.*
|
||||
|
||||
interface IrCodeVisitor<T> : IrVisitor<T> {
|
||||
override fun visitIrDefinition(ir: IrDefinition): T {
|
||||
codeOnlyError("IrDefinition")
|
||||
}
|
||||
|
||||
override fun visitIrSlab(ir: IrSlab): T {
|
||||
codeOnlyError("IrSlab")
|
||||
}
|
||||
|
||||
override fun visitIrSlabLocation(ir: IrSlabLocation): T {
|
||||
codeOnlyError("IrSlabLocation")
|
||||
}
|
||||
|
||||
override fun visitIrWorld(ir: IrWorld): T {
|
||||
codeOnlyError("IrWorld")
|
||||
}
|
||||
|
||||
override fun visitIrSymbol(ir: IrSymbol): T {
|
||||
codeOnlyError("IrSymbol")
|
||||
}
|
||||
|
||||
override fun visitIrFunctionArgument(ir: IrFunctionArgument): T {
|
||||
codeOnlyError("IrFunctionArgument")
|
||||
}
|
||||
|
||||
private fun codeOnlyError(type: String): Nothing {
|
||||
throw RuntimeException("This visitor targets only code, and $type is not a code element.")
|
||||
}
|
||||
}
|
@ -0,0 +1,222 @@
|
||||
package gay.pizza.pork.compiler
|
||||
|
||||
import gay.pizza.pork.bir.*
|
||||
import gay.pizza.pork.bytecode.ConstantTag
|
||||
import gay.pizza.pork.bytecode.MutableRel
|
||||
import gay.pizza.pork.bytecode.Opcode
|
||||
|
||||
class IrStubOpEmitter(val irDefinition: IrDefinition, val code: CodeBuilder) : IrCodeVisitor<Unit> {
|
||||
private val symbol = code.symbol
|
||||
private val functionArgumentCount = irDefinition.arguments.size
|
||||
|
||||
init {
|
||||
for (argument in irDefinition.arguments.reversed()) {
|
||||
val stubVar = code.localState.createOrFindLocal(argument.symbol)
|
||||
code.emit(Opcode.StoreLocal, listOf(stubVar.index))
|
||||
}
|
||||
}
|
||||
|
||||
fun final() {
|
||||
if (irDefinition.type == IrDefinitionType.CodeFunction) {
|
||||
code.emit(Opcode.None)
|
||||
}
|
||||
code.emit(Opcode.Return)
|
||||
code.emit(Opcode.End)
|
||||
}
|
||||
|
||||
private fun resolve(symbol: IrSymbol): Loadable = code.localState.resolve(symbol)
|
||||
|
||||
private fun load(callOrStubVar: Loadable) {
|
||||
if (callOrStubVar.stubVar != null) {
|
||||
code.emit(Opcode.LoadLocal, listOf(callOrStubVar.stubVar.index))
|
||||
} else {
|
||||
code.emit(Opcode.Integer, listOf(code.nextOpInst() + 2u))
|
||||
code.patch(Opcode.Call, listOf(0u), mapOf(0 to callOrStubVar.call!!))
|
||||
}
|
||||
}
|
||||
|
||||
private fun store(stubVar: StubVar) {
|
||||
code.emit(Opcode.StoreLocal, listOf(stubVar.index))
|
||||
}
|
||||
|
||||
override fun visitIrBeak(ir: IrBreak) {
|
||||
val loop = code.localState.findLoopState(ir.target)
|
||||
code.patch(Opcode.Jump, listOf(0u), 0, symbol, loop.exitJumpTarget)
|
||||
}
|
||||
|
||||
override fun visitIrCall(ir: IrCall) {
|
||||
val target = resolve(ir.target)
|
||||
val targetSymbol = target.call!!
|
||||
val retRel = MutableRel(0u)
|
||||
for (argument in ir.arguments) {
|
||||
visit(argument)
|
||||
}
|
||||
val variableArguments = ir.variableArguments
|
||||
if (variableArguments != null) {
|
||||
for (argument in variableArguments) {
|
||||
visit(argument)
|
||||
}
|
||||
code.emit(Opcode.ListMake, listOf(variableArguments.size.toUInt()))
|
||||
}
|
||||
retRel.rel = code.nextOpInst() + 2u
|
||||
code.patch(Opcode.ReturnAddress, listOf(0u), 0, symbol, retRel)
|
||||
code.patch(Opcode.Call, listOf(0u), mapOf(0 to targetSymbol))
|
||||
}
|
||||
|
||||
override fun visitIrCodeBlock(ir: IrCodeBlock) {
|
||||
for (item in ir.items) {
|
||||
visit(item)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitIrConditional(ir: IrConditional) {
|
||||
val thenRel = MutableRel(0u)
|
||||
val endRel = MutableRel(0u)
|
||||
visit(ir.conditional)
|
||||
code.patch(Opcode.JumpIf, listOf(0u), 0, symbol, thenRel)
|
||||
visit(ir.ifTrue)
|
||||
code.patch(Opcode.Jump, listOf(0u), 0, symbol, endRel)
|
||||
thenRel.rel = code.nextOpInst()
|
||||
visit(ir.ifFalse)
|
||||
endRel.rel = code.nextOpInst()
|
||||
}
|
||||
|
||||
override fun visitIrBooleanConstant(ir: IrBooleanConstant) {
|
||||
code.emit(if (ir.value) Opcode.True else Opcode.False)
|
||||
}
|
||||
|
||||
override fun visitIrIntegerConstant(ir: IrIntegerConstant) {
|
||||
code.emit(Opcode.Integer, listOf(ir.value.toUInt()))
|
||||
}
|
||||
|
||||
override fun visitIrLongConstant(ir: IrLongConstant) {
|
||||
code.emit(Opcode.Integer, listOf(ir.value.toUInt()))
|
||||
}
|
||||
|
||||
override fun visitIrDoubleConstant(ir: IrDoubleConstant) {
|
||||
code.emit(Opcode.Integer, listOf(ir.value.toUInt()))
|
||||
}
|
||||
|
||||
override fun visitIrStringConstant(ir: IrStringConstant) {
|
||||
val bytes = ir.value.toByteArray()
|
||||
val constant = symbol.compilableSlab.compiler.constantPool.assign(ConstantTag.String, bytes)
|
||||
code.emit(Opcode.Constant, listOf(constant))
|
||||
}
|
||||
|
||||
override fun visitIrNoneConstant(ir: IrNoneConstant) {
|
||||
code.emit(Opcode.None)
|
||||
}
|
||||
|
||||
override fun visitIrContinue(ir: IrContinue) {
|
||||
val loop = code.localState.findLoopState(ir.target)
|
||||
code.patch(Opcode.Jump, listOf(0u), 0, symbol, loop.exitJumpTarget)
|
||||
}
|
||||
|
||||
override fun visitIrInfix(ir: IrInfix) {
|
||||
visit(ir.left)
|
||||
visit(ir.right)
|
||||
when (ir.op) {
|
||||
IrInfixOp.Add -> code.emit(Opcode.Add)
|
||||
IrInfixOp.Subtract -> code.emit(Opcode.Subtract)
|
||||
IrInfixOp.Multiply -> code.emit(Opcode.Multiply)
|
||||
IrInfixOp.Divide -> code.emit(Opcode.Divide)
|
||||
IrInfixOp.Equals -> code.emit(Opcode.CompareEqual)
|
||||
IrInfixOp.NotEquals -> {
|
||||
code.emit(Opcode.CompareEqual)
|
||||
code.emit(Opcode.Not)
|
||||
}
|
||||
IrInfixOp.EuclideanModulo -> code.emit(Opcode.EuclideanModulo)
|
||||
IrInfixOp.Remainder -> code.emit(Opcode.Remainder)
|
||||
IrInfixOp.Lesser -> code.emit(Opcode.CompareLesser)
|
||||
IrInfixOp.Greater -> code.emit(Opcode.CompareGreater)
|
||||
IrInfixOp.GreaterEqual -> code.emit(Opcode.CompareGreaterEqual)
|
||||
IrInfixOp.LesserEqual -> code.emit(Opcode.CompareLesserEqual)
|
||||
IrInfixOp.BooleanAnd -> code.emit(Opcode.And)
|
||||
IrInfixOp.BooleanOr -> code.emit(Opcode.Or)
|
||||
IrInfixOp.BinaryAnd -> code.emit(Opcode.BinaryAnd)
|
||||
IrInfixOp.BinaryOr -> code.emit(Opcode.BinaryOr)
|
||||
IrInfixOp.BinaryExclusiveOr -> code.emit(Opcode.BinaryXor)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitIrList(ir: IrList) {
|
||||
val count = ir.items.size
|
||||
for (item in ir.items) {
|
||||
visit(item)
|
||||
}
|
||||
code.emit(Opcode.ListMake, listOf(count.toUInt()))
|
||||
}
|
||||
|
||||
override fun visitIrLoad(ir: IrLoad) {
|
||||
val loadable = resolve(ir.target)
|
||||
load(loadable)
|
||||
}
|
||||
|
||||
override fun visitIrLoop(ir: IrLoop) {
|
||||
val startOfBody = MutableRel(0u)
|
||||
val startOfLoop = MutableRel(0u)
|
||||
val endOfLoop = MutableRel(0u)
|
||||
code.localState.startLoop(ir.symbol, code.nextOpInst(), endOfLoop)
|
||||
startOfLoop.rel = code.nextOpInst()
|
||||
visit(ir.condition)
|
||||
code.patch(Opcode.JumpIf, listOf(0u), 0, symbol, startOfBody)
|
||||
code.patch(Opcode.Jump, listOf(0u), 0, symbol, endOfLoop)
|
||||
startOfBody.rel = code.nextOpInst()
|
||||
visit(ir.inner)
|
||||
code.patch(Opcode.Jump, listOf(0u), 0, symbol, startOfLoop)
|
||||
endOfLoop.rel = code.nextOpInst()
|
||||
code.localState.endLoop(ir.symbol)
|
||||
}
|
||||
|
||||
override fun visitIrPrefix(ir: IrPrefix) {
|
||||
visit(ir.value)
|
||||
when (ir.op) {
|
||||
IrPrefixOp.BooleanNot -> code.emit(Opcode.Not)
|
||||
IrPrefixOp.UnaryPlus -> code.emit(Opcode.UnaryPlus)
|
||||
IrPrefixOp.UnaryMinus -> code.emit(Opcode.UnaryMinus)
|
||||
IrPrefixOp.BinaryNot -> code.emit(Opcode.BinaryNot)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitIrReturn(ir: IrReturn) {
|
||||
visit(ir.value)
|
||||
code.emit(Opcode.Return)
|
||||
}
|
||||
|
||||
override fun visitIrStore(ir: IrStore) {
|
||||
visit(ir.value)
|
||||
val variable = code.localState.createOrFindLocal(ir.target)
|
||||
store(variable)
|
||||
}
|
||||
|
||||
override fun visitIrSuffix(ir: IrSuffix) {
|
||||
val loadable = code.localState.resolve(ir.target)
|
||||
load(loadable)
|
||||
when (ir.op) {
|
||||
IrSuffixOp.Increment -> {
|
||||
code.emit(Opcode.Integer, listOf(1u))
|
||||
code.emit(Opcode.Add, emptyList())
|
||||
}
|
||||
IrSuffixOp.Decrement-> {
|
||||
code.emit(Opcode.Integer, listOf(1u))
|
||||
code.emit(Opcode.Subtract, emptyList())
|
||||
}
|
||||
}
|
||||
store(loadable.stubVar!!)
|
||||
}
|
||||
|
||||
override fun visitIrNativeDefinition(ir: IrNativeDefinition) {
|
||||
for (def in ir.definitions.reversed()) {
|
||||
val defConstant = symbol.compilableSlab.compiler.constantPool.assign(
|
||||
ConstantTag.String,
|
||||
def.encodeToByteArray()
|
||||
)
|
||||
code.emit(Opcode.Constant, listOf(defConstant))
|
||||
}
|
||||
val formConstant = symbol.compilableSlab.compiler.constantPool.assign(
|
||||
ConstantTag.String,
|
||||
ir.form.encodeToByteArray()
|
||||
)
|
||||
code.emit(Opcode.Native, listOf(formConstant, ir.definitions.size.toUInt(), functionArgumentCount.toUInt()))
|
||||
}
|
||||
}
|
@ -4,10 +4,13 @@ import gay.pizza.pork.bir.IrSymbol
|
||||
import gay.pizza.pork.bir.IrSymbolAssignment
|
||||
import gay.pizza.pork.bir.IrSymbolTag
|
||||
|
||||
class IrSymbolWorld(val irSymbolAssignment: IrSymbolAssignment) {
|
||||
private val symbols = mutableMapOf<Any, IrSymbol>()
|
||||
class IrSymbolWorld<T>(val irSymbolAssignment: IrSymbolAssignment) {
|
||||
private val symbols = mutableMapOf<T, IrSymbol>()
|
||||
|
||||
fun lookup(value: Any, tag: IrSymbolTag): IrSymbol = symbols.getOrPut(value) {
|
||||
fun create(value: T, tag: IrSymbolTag): IrSymbol = symbols.getOrPut(value) {
|
||||
irSymbolAssignment.next(tag)
|
||||
}
|
||||
|
||||
fun resolve(value: T): IrSymbol? = symbols[value]
|
||||
fun resolve(symbol: IrSymbol): T? = symbols.entries.firstOrNull { it.value == symbol }?.key
|
||||
}
|
||||
|
@ -1,62 +1,55 @@
|
||||
package gay.pizza.pork.compiler
|
||||
|
||||
import gay.pizza.pork.ast.gen.Symbol
|
||||
import gay.pizza.pork.bir.IrSymbol
|
||||
import gay.pizza.pork.bytecode.MutableRel
|
||||
import gay.pizza.pork.frontend.scope.ScopeSymbol
|
||||
|
||||
class LocalState(val symbol: CompilableSymbol) {
|
||||
private var internalLoopState: LoopState? = null
|
||||
val loopState: LoopState?
|
||||
get() = internalLoopState
|
||||
|
||||
private var localVarIndex: UInt = 0u
|
||||
private val variables = mutableListOf<MutableList<StubVar>>()
|
||||
private val stubVariables = mutableMapOf<IrSymbol, StubVar>()
|
||||
private val loops = mutableMapOf<IrSymbol, LoopState>()
|
||||
|
||||
fun startLoop(startOfLoop: UInt, exitJumpTarget: MutableRel) {
|
||||
internalLoopState = LoopState(
|
||||
fun startLoop(symbol: IrSymbol, startOfLoop: UInt, exitJumpTarget: MutableRel) {
|
||||
val existing = loops[symbol]
|
||||
if (existing != null) {
|
||||
throw CompileError("Starting loop that already is started")
|
||||
}
|
||||
val loopState = LoopState(
|
||||
startOfLoop = startOfLoop,
|
||||
exitJumpTarget = exitJumpTarget,
|
||||
scopeDepth = (internalLoopState?.scopeDepth ?: 0u) + 1u,
|
||||
enclosing = internalLoopState
|
||||
scopeDepth = (internalLoopState?.scopeDepth ?: 0u) + 1u
|
||||
)
|
||||
loops[symbol] = loopState
|
||||
}
|
||||
|
||||
fun endLoop() {
|
||||
internalLoopState = internalLoopState?.enclosing
|
||||
fun findLoopState(symbol: IrSymbol): LoopState =
|
||||
loops[symbol] ?: throw CompileError("Unable to find target loop")
|
||||
|
||||
fun endLoop(symbol: IrSymbol) {
|
||||
loops.remove(symbol) ?: throw CompileError("End of loop target not found")
|
||||
}
|
||||
|
||||
fun createLocal(symbol: Symbol): StubVar {
|
||||
val scope = variables.last()
|
||||
val variable = StubVar(localVarIndex++, symbol)
|
||||
scope.add(variable)
|
||||
fun createOrFindLocal(symbol: IrSymbol): StubVar {
|
||||
val existing = stubVariables[symbol]
|
||||
if (existing != null) {
|
||||
return existing
|
||||
}
|
||||
val variable = StubVar(localVarIndex++, symbol.id)
|
||||
stubVariables[symbol] = variable
|
||||
return variable
|
||||
}
|
||||
|
||||
fun createAnonymousLocal(): StubVar {
|
||||
val scope = variables.last()
|
||||
val variable = StubVar(localVarIndex++)
|
||||
scope.add(variable)
|
||||
return variable
|
||||
}
|
||||
|
||||
fun pushScope() {
|
||||
variables.add(mutableListOf())
|
||||
}
|
||||
|
||||
fun popScope() {
|
||||
variables.removeLast()
|
||||
}
|
||||
|
||||
fun resolve(symbol: Symbol): Loadable {
|
||||
for (scope in variables.reversed()) {
|
||||
val found = scope.firstOrNull { it.symbol == symbol }
|
||||
if (found != null) {
|
||||
return Loadable(stubVar = found)
|
||||
}
|
||||
fun resolve(symbol: IrSymbol): Loadable {
|
||||
val localStubVar = stubVariables[symbol]
|
||||
if (localStubVar != null) {
|
||||
return Loadable(stubVar = localStubVar)
|
||||
}
|
||||
val found = this.symbol.compilableSlab.resolveVisible(symbol)
|
||||
if (found != null) {
|
||||
return Loadable(call = found)
|
||||
}
|
||||
throw RuntimeException("Unable to resolve symbol: ${symbol.id}")
|
||||
val value = this.symbol.compilableSlab.compiler.irSymbolWorld.resolve(symbol) ?:
|
||||
throw RuntimeException("Unable to resolve symbol: ${symbol.id} ${symbol.tag}")
|
||||
val scopeSymbol = value as ScopeSymbol
|
||||
val call = this.symbol.compilableSlab.compiler.resolve(scopeSymbol)
|
||||
return Loadable(call = call)
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,5 @@ import gay.pizza.pork.bytecode.MutableRel
|
||||
class LoopState(
|
||||
val startOfLoop: UInt,
|
||||
val exitJumpTarget: MutableRel,
|
||||
val scopeDepth: UInt,
|
||||
val enclosing: LoopState? = null
|
||||
val scopeDepth: UInt
|
||||
)
|
||||
|
@ -1,301 +0,0 @@
|
||||
package gay.pizza.pork.compiler
|
||||
|
||||
import gay.pizza.pork.ast.FunctionLevelVisitor
|
||||
import gay.pizza.pork.ast.gen.*
|
||||
import gay.pizza.pork.bytecode.ConstantTag
|
||||
import gay.pizza.pork.bytecode.MutableRel
|
||||
import gay.pizza.pork.bytecode.Opcode
|
||||
|
||||
class StubOpEmitter(val compiler: Compiler, val symbol: CompilableSymbol) : FunctionLevelVisitor<Unit>() {
|
||||
val code: CodeBuilder = CodeBuilder(symbol)
|
||||
|
||||
fun allocateOuterScope(definition: FunctionDefinition) {
|
||||
val allNormalArguments = definition.arguments.takeWhile { !it.multiple }
|
||||
val varArgument = definition.arguments.firstOrNull { it.multiple }
|
||||
|
||||
for (arg in allNormalArguments.reversed()) {
|
||||
val functionLocal = code.localState.createLocal(arg.symbol)
|
||||
code.emit(Opcode.StoreLocal, listOf(functionLocal.index))
|
||||
}
|
||||
|
||||
if (varArgument != null) {
|
||||
val functionLocal = code.localState.createLocal(varArgument.symbol)
|
||||
code.emit(Opcode.StoreLocal, listOf(functionLocal.index))
|
||||
}
|
||||
}
|
||||
|
||||
fun enter() {
|
||||
code.localState.pushScope()
|
||||
}
|
||||
|
||||
fun exit() {
|
||||
code.localState.popScope()
|
||||
code.emit(Opcode.None)
|
||||
code.emit(Opcode.Return)
|
||||
}
|
||||
|
||||
override fun visitBlock(node: Block) {
|
||||
code.localState.pushScope()
|
||||
node.visitChildren(this)
|
||||
code.localState.popScope()
|
||||
}
|
||||
|
||||
override fun visitBooleanLiteral(node: BooleanLiteral) {
|
||||
code.emit(if (node.value) Opcode.True else Opcode.False)
|
||||
}
|
||||
|
||||
override fun visitBreak(node: Break) {
|
||||
code.patch(Opcode.Jump, listOf(0u), 0, symbol, code.localState.loopState!!.exitJumpTarget)
|
||||
}
|
||||
|
||||
override fun visitContinue(node: Continue) {
|
||||
code.patch(Opcode.Jump, listOf(0u), 0, symbol, code.localState.loopState!!.startOfLoop)
|
||||
}
|
||||
|
||||
override fun visitDoubleLiteral(node: DoubleLiteral) {
|
||||
code.emit(Opcode.Integer, listOf(node.value.toUInt()))
|
||||
}
|
||||
|
||||
override fun visitForIn(node: ForIn) {
|
||||
val listLocalVar = code.localState.createAnonymousLocal()
|
||||
val sizeLocalVar = code.localState.createAnonymousLocal()
|
||||
val currentIndexVar = code.localState.createAnonymousLocal()
|
||||
val currentValueVar = code.localState.createLocal(node.item.symbol)
|
||||
node.expression.visit(this)
|
||||
code.emit(Opcode.StoreLocal, listOf(listLocalVar.index))
|
||||
load(Loadable(stubVar = listLocalVar))
|
||||
code.emit(Opcode.ListSize)
|
||||
code.emit(Opcode.StoreLocal, listOf(sizeLocalVar.index))
|
||||
code.emit(Opcode.Integer, listOf(0u))
|
||||
code.emit(Opcode.StoreLocal, listOf(currentIndexVar.index))
|
||||
val endOfLoop = MutableRel(0u)
|
||||
val startOfLoop = code.nextOpInst()
|
||||
code.localState.startLoop(startOfLoop, endOfLoop)
|
||||
load(Loadable(stubVar = currentIndexVar))
|
||||
load(Loadable(stubVar = sizeLocalVar))
|
||||
code.emit(Opcode.CompareGreaterEqual)
|
||||
code.patch(Opcode.JumpIf, listOf(0u), 0, symbol, endOfLoop)
|
||||
load(Loadable(stubVar = currentIndexVar))
|
||||
load(Loadable(stubVar = listLocalVar))
|
||||
code.emit(Opcode.Index)
|
||||
code.emit(Opcode.StoreLocal, listOf(currentValueVar.index))
|
||||
node.block.visit(this)
|
||||
code.emit(Opcode.LoadLocal, listOf(currentIndexVar.index))
|
||||
code.emit(Opcode.Integer, listOf(1u))
|
||||
code.emit(Opcode.Add)
|
||||
code.emit(Opcode.StoreLocal, listOf(currentIndexVar.index))
|
||||
code.patch(Opcode.Jump, listOf(0u), 0, symbol, startOfLoop)
|
||||
endOfLoop.rel = code.nextOpInst()
|
||||
}
|
||||
|
||||
override fun visitFunctionCall(node: FunctionCall) {
|
||||
val targetScopeSymbol = symbol.scopeSymbol.scope.resolve(node.symbol) ?:
|
||||
throw RuntimeException("Unable to resolve symbol: ${node.symbol.id}")
|
||||
val targetSymbol = compiler.resolve(targetScopeSymbol)
|
||||
val functionDefinition = targetSymbol.scopeSymbol.definition as FunctionDefinition
|
||||
val retRel = MutableRel(0u)
|
||||
val normalArguments = mutableListOf<Expression>()
|
||||
var variableArguments: List<Expression>? = null
|
||||
if (functionDefinition.arguments.any { it.multiple }) {
|
||||
variableArguments = emptyList()
|
||||
}
|
||||
|
||||
for ((index, item) in functionDefinition.arguments.zip(node.arguments).withIndex()) {
|
||||
val (spec, value) = item
|
||||
if (spec.multiple) {
|
||||
val remaining = node.arguments.drop(index)
|
||||
variableArguments = remaining
|
||||
break
|
||||
} else {
|
||||
normalArguments.add(value)
|
||||
}
|
||||
}
|
||||
|
||||
if (variableArguments != null) {
|
||||
for (item in variableArguments.reversed()) {
|
||||
item.visit(this)
|
||||
}
|
||||
code.emit(Opcode.ListMake, listOf(variableArguments.size.toUInt()))
|
||||
}
|
||||
|
||||
for (item in normalArguments.reversed()) {
|
||||
visit(item)
|
||||
}
|
||||
|
||||
retRel.rel = code.nextOpInst() + 2u
|
||||
code.patch(Opcode.ReturnAddress, listOf(0u), 0, symbol, retRel)
|
||||
code.patch(Opcode.Call, listOf(0u), mapOf(0 to targetSymbol))
|
||||
}
|
||||
|
||||
override fun visitIf(node: If) {
|
||||
val thenRel = MutableRel(0u)
|
||||
val endRel = MutableRel(0u)
|
||||
node.condition.visit(this)
|
||||
code.patch(Opcode.JumpIf, listOf(0u), 0, symbol, thenRel)
|
||||
node.elseBlock?.visit(this)
|
||||
code.patch(Opcode.Jump, listOf(0u), 0, symbol, endRel)
|
||||
thenRel.rel = code.nextOpInst()
|
||||
node.thenBlock.visit(this)
|
||||
endRel.rel = code.nextOpInst()
|
||||
}
|
||||
|
||||
override fun visitIndexedBy(node: IndexedBy) {
|
||||
node.expression.visit(this)
|
||||
node.index.visit(this)
|
||||
code.emit(Opcode.Index)
|
||||
}
|
||||
|
||||
override fun visitInfixOperation(node: InfixOperation) {
|
||||
node.left.visit(this)
|
||||
node.right.visit(this)
|
||||
when (node.op) {
|
||||
InfixOperator.Plus -> code.emit(Opcode.Add)
|
||||
InfixOperator.Minus -> code.emit(Opcode.Subtract)
|
||||
InfixOperator.Multiply -> code.emit(Opcode.Multiply)
|
||||
InfixOperator.Divide -> code.emit(Opcode.Divide)
|
||||
InfixOperator.Equals -> code.emit(Opcode.CompareEqual)
|
||||
InfixOperator.NotEquals -> {
|
||||
code.emit(Opcode.CompareEqual)
|
||||
code.emit(Opcode.Not)
|
||||
}
|
||||
InfixOperator.EuclideanModulo -> code.emit(Opcode.EuclideanModulo)
|
||||
InfixOperator.Remainder -> code.emit(Opcode.Remainder)
|
||||
InfixOperator.Lesser -> code.emit(Opcode.CompareLesser)
|
||||
InfixOperator.Greater -> code.emit(Opcode.CompareGreater)
|
||||
InfixOperator.GreaterEqual -> code.emit(Opcode.CompareGreaterEqual)
|
||||
InfixOperator.LesserEqual -> code.emit(Opcode.CompareLesserEqual)
|
||||
InfixOperator.BooleanAnd -> code.emit(Opcode.And)
|
||||
InfixOperator.BooleanOr -> code.emit(Opcode.Or)
|
||||
InfixOperator.BinaryAnd -> code.emit(Opcode.BinaryAnd)
|
||||
InfixOperator.BinaryOr -> code.emit(Opcode.BinaryOr)
|
||||
InfixOperator.BinaryExclusiveOr -> code.emit(Opcode.BinaryXor)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitIntegerLiteral(node: IntegerLiteral) {
|
||||
code.emit(Opcode.Integer, listOf(node.value.toUInt()))
|
||||
}
|
||||
|
||||
override fun visitLetAssignment(node: LetAssignment) {
|
||||
val variable = code.localState.createLocal(node.symbol)
|
||||
node.value.visit(this)
|
||||
code.emit(Opcode.StoreLocal, listOf(variable.index))
|
||||
}
|
||||
|
||||
override fun visitListLiteral(node: ListLiteral) {
|
||||
val count = node.items.size
|
||||
for (item in node.items) {
|
||||
item.visit(this)
|
||||
}
|
||||
code.emit(Opcode.ListMake, listOf(count.toUInt()))
|
||||
}
|
||||
|
||||
override fun visitLongLiteral(node: LongLiteral) {
|
||||
code.emit(Opcode.Integer, listOf(node.value.toUInt()))
|
||||
}
|
||||
|
||||
override fun visitNoneLiteral(node: NoneLiteral) {
|
||||
code.emit(Opcode.None)
|
||||
}
|
||||
|
||||
override fun visitParentheses(node: Parentheses) {
|
||||
node.expression.visit(this)
|
||||
}
|
||||
|
||||
override fun visitPrefixOperation(node: PrefixOperation) {
|
||||
node.expression.visit(this)
|
||||
when (node.op) {
|
||||
PrefixOperator.BooleanNot -> code.emit(Opcode.Not)
|
||||
PrefixOperator.UnaryPlus -> code.emit(Opcode.UnaryPlus)
|
||||
PrefixOperator.UnaryMinus -> code.emit(Opcode.UnaryMinus)
|
||||
PrefixOperator.BinaryNot -> code.emit(Opcode.BinaryNot)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitReturn(node: Return) {
|
||||
node.value.visit(this)
|
||||
code.emit(Opcode.Return)
|
||||
}
|
||||
|
||||
override fun visitSetAssignment(node: SetAssignment) {
|
||||
val stubVarOrCall = code.localState.resolve(node.symbol)
|
||||
if (stubVarOrCall.stubVar == null) {
|
||||
throw RuntimeException("Invalid set assignment.")
|
||||
}
|
||||
node.value.visit(this)
|
||||
code.emit(Opcode.StoreLocal, listOf(stubVarOrCall.stubVar.index))
|
||||
}
|
||||
|
||||
override fun visitStringLiteral(node: StringLiteral) {
|
||||
val bytes = node.text.toByteArray()
|
||||
val constant = compiler.constantPool.assign(ConstantTag.String, bytes)
|
||||
code.emit(Opcode.Constant, listOf(constant))
|
||||
}
|
||||
|
||||
override fun visitSuffixOperation(node: SuffixOperation) {
|
||||
val stubVarOrCall = code.localState.resolve(node.reference.symbol)
|
||||
if (stubVarOrCall.stubVar == null) {
|
||||
throw RuntimeException("Invalid suffix operation.")
|
||||
}
|
||||
load(stubVarOrCall)
|
||||
when (node.op) {
|
||||
SuffixOperator.Increment -> {
|
||||
code.emit(Opcode.Integer, listOf(1u))
|
||||
code.emit(Opcode.Add, emptyList())
|
||||
code.emit(Opcode.StoreLocal, listOf(stubVarOrCall.stubVar.index))
|
||||
}
|
||||
SuffixOperator.Decrement -> {
|
||||
code.emit(Opcode.Integer, listOf(1u))
|
||||
code.emit(Opcode.Subtract, emptyList())
|
||||
code.emit(Opcode.StoreLocal, listOf(stubVarOrCall.stubVar.index))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitSymbolReference(node: SymbolReference) {
|
||||
val variable = code.localState.resolve(node.symbol)
|
||||
load(variable)
|
||||
}
|
||||
|
||||
override fun visitVarAssignment(node: VarAssignment) {
|
||||
val variable = code.localState.createLocal(node.symbol)
|
||||
node.value.visit(this)
|
||||
code.emit(Opcode.StoreLocal, listOf(variable.index))
|
||||
}
|
||||
|
||||
override fun visitWhile(node: While) {
|
||||
val startOfBody = MutableRel(0u)
|
||||
val startOfLoop = MutableRel(0u)
|
||||
val endOfLoop = MutableRel(0u)
|
||||
code.localState.startLoop(code.nextOpInst(), endOfLoop)
|
||||
startOfLoop.rel = code.nextOpInst()
|
||||
node.condition.visit(this)
|
||||
code.patch(Opcode.JumpIf, listOf(0u), 0, symbol, startOfBody)
|
||||
code.patch(Opcode.Jump, listOf(0u), 0, symbol, endOfLoop)
|
||||
startOfBody.rel = code.nextOpInst()
|
||||
node.block.visit(this)
|
||||
code.patch(Opcode.Jump, listOf(0u), 0, symbol, startOfLoop)
|
||||
endOfLoop.rel = code.nextOpInst()
|
||||
code.localState.endLoop()
|
||||
}
|
||||
|
||||
override fun visitNativeFunctionDescriptor(node: NativeFunctionDescriptor) {
|
||||
for (def in node.definitions) {
|
||||
val defConstant = compiler.constantPool.assign(ConstantTag.String, def.text.toByteArray())
|
||||
code.emit(Opcode.Constant, listOf(defConstant))
|
||||
}
|
||||
val formConstant = compiler.constantPool.assign(ConstantTag.String, node.form.id.toByteArray())
|
||||
val functionDefinition = symbol.scopeSymbol.definition as FunctionDefinition
|
||||
val functionArgumentCount = functionDefinition.arguments.size
|
||||
code.emit(Opcode.Native, listOf(formConstant, node.definitions.size.toUInt(), functionArgumentCount.toUInt()))
|
||||
}
|
||||
|
||||
private fun load(callOrStubVar: Loadable) {
|
||||
if (callOrStubVar.stubVar != null) {
|
||||
code.emit(Opcode.LoadLocal, listOf(callOrStubVar.stubVar.index))
|
||||
} else {
|
||||
code.emit(Opcode.Integer, listOf(code.nextOpInst() + 2u))
|
||||
code.patch(Opcode.Call, listOf(0u), mapOf(0 to callOrStubVar.call!!))
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
package gay.pizza.pork.compiler
|
||||
|
||||
import gay.pizza.pork.ast.gen.Symbol
|
||||
|
||||
class StubVar(
|
||||
val index: UInt,
|
||||
val symbol: Symbol? = null
|
||||
val id: UInt
|
||||
)
|
||||
|
@ -18,4 +18,6 @@ class ScopeSymbol(val slabScope: SlabScope, val definition: Definition) {
|
||||
result = 31 * result + symbol.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String = "ScopeSymbol(${symbol.id})"
|
||||
}
|
||||
|
@ -3,14 +3,8 @@ package gay.pizza.pork.tool
|
||||
import com.github.ajalt.clikt.core.CliktCommand
|
||||
import com.github.ajalt.clikt.parameters.arguments.argument
|
||||
import gay.pizza.dough.fs.PlatformFsProvider
|
||||
import gay.pizza.pork.ast.gen.FunctionDefinition
|
||||
import gay.pizza.pork.ast.gen.Symbol
|
||||
import gay.pizza.pork.ast.gen.visit
|
||||
import gay.pizza.pork.bir.IrSymbolAssignment
|
||||
import gay.pizza.pork.bir.IrSymbolTag
|
||||
import gay.pizza.pork.compiler.Compiler
|
||||
import gay.pizza.pork.compiler.IrCodeEmitter
|
||||
import gay.pizza.pork.compiler.IrSymbolWorld
|
||||
import gay.pizza.pork.minimal.FileTool
|
||||
|
||||
class CompileCommand : CliktCommand(help = "Compile Pork to Bytecode", name = "compile") {
|
||||
@ -37,12 +31,5 @@ class CompileCommand : CliktCommand(help = "Compile Pork to Bytecode", name = "c
|
||||
println(" ${symbol.offset + index.toUInt()} ${op}${annotation}")
|
||||
}
|
||||
}
|
||||
|
||||
val irSymbolAssignment = IrSymbolAssignment()
|
||||
val irSymbolWorld = IrSymbolWorld(irSymbolAssignment)
|
||||
val self = irSymbolAssignment.next(IrSymbolTag.Function)
|
||||
val irCodeEmitter = IrCodeEmitter(self, irSymbolWorld, irSymbolAssignment, compiledSlab.slab.scope)
|
||||
val ir = irCodeEmitter.visit((compiledMain.scopeSymbol.definition as FunctionDefinition).block!!)
|
||||
println(ir)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user