language: implement proper virtual machine support

This commit is contained in:
2023-11-28 05:23:48 -08:00
parent 8951c3cd60
commit f2ff23e9be
55 changed files with 312 additions and 63 deletions

View File

@ -1,5 +1,8 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
data class IrBreak(val target: IrSymbol) : IrCodeElement { data class IrBreak(val target: IrSymbol) : IrCodeElement {
override fun crawl(block: (IrElement) -> Unit) { override fun crawl(block: (IrElement) -> Unit) {
block(target) block(target)

View File

@ -1,5 +1,8 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
data class IrCall( data class IrCall(
override val target: IrSymbol, override val target: IrSymbol,
val arguments: List<IrCodeElement>, val arguments: List<IrCodeElement>,

View File

@ -1,5 +1,8 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
data class IrCodeBlock(val items: List<IrCodeElement>) : IrCodeElement { data class IrCodeBlock(val items: List<IrCodeElement>) : IrCodeElement {
override fun crawl(block: (IrElement) -> Unit) { override fun crawl(block: (IrElement) -> Unit) {
items.forEach(block) items.forEach(block)

View File

@ -1,3 +1,6 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
sealed interface IrCodeElement : IrElement sealed interface IrCodeElement : IrElement

View File

@ -1,6 +1,4 @@
package gay.pizza.pork.compiler package gay.pizza.pork.bir
import gay.pizza.pork.bir.*
interface IrCodeVisitor<T> : IrVisitor<T> { interface IrCodeVisitor<T> : IrVisitor<T> {
override fun visitIrDefinition(ir: IrDefinition): T { override fun visitIrDefinition(ir: IrDefinition): T {

View File

@ -1,5 +1,8 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
data class IrConditional( data class IrConditional(
val conditional: IrCodeElement, val conditional: IrCodeElement,
val ifTrue: IrCodeElement, val ifTrue: IrCodeElement,

View File

@ -1,12 +1,26 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
sealed interface IrConstant : IrCodeElement { sealed interface IrConstant : IrCodeElement {
override fun crawl(block: (IrElement) -> Unit) {} override fun crawl(block: (IrElement) -> Unit) {}
} }
@Serializable
data class IrIntegerConstant(val value: Int) : IrConstant data class IrIntegerConstant(val value: Int) : IrConstant
@Serializable
data class IrLongConstant(val value: Long) : IrConstant data class IrLongConstant(val value: Long) : IrConstant
@Serializable
data class IrDoubleConstant(val value: Double) : IrConstant data class IrDoubleConstant(val value: Double) : IrConstant
@Serializable
data class IrStringConstant(val value: String) : IrConstant data class IrStringConstant(val value: String) : IrConstant
@Serializable
data class IrBooleanConstant(val value: Boolean) : IrConstant data class IrBooleanConstant(val value: Boolean) : IrConstant
@Serializable
data object IrNoneConstant : IrConstant data object IrNoneConstant : IrConstant

View File

@ -1,5 +1,8 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
data class IrContinue(override val target: IrSymbol) : IrCodeElement, IrSymbolUser { data class IrContinue(override val target: IrSymbol) : IrCodeElement, IrSymbolUser {
override fun crawl(block: (IrElement) -> Unit) { override fun crawl(block: (IrElement) -> Unit) {
block(target) block(target)

View File

@ -1,5 +1,8 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
data class IrDefinition( data class IrDefinition(
override val symbol: IrSymbol, override val symbol: IrSymbol,
val type: IrDefinitionType, val type: IrDefinitionType,

View File

@ -1,5 +1,8 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
enum class IrDefinitionType { enum class IrDefinitionType {
Variable, Variable,
CodeFunction, CodeFunction,

View File

@ -1,5 +1,8 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
sealed interface IrElement { sealed interface IrElement {
fun crawl(block: (IrElement) -> Unit) fun crawl(block: (IrElement) -> Unit)
} }

View File

@ -1,5 +1,8 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
data class IrFunctionArgument(override val symbol: IrSymbol) : IrSymbolOwner { data class IrFunctionArgument(override val symbol: IrSymbol) : IrSymbolOwner {
override fun crawl(block: (IrElement) -> Unit) {} override fun crawl(block: (IrElement) -> Unit) {}
} }

View File

@ -0,0 +1,11 @@
package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
data class IrIndex(val data: IrCodeElement, val index: IrCodeElement) : IrCodeElement {
override fun crawl(block: (IrElement) -> Unit) {
block(data)
block(index)
}
}

View File

@ -1,5 +1,8 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
data class IrInfix(val op: IrInfixOp, val left: IrCodeElement, val right: IrCodeElement) : IrCodeElement { data class IrInfix(val op: IrInfixOp, val left: IrCodeElement, val right: IrCodeElement) : IrCodeElement {
override fun crawl(block: (IrElement) -> Unit) { override fun crawl(block: (IrElement) -> Unit) {
block(left) block(left)

View File

@ -1,5 +1,8 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
enum class IrInfixOp { enum class IrInfixOp {
Add, Add,
Subtract, Subtract,

View File

@ -1,5 +1,8 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
data class IrList(val items: List<IrCodeElement>) : IrCodeElement { data class IrList(val items: List<IrCodeElement>) : IrCodeElement {
override fun crawl(block: (IrElement) -> Unit) { override fun crawl(block: (IrElement) -> Unit) {
items.forEach(block) items.forEach(block)

View File

@ -0,0 +1,10 @@
package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
data class IrListSize(val list: IrCodeElement) : IrCodeElement {
override fun crawl(block: (IrElement) -> Unit) {
block(list)
}
}

View File

@ -1,5 +1,8 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
data class IrLoad(override val target: IrSymbol) : IrCodeElement, IrSymbolUser { data class IrLoad(override val target: IrSymbol) : IrCodeElement, IrSymbolUser {
override fun crawl(block: (IrElement) -> Unit) { override fun crawl(block: (IrElement) -> Unit) {
block(target) block(target)

View File

@ -1,5 +1,8 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
data class IrLoop( data class IrLoop(
override val symbol: IrSymbol, override val symbol: IrSymbol,
val condition: IrCodeElement, val condition: IrCodeElement,

View File

@ -1,5 +1,8 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
data class IrNativeDefinition(val form: String, val definitions: List<String>) : IrCodeElement { data class IrNativeDefinition(val form: String, val definitions: List<String>) : IrCodeElement {
override fun crawl(block: (IrElement) -> Unit) {} override fun crawl(block: (IrElement) -> Unit) {}
} }

View File

@ -1,5 +1,8 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
data class IrPrefix(val op: IrPrefixOp, val value: IrCodeElement) : IrCodeElement { data class IrPrefix(val op: IrPrefixOp, val value: IrCodeElement) : IrCodeElement {
override fun crawl(block: (IrElement) -> Unit) { override fun crawl(block: (IrElement) -> Unit) {
block(value) block(value)

View File

@ -1,5 +1,8 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
enum class IrPrefixOp { enum class IrPrefixOp {
BooleanNot, BooleanNot,
UnaryPlus, UnaryPlus,

View File

@ -1,5 +1,8 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
data class IrReturn(val from: IrSymbol, val value: IrCodeElement) : IrCodeElement { data class IrReturn(val from: IrSymbol, val value: IrCodeElement) : IrCodeElement {
override fun crawl(block: (IrElement) -> Unit) { override fun crawl(block: (IrElement) -> Unit) {
block(from) block(from)

View File

@ -1,5 +1,8 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
data class IrSlab( data class IrSlab(
val location: IrSlabLocation, val location: IrSlabLocation,
val definitions: List<IrDefinition> val definitions: List<IrDefinition>

View File

@ -1,5 +1,8 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
data class IrSlabLocation( data class IrSlabLocation(
val form: String, val form: String,
val path: String val path: String

View File

@ -1,5 +1,8 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
data class IrStore(override val target: IrSymbol, val value: IrCodeElement) : IrCodeElement, IrSymbolUser { data class IrStore(override val target: IrSymbol, val value: IrCodeElement) : IrCodeElement, IrSymbolUser {
override fun crawl(block: (IrElement) -> Unit) { override fun crawl(block: (IrElement) -> Unit) {
value.crawl(block) value.crawl(block)

View File

@ -1,5 +1,8 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
data class IrSuffix(val op: IrSuffixOp, override val target: IrSymbol) : IrCodeElement, IrSymbolUser { data class IrSuffix(val op: IrSuffixOp, override val target: IrSymbol) : IrCodeElement, IrSymbolUser {
override fun crawl(block: (IrElement) -> Unit) { override fun crawl(block: (IrElement) -> Unit) {
block(target) block(target)

View File

@ -1,5 +1,8 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
enum class IrSuffixOp { enum class IrSuffixOp {
Increment, Increment,
Decrement Decrement

View File

@ -1,5 +1,24 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
data class IrSymbol(val id: UInt, val tag: IrSymbolTag) : IrElement { import kotlinx.serialization.Serializable
@Serializable
data class IrSymbol(val id: UInt, val tag: IrSymbolTag, val name: String? = null) : IrElement {
override fun crawl(block: (IrElement) -> Unit) {} override fun crawl(block: (IrElement) -> Unit) {}
override fun equals(other: Any?): Boolean {
if (other !is IrSymbol) return false
return other.id == id && other.tag == tag
}
override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + tag.hashCode()
return result
}
val friendlyName: String
get() = if (name != null) {
"$id $tag $name"
} else "$id $tag"
} }

View File

@ -4,5 +4,6 @@ class IrSymbolAssignment {
private var index = 0u private var index = 0u
private fun nextSymbolId(): UInt = index++ private fun nextSymbolId(): UInt = index++
fun next(tag: IrSymbolTag): IrSymbol = IrSymbol(nextSymbolId(), tag) fun next(tag: IrSymbolTag, name: String? = null): IrSymbol =
IrSymbol(id = nextSymbolId(), tag = tag, name = name)
} }

View File

@ -1,5 +1,8 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
sealed interface IrSymbolOwner : IrElement { sealed interface IrSymbolOwner : IrElement {
val symbol: IrSymbol val symbol: IrSymbol
} }

View File

@ -1,5 +1,8 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
enum class IrSymbolTag { enum class IrSymbolTag {
Function, Function,
Variable, Variable,

View File

@ -1,5 +1,8 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
sealed interface IrSymbolUser : IrElement { sealed interface IrSymbolUser : IrElement {
val target: IrSymbol val target: IrSymbol
} }

View File

@ -27,6 +27,8 @@ interface IrVisitor<T> {
fun visitIrWorld(ir: IrWorld): T fun visitIrWorld(ir: IrWorld): T
fun visitIrNativeDefinition(ir: IrNativeDefinition): T fun visitIrNativeDefinition(ir: IrNativeDefinition): T
fun visitIrFunctionArgument(ir: IrFunctionArgument): T fun visitIrFunctionArgument(ir: IrFunctionArgument): T
fun visitIrIndex(ir: IrIndex): T
fun visitIrListSize(ir: IrListSize): T
fun visit(ir: IrElement): T = when (ir) { fun visit(ir: IrElement): T = when (ir) {
is IrBreak -> visitIrBeak(ir) is IrBreak -> visitIrBeak(ir)
@ -55,5 +57,7 @@ interface IrVisitor<T> {
is IrWorld -> visitIrWorld(ir) is IrWorld -> visitIrWorld(ir)
is IrNativeDefinition -> visitIrNativeDefinition(ir) is IrNativeDefinition -> visitIrNativeDefinition(ir)
is IrFunctionArgument -> visitIrFunctionArgument(ir) is IrFunctionArgument -> visitIrFunctionArgument(ir)
is IrIndex -> visitIrIndex(ir)
is IrListSize -> visitIrListSize(ir)
} }
} }

View File

@ -1,5 +1,8 @@
package gay.pizza.pork.bir package gay.pizza.pork.bir
import kotlinx.serialization.Serializable
@Serializable
data class IrWorld(val slabs: List<IrSlab>) : IrElement { data class IrWorld(val slabs: List<IrSlab>) : IrElement {
override fun crawl(block: (IrElement) -> Unit) { override fun crawl(block: (IrElement) -> Unit) {
slabs.forEach(block) slabs.forEach(block)

View File

@ -33,13 +33,14 @@ enum class Opcode(val id: UByte) {
ListMake(31u), ListMake(31u),
ListSize(32u), ListSize(32u),
Integer(33u), Integer(33u),
Double(34u), Long(34u),
Call(35u), Double(35u),
EuclideanModulo(36u), Call(36u),
Remainder(37u), EuclideanModulo(37u),
Index(38u), Remainder(38u),
ScopeIn(39u), Index(39u),
ScopeOut(40u), ScopeIn(40u),
ReturnAddress(41u), ScopeOut(41u),
ReturnAddress(42u),
End(255u), End(255u),
} }

View File

@ -19,11 +19,6 @@ class CompilableSlab(val compiler: Compiler, val slab: Slab) {
it.scopeSymbol.symbol == symbol it.scopeSymbol.symbol == symbol
} }
fun resolveVisible(symbol: Symbol): CompilableSymbol? {
val scopeSymbol = slab.scope.resolve(symbol) ?: return null
return compiler.resolveOrNull(scopeSymbol)
}
private fun compileIrSlab(): IrSlab { private fun compileIrSlab(): IrSlab {
val definitions = mutableListOf<IrDefinition>() val definitions = mutableListOf<IrDefinition>()
for (compilableSymbol in compilableSymbols) { for (compilableSymbol in compilableSymbols) {

View File

@ -28,7 +28,7 @@ class CompilableSymbol(val compilableSlab: CompilableSlab, val scopeSymbol: Scop
private fun compileIrDefinition(): IrDefinition { private fun compileIrDefinition(): IrDefinition {
val compiler = compilableSlab.compiler val compiler = compilableSlab.compiler
val functionSymbol = compiler.irSymbolWorld.create(scopeSymbol, IrSymbolTag.Function) val functionSymbol = compiler.irSymbolWorld.create(scopeSymbol, IrSymbolTag.Function, scopeSymbol.symbol.id)
val irCodeEmitter = IrCodeEmitter( val irCodeEmitter = IrCodeEmitter(
self = functionSymbol, self = functionSymbol,
irSymbolWorld = compiler.irSymbolWorld, irSymbolWorld = compiler.irSymbolWorld,

View File

@ -13,7 +13,7 @@ class CompiledWorldLayout(val compiler: Compiler) : StubResolutionContext {
val stubOps = result.ops val stubOps = result.ops
symbolTable[symbol] = SymbolInfo( symbolTable[symbol] = SymbolInfo(
slab = symbol.compilableSlab.slab.location.commonLocationIdentity, slab = symbol.compilableSlab.slab.location.commonLocationIdentity,
symbol = symbol.id, symbol = symbol.scopeSymbol.symbol.id,
offset = start.toUInt(), offset = start.toUInt(),
size = stubOps.size.toUInt() size = stubOps.size.toUInt()
) )

View File

@ -1,12 +1,15 @@
package gay.pizza.pork.compiler package gay.pizza.pork.compiler
import gay.pizza.pork.bir.IrSlab
import gay.pizza.pork.bir.IrSymbolAssignment import gay.pizza.pork.bir.IrSymbolAssignment
import gay.pizza.pork.bir.IrWorld
import gay.pizza.pork.bytecode.CompiledWorld import gay.pizza.pork.bytecode.CompiledWorld
import gay.pizza.pork.bytecode.MutableConstantPool import gay.pizza.pork.bytecode.MutableConstantPool
import gay.pizza.pork.frontend.Slab import gay.pizza.pork.frontend.Slab
import gay.pizza.pork.frontend.World
import gay.pizza.pork.frontend.scope.ScopeSymbol import gay.pizza.pork.frontend.scope.ScopeSymbol
class Compiler { class Compiler(val world: World) {
val constantPool: MutableConstantPool = MutableConstantPool() val constantPool: MutableConstantPool = MutableConstantPool()
val compilableSlabs: ComputableState<Slab, CompilableSlab> = ComputableState { slab -> val compilableSlabs: ComputableState<Slab, CompilableSlab> = ComputableState { slab ->
CompilableSlab(this, slab) CompilableSlab(this, slab)
@ -39,6 +42,14 @@ class Compiler {
} }
} }
fun compileIrWorld(): IrWorld {
val slabs = mutableListOf<IrSlab>()
for (slab in world.slabs) {
slabs.add(compilableSlabs.of(slab).compiledIrSlab)
}
return IrWorld(slabs)
}
fun compile(entryPointSymbol: CompilableSymbol): CompiledWorld { fun compile(entryPointSymbol: CompilableSymbol): CompiledWorld {
val usedSymbolSet = mutableSetOf<CompilableSymbol>() val usedSymbolSet = mutableSetOf<CompilableSymbol>()
contributeCompiledSymbols(usedSymbolSet, entryPointSymbol.scopeSymbol, entryPointSymbol) contributeCompiledSymbols(usedSymbolSet, entryPointSymbol.scopeSymbol, entryPointSymbol)

View File

@ -13,7 +13,7 @@ class IrCodeEmitter(
val scope: SlabScope val scope: SlabScope
) : FunctionLevelVisitor<IrCodeElement>() { ) : FunctionLevelVisitor<IrCodeElement>() {
private val loopSymbols = mutableListOf<IrSymbol>() private val loopSymbols = mutableListOf<IrSymbol>()
private val localVariables = mutableListOf<MutableMap<String, LocalVariable>>() private val localVariables = mutableListOf<MutableMap<Pair<String?, UInt?>, LocalVariable>>()
var functionArguments: List<IrFunctionArgument> = emptyList() var functionArguments: List<IrFunctionArgument> = emptyList()
@ -46,7 +46,7 @@ class IrCodeEmitter(
} }
fun enterLocalScope() { fun enterLocalScope() {
val locals = mutableMapOf<String, LocalVariable>() val locals = mutableMapOf<Pair<String?, UInt?>, LocalVariable>()
localVariables.add(locals) localVariables.add(locals)
} }
@ -54,15 +54,16 @@ class IrCodeEmitter(
localVariables.removeLast() localVariables.removeLast()
} }
private fun createLocalVariable(name: Symbol): IrSymbol { private fun createLocalVariable(name: Symbol? = null): IrSymbol {
val symbol = irSymbolAssignment.next(IrSymbolTag.Local) val symbol = irSymbolAssignment.next(tag = IrSymbolTag.Local, name = name?.id)
val variable = LocalVariable(symbol, name) val variable = LocalVariable(symbol, name)
val variables = localVariables.last() val variables = localVariables.last()
val existing = variables[name.id] val identifier = name?.id to (if (name == null) symbol.id else null)
val existing = variables[identifier]
if (existing != null) { if (existing != null) {
throw CompileError("Unable to define local variable '${name.id}' within this scope, it already exists", name) throw CompileError("Unable to define local variable '${identifier.first}' within this scope, it already exists", name)
} }
variables[name.id] = variable variables[identifier] = variable
return symbol return symbol
} }
@ -74,10 +75,11 @@ class IrCodeEmitter(
} }
private fun lookupLocalVariable(name: Symbol): IrSymbol? { private fun lookupLocalVariable(name: Symbol): IrSymbol? {
val identifier = name.id to null
for (i in 1..localVariables.size) { for (i in 1..localVariables.size) {
val b = localVariables.size - i val b = localVariables.size - i
val scope = localVariables[b] val scope = localVariables[b]
val found = scope[name.id] val found = scope[identifier]
if (found != null) { if (found != null) {
return found.symbol return found.symbol
} }
@ -99,7 +101,7 @@ class IrCodeEmitter(
private fun lookupFunction(name: Symbol): Pair<ScopeSymbol, IrSymbol>? { private fun lookupFunction(name: Symbol): Pair<ScopeSymbol, IrSymbol>? {
val scoped = scope.resolve(name) ?: return null val scoped = scope.resolve(name) ?: return null
return scoped to irSymbolWorld.create(scoped, scopeSymbolToTag(scoped)) return scoped to irSymbolWorld.create(value = scoped, tag = scopeSymbolToTag(scoped), name = scoped.symbol.id)
} }
override fun visitBlock(node: Block): IrCodeBlock { override fun visitBlock(node: Block): IrCodeBlock {
@ -128,7 +130,33 @@ class IrCodeEmitter(
IrDoubleConstant(node.value) IrDoubleConstant(node.value)
override fun visitForIn(node: ForIn): IrCodeElement { override fun visitForIn(node: ForIn): IrCodeElement {
return IrNoneConstant val listLocal = createLocalVariable()
val indexLocal = createLocalVariable()
val sizeLocal = createLocalVariable()
val loopSymbol = irSymbolAssignment.next(IrSymbolTag.Loop)
val items = mutableListOf<IrCodeElement>(
IrStore(listLocal, visit(node.expression)),
IrStore(indexLocal, IrIntegerConstant(0)),
IrStore(sizeLocal, IrListSize(IrLoad(listLocal)))
)
enterLocalScope()
val loopValueLocal = createLocalVariable(node.item.symbol)
val subCodeBlock = visitBlock(node.block)
val innerCodeBlock = IrCodeBlock(listOf(
IrStore(loopValueLocal, IrIndex(IrLoad(listLocal), IrLoad(indexLocal))),
IrStore(indexLocal, IrInfix(IrInfixOp.Add, IrLoad(indexLocal), IrIntegerConstant(1))),
subCodeBlock
))
exitLocalScope()
val loop = IrLoop(
symbol = loopSymbol,
condition = IrInfix(IrInfixOp.Lesser, IrLoad(indexLocal), IrLoad(sizeLocal)),
inner = innerCodeBlock
)
items.add(loop)
return IrCodeBlock(items)
} }
override fun visitFunctionCall(node: FunctionCall): IrCodeElement { override fun visitFunctionCall(node: FunctionCall): IrCodeElement {
@ -169,19 +197,20 @@ class IrCodeEmitter(
variableArguments = mutableListOf() variableArguments = mutableListOf()
} }
return IrCall(symbol, arguments, variableArguments) return IrCall(target = symbol, arguments = arguments, variableArguments = variableArguments)
} }
override fun visitIf(node: If): IrCodeElement = override fun visitIf(node: If): IrCodeElement =
IrConditional( IrConditional(
node.condition.visit(this), conditional = node.condition.visit(this),
node.thenBlock.visit(this), ifTrue = node.thenBlock.visit(this),
node.elseBlock?.visit(this) ?: IrNoneConstant ifFalse = node.elseBlock?.visit(this) ?: IrNoneConstant
) )
override fun visitIndexedBy(node: IndexedBy): IrCodeElement { override fun visitIndexedBy(node: IndexedBy): IrCodeElement = IrIndex(
TODO("Not yet implemented") data = visit(node.expression),
} index = visit(node.index)
)
override fun visitInfixOperation(node: InfixOperation): IrCodeElement { override fun visitInfixOperation(node: InfixOperation): IrCodeElement {
val op = when (node.op) { val op = when (node.op) {

View File

@ -74,10 +74,10 @@ class IrStubOpEmitter(val irDefinition: IrDefinition, val code: CodeBuilder) : I
val endRel = MutableRel(0u) val endRel = MutableRel(0u)
visit(ir.conditional) visit(ir.conditional)
code.patch(Opcode.JumpIf, listOf(0u), 0, symbol, thenRel) code.patch(Opcode.JumpIf, listOf(0u), 0, symbol, thenRel)
visit(ir.ifTrue) visit(ir.ifFalse)
code.patch(Opcode.Jump, listOf(0u), 0, symbol, endRel) code.patch(Opcode.Jump, listOf(0u), 0, symbol, endRel)
thenRel.rel = code.nextOpInst() thenRel.rel = code.nextOpInst()
visit(ir.ifFalse) visit(ir.ifTrue)
endRel.rel = code.nextOpInst() endRel.rel = code.nextOpInst()
} }
@ -90,11 +90,16 @@ class IrStubOpEmitter(val irDefinition: IrDefinition, val code: CodeBuilder) : I
} }
override fun visitIrLongConstant(ir: IrLongConstant) { override fun visitIrLongConstant(ir: IrLongConstant) {
code.emit(Opcode.Integer, listOf(ir.value.toUInt())) val value1 = ir.value.toUInt()
val value2 = (ir.value shr 32).toUInt()
code.emit(Opcode.Long, listOf(value1, value2))
} }
override fun visitIrDoubleConstant(ir: IrDoubleConstant) { override fun visitIrDoubleConstant(ir: IrDoubleConstant) {
code.emit(Opcode.Integer, listOf(ir.value.toUInt())) val value = ir.value.toRawBits()
val value1 = value.toUInt()
val value2 = (value shr 32).toUInt()
code.emit(Opcode.Double, listOf(value1, value2))
} }
override fun visitIrStringConstant(ir: IrStringConstant) { override fun visitIrStringConstant(ir: IrStringConstant) {
@ -219,4 +224,15 @@ class IrStubOpEmitter(val irDefinition: IrDefinition, val code: CodeBuilder) : I
) )
code.emit(Opcode.Native, listOf(formConstant, ir.definitions.size.toUInt(), functionArgumentCount.toUInt())) code.emit(Opcode.Native, listOf(formConstant, ir.definitions.size.toUInt(), functionArgumentCount.toUInt()))
} }
override fun visitIrIndex(ir: IrIndex) {
visit(ir.index)
visit(ir.data)
code.emit(Opcode.Index)
}
override fun visitIrListSize(ir: IrListSize) {
visit(ir.list)
code.emit(Opcode.ListSize)
}
} }

View File

@ -7,8 +7,8 @@ import gay.pizza.pork.bir.IrSymbolTag
class IrSymbolWorld<T>(val irSymbolAssignment: IrSymbolAssignment) { class IrSymbolWorld<T>(val irSymbolAssignment: IrSymbolAssignment) {
private val symbols = mutableMapOf<T, IrSymbol>() private val symbols = mutableMapOf<T, IrSymbol>()
fun create(value: T, tag: IrSymbolTag): IrSymbol = symbols.getOrPut(value) { fun create(value: T, tag: IrSymbolTag, name: String? = null): IrSymbol = symbols.getOrPut(value) {
irSymbolAssignment.next(tag) irSymbolAssignment.next(tag, name)
} }
fun resolve(value: T): IrSymbol? = symbols[value] fun resolve(value: T): IrSymbol? = symbols[value]

View File

@ -3,4 +3,4 @@ package gay.pizza.pork.compiler
import gay.pizza.pork.ast.gen.Symbol import gay.pizza.pork.ast.gen.Symbol
import gay.pizza.pork.bir.IrSymbol import gay.pizza.pork.bir.IrSymbol
data class LocalVariable(val symbol: IrSymbol, val name: Symbol) data class LocalVariable(val symbol: IrSymbol, val name: Symbol? = null)

View File

@ -1,9 +1,9 @@
/* fibonacci sequence */ /* fibonacci sequence */
func fib(n) { func fib(n) {
return if n < 2 { if n < 2 {
n return n
} else { } else {
fib(n - 1) + fib(n - 2) return fib(n - 1) + fib(n - 2)
} }
} }

View File

@ -1,6 +1,5 @@
let items = ["Hello", "Goodbye"]
export func main() { export func main() {
let items = ["Hello", "Goodbye"]
for item in items { for item in items {
println(item) println(item)
} }

6
examples/list.pork Normal file
View File

@ -0,0 +1,6 @@
export func main() {
let items = listInitWith(30, 0)
for item in items {
println(item)
}
}

View File

@ -14,5 +14,5 @@ class NativeRegistry {
} }
fun of(form: String): NativeProvider = fun of(form: String): NativeProvider =
providers[form] ?: throw RuntimeException("Unknown native form: ${form}") providers[form] ?: throw RuntimeException("Unknown native form: $form")
} }

View File

@ -4,21 +4,40 @@ import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.arguments.argument import com.github.ajalt.clikt.parameters.arguments.argument
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.bir.IrWorld
import gay.pizza.pork.bytecode.CompiledWorld
import gay.pizza.pork.compiler.Compiler import gay.pizza.pork.compiler.Compiler
import gay.pizza.pork.minimal.FileTool import gay.pizza.pork.minimal.FileTool
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
@OptIn(ExperimentalSerializationApi::class)
class CompileCommand : CliktCommand(help = "Compile Pork to Bytecode", name = "compile") { class CompileCommand : CliktCommand(help = "Compile Pork to Bytecode", name = "compile") {
val path by argument("file") val path by argument("file")
override fun run() { override fun run() {
val tool = FileTool(PlatformFsProvider.resolve(path)) val tool = FileTool(PlatformFsProvider.resolve(path))
val world = tool.buildWorld() val world = tool.buildWorld()
val compiler = Compiler() val compiler = Compiler(world)
val slab = world.load(tool.rootImportLocator) val slab = world.load(tool.rootImportLocator)
val compiledSlab = compiler.compilableSlabs.of(slab) val compiledSlab = compiler.compilableSlabs.of(slab)
val compiledMain = compiledSlab.resolve(Symbol("main")) val compiledMain = compiledSlab.resolve(Symbol("main"))
?: throw RuntimeException("'main' function not found.") ?: throw RuntimeException("'main' function not found.")
val compiledWorld = compiler.compile(compiledMain) val compiledWorld = compiler.compile(compiledMain)
val irWorld = compiler.compileIrWorld()
printCompiledIr(irWorld)
printCompiledWorld(compiledWorld)
}
private fun printCompiledIr(irWorld: IrWorld) {
val json = Json {
prettyPrint = true
prettyPrintIndent = " "
}
println(json.encodeToString(IrWorld.serializer(), irWorld))
}
private fun printCompiledWorld(compiledWorld: CompiledWorld) {
for (symbol in compiledWorld.symbolTable.symbols) { for (symbol in compiledWorld.symbolTable.symbols) {
val code = compiledWorld.code.subList(symbol.offset.toInt(), (symbol.offset + symbol.size).toInt()) val code = compiledWorld.code.subList(symbol.offset.toInt(), (symbol.offset + symbol.size).toInt())
println(symbol.commonSymbolIdentity) println(symbol.commonSymbolIdentity)

View File

@ -47,11 +47,14 @@ class InternalMachine(val world: CompiledWorld, val nativeRegistry: NativeRegist
} }
} }
fun loadLocal(id: UInt) { fun localAt(id: UInt): Any {
val localSet = locals.last() val localSet = locals.last()
val local = localSet[id] return localSet[id] ?:
?: throw VirtualMachineException("Attempted to load local $id but it was not stored.") throw VirtualMachineException("Attempted to load local $id but it was not stored.")
push(local) }
fun loadLocal(id: UInt) {
push(localAt(id))
} }
fun storeLocal(id: UInt) { fun storeLocal(id: UInt) {
@ -108,4 +111,11 @@ class InternalMachine(val world: CompiledWorld, val nativeRegistry: NativeRegist
exitFlag = false exitFlag = false
autoNextInst = true autoNextInst = true
} }
fun frame(at: UInt = inst): StackFrame? {
val (symbolInfo, rel) = world.symbolTable.lookup(at) ?: (null to null)
return if (symbolInfo != null && rel != null) {
StackFrame(symbolInfo, rel)
} else null
}
} }

View File

@ -0,0 +1,19 @@
package gay.pizza.pork.vm
import gay.pizza.pork.bytecode.CompiledWorld
import gay.pizza.pork.bytecode.Op
import gay.pizza.pork.bytecode.SymbolInfo
data class StackFrame(val symbolInfo: SymbolInfo, val rel: UInt) {
val worldCodeOffset: UInt
get() = symbolInfo.offset + rel
fun opInWorld(world: CompiledWorld): Op =
world.code[worldCodeOffset.toInt()]
fun describeInWorld(world: CompiledWorld): String =
"StackFrame(${worldCodeOffset}, ${symbolInfo.slab} ${symbolInfo.symbol} + ${rel}, ${opInWorld(world)})"
override fun toString(): String =
"StackFrame(${worldCodeOffset}, ${symbolInfo.slab} ${symbolInfo.symbol} + ${rel})"
}

View File

@ -10,7 +10,7 @@ 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, nativeRegistry: NativeRegistry): ExecutionContext {
val compiler = Compiler() 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) ?:

View File

@ -7,8 +7,8 @@ import gay.pizza.pork.vm.OpHandler
object AddOpHandler : OpHandler(Opcode.Add) { object AddOpHandler : OpHandler(Opcode.Add) {
override fun handle(machine: InternalMachine, op: Op) { override fun handle(machine: InternalMachine, op: Op) {
val left = machine.pop<Int>()
val right = machine.pop<Int>() val right = machine.pop<Int>()
val left = machine.pop<Int>()
machine.push(left + right) machine.push(left + right)
} }
} }

View File

@ -2,6 +2,7 @@ package gay.pizza.pork.vm.ops
import gay.pizza.pork.bytecode.Op import gay.pizza.pork.bytecode.Op
import gay.pizza.pork.bytecode.Opcode import gay.pizza.pork.bytecode.Opcode
import gay.pizza.pork.execution.None
import gay.pizza.pork.vm.InternalMachine import gay.pizza.pork.vm.InternalMachine
import gay.pizza.pork.vm.OpHandler import gay.pizza.pork.vm.OpHandler
@ -9,9 +10,10 @@ object NativeOpHandler : OpHandler(Opcode.Native) {
override fun handle(machine: InternalMachine, op: Op) { override fun handle(machine: InternalMachine, op: Op) {
val argumentCount = op.args[2] val argumentCount = op.args[2]
val arguments = mutableListOf<Any>() val arguments = mutableListOf<Any>()
for (i in 0u until argumentCount) { var x = argumentCount
machine.loadLocal(i) while (x > 0u) {
arguments.add(machine.popAnyValue()) x--
arguments.add(machine.localAt(x))
} }
val formConstant = machine.world.constantPool.read(op.args[0]) val formConstant = machine.world.constantPool.read(op.args[0])
val form = formConstant.readAsString() val form = formConstant.readAsString()
@ -22,6 +24,7 @@ object NativeOpHandler : OpHandler(Opcode.Native) {
defs.add(machine.pop()) defs.add(machine.pop())
} }
val function = provider.provideNativeFunction(defs) val function = provider.provideNativeFunction(defs)
function.invoke(arguments) val result = function.invoke(arguments)
machine.push(if (result == Unit) None else result)
} }
} }

View File

@ -7,8 +7,8 @@ import gay.pizza.pork.vm.OpHandler
object SubtractOpHandler : OpHandler(Opcode.Subtract) { object SubtractOpHandler : OpHandler(Opcode.Subtract) {
override fun handle(machine: InternalMachine, op: Op) { override fun handle(machine: InternalMachine, op: Op) {
val left = machine.pop<Int>()
val right = machine.pop<Int>() val right = machine.pop<Int>()
val left = machine.pop<Int>()
machine.push(left - right) machine.push(left - right)
} }
} }