vm: very basic virtual machine

This commit is contained in:
2023-11-14 23:44:10 -08:00
parent 8c48c93663
commit 041848c14e
92 changed files with 1652 additions and 243 deletions

View File

@ -4,5 +4,5 @@ import gay.pizza.pork.tokenizer.CharSource
interface ContentSource {
fun loadAsCharSource(path: String): CharSource
fun stableContentIdentity(path: String): String
fun stableContentPath(path: String): String
}

View File

@ -1,7 +1,7 @@
package gay.pizza.pork.frontend
class DynamicImportSource : ImportSource {
private val providers = mutableMapOf<String,ContentSource>()
private val providers = mutableMapOf<String, ContentSource>()
override fun provideContentSource(form: String): ContentSource {
return providers[form] ?:

View File

@ -10,7 +10,7 @@ class FsContentSource(val root: FsPath) : ContentSource {
override fun loadAsCharSource(path: String): CharSource =
StringCharSource(asFsPath(path).readString())
override fun stableContentIdentity(path: String): String =
override fun stableContentPath(path: String): String =
asFsPath(path).fullPathString
private fun asFsPath(path: String): FsPath {

View File

@ -0,0 +1,12 @@
package gay.pizza.pork.frontend
import gay.pizza.pork.ast.gen.CompilationUnit
import gay.pizza.pork.frontend.scope.SlabScope
class Slab(val world: World, val location: SourceLocation, val compilationUnit: CompilationUnit) {
val importedSlabs: List<Slab> by lazy {
world.resolveAllImports(this)
}
val scope: SlabScope by lazy { world.scope.index(this) }
}

View File

@ -0,0 +1,10 @@
package gay.pizza.pork.frontend
import gay.pizza.pork.tokenizer.SourceIndex
data class SourceLocation(val form: String, val filePath: String, val index: SourceIndex? = null) {
val commonFriendlyName: String by lazy { "$form $filePath" }
fun withSourceIndex(index: SourceIndex): SourceLocation =
SourceLocation(form, filePath, index)
}

View File

@ -0,0 +1,5 @@
package gay.pizza.pork.frontend
data class StableSourceKey(val form: String, val path: String) {
fun asSourceLocation(): SourceLocation = SourceLocation(form, path)
}

View File

@ -1,22 +1,25 @@
package gay.pizza.pork.frontend
import gay.pizza.pork.ast.gen.CompilationUnit
import gay.pizza.pork.ast.gen.ImportDeclaration
import gay.pizza.pork.frontend.scope.WorldScope
import gay.pizza.pork.parser.DiscardNodeAttribution
import gay.pizza.pork.parser.Parser
import gay.pizza.pork.tokenizer.Tokenizer
class World(val importSource: ImportSource) {
private val internalUnits = mutableMapOf<String, CompilationUnit>()
private val importedUnits = mutableMapOf<CompilationUnit, Set<CompilationUnit>>()
private val preludeImportLocator = ImportLocator("std", "lang/prelude.pork")
val units: List<CompilationUnit>
get() = internalUnits.values.toList()
private val internalSlabs = mutableMapOf<StableSourceKey, Slab>()
private fun loadOneUnit(importLocator: ImportLocator): CompilationUnit {
val slabs: List<Slab>
get() = internalSlabs.values.toList()
val scope: WorldScope by lazy { WorldScope(this) }
private fun loadOneSlab(importLocator: ImportLocator): Slab {
val contentSource = pickContentSource(importLocator.form)
val stableKey = stableIdentity(importLocator, contentSource = contentSource)
val cached = internalUnits[stableKey]
val stableKey = stableSourceKey(importLocator, contentSource = contentSource)
val cached = internalSlabs[stableKey]
if (cached != null) {
return cached
}
@ -24,40 +27,39 @@ class World(val importSource: ImportSource) {
val tokenizer = Tokenizer(charSource)
val parser = Parser(tokenizer, DiscardNodeAttribution)
val unit = parser.parseCompilationUnit()
internalUnits[stableKey] = unit
return unit
val slab = Slab(world = this, location = stableKey.asSourceLocation(), compilationUnit = unit)
internalSlabs[stableKey] = slab
return slab
}
private fun resolveAllImports(unit: CompilationUnit): Set<CompilationUnit> {
val units = mutableSetOf<CompilationUnit>()
for (declaration in unit.declarations.filterIsInstance<ImportDeclaration>()) {
internal fun resolveAllImports(slab: Slab): List<Slab> {
val slabs = mutableListOf<Slab>()
if (slab.location.form != preludeImportLocator.form &&
slab.location.filePath != preludeImportLocator.path) {
slabs.add(loadOneSlab(preludeImportLocator))
}
for (declaration in slab.compilationUnit.declarations.filterIsInstance<ImportDeclaration>()) {
val importPath = declaration.path.components.joinToString("/") { it.id } + ".pork"
val importLocator = ImportLocator(declaration.form.id, importPath)
val importedUnit = loadOneUnit(importLocator)
units.add(importedUnit)
val importedModule = loadOneSlab(importLocator)
slabs.add(importedModule)
}
importedUnits[unit] = units
return units
return slabs
}
fun load(importLocator: ImportLocator): CompilationUnit {
val unit = loadOneUnit(importLocator)
resolveAllImports(unit)
return unit
fun load(importLocator: ImportLocator): Slab {
return loadOneSlab(importLocator)
}
fun importedBy(unit: CompilationUnit): Set<CompilationUnit> =
importedUnits[unit] ?: emptySet()
private fun pickContentSource(form: String): ContentSource =
importSource.provideContentSource(form)
fun stableIdentity(
fun stableSourceKey(
importLocator: ImportLocator,
contentSource: ContentSource = pickContentSource(importLocator.form)
): String {
): StableSourceKey {
val formKey = importLocator.form
val stableIdentity = contentSource.stableContentIdentity(importLocator.path)
return "[${formKey}][${stableIdentity}]"
val stableContentPath = contentSource.stableContentPath(importLocator.path)
return StableSourceKey(formKey, stableContentPath)
}
}

View File

@ -1,36 +0,0 @@
package gay.pizza.pork.frontend.scope
import gay.pizza.pork.ast.gen.CompilationUnit
import gay.pizza.pork.ast.gen.Symbol
class CompilationUnitScope(val worldScope: WorldScope, val unit: CompilationUnit) {
val externalSymbols = mutableSetOf<ScopeSymbol>()
val internalSymbols = mutableSetOf<ScopeSymbol>()
fun index() {
for (definition in unit.definitions) {
val scopeSymbol = ScopeSymbol(unit, definition)
if (definition.modifiers.export) {
externalSymbols.add(scopeSymbol)
}
internalSymbols.add(scopeSymbol)
}
}
fun findInternallyVisibleSymbols(): Set<VisibleScopeSymbol> {
val allSymbols = mutableMapOf<Symbol, VisibleScopeSymbol>()
val imports = worldScope.world.importedBy(unit)
for (import in imports) {
val scope = worldScope.index(import)
for (importedSymbol in scope.externalSymbols) {
allSymbols[importedSymbol.symbol] = VisibleScopeSymbol(unit, importedSymbol)
}
}
for (internalSymbol in internalSymbols) {
allSymbols[internalSymbol.symbol] = VisibleScopeSymbol(unit, internalSymbol)
}
return allSymbols.values.toSet()
}
}

View File

@ -0,0 +1,21 @@
package gay.pizza.pork.frontend.scope
import gay.pizza.pork.ast.gen.Definition
import gay.pizza.pork.ast.gen.Symbol
import gay.pizza.pork.ast.gen.visit
class DefinitionScope(val slabScope: SlabScope, val definition: Definition) {
val usedSymbols: List<ScopeSymbol> by lazy {
val symbols = mutableListOf<ScopeSymbol>()
val analyzer = ExternalSymbolUsageAnalyzer()
analyzer.visit(definition)
for (symbol in analyzer.usedSymbols) {
val resolved = slabScope.resolve(symbol)
?: throw RuntimeException("Unable to resolve symbol: ${symbol.id}")
symbols.add(resolved)
}
symbols
}
fun resolve(symbol: Symbol): ScopeSymbol? = slabScope.resolve(symbol)
}

View File

@ -0,0 +1,135 @@
package gay.pizza.pork.frontend.scope
import gay.pizza.pork.ast.FunctionLevelVisitor
import gay.pizza.pork.ast.gen.*
class ExternalSymbolUsageAnalyzer : FunctionLevelVisitor<Unit>() {
private val symbols = mutableSetOf<Symbol>()
private val internalSymbols = mutableListOf<MutableSet<Symbol>>()
val usedSymbols: Set<Symbol>
get() = symbols
override fun visitFunctionDefinition(node: FunctionDefinition) {
internalSymbols.add(node.arguments.map { it.symbol }.toMutableSet())
node.block?.visit(this)
internalSymbols.removeLast()
}
override fun visitLetDefinition(node: LetDefinition) {
node.value.visit(this)
}
override fun visitBlock(node: Block) {
internalSymbols.add(mutableSetOf())
node.visitChildren(this)
internalSymbols.removeLast()
}
override fun visitBooleanLiteral(node: BooleanLiteral) {
node.visitChildren(this)
}
override fun visitBreak(node: Break) {
node.visitChildren(this)
}
override fun visitContinue(node: Continue) {
node.visitChildren(this)
}
override fun visitDoubleLiteral(node: DoubleLiteral) {
node.visitChildren(this)
}
override fun visitForIn(node: ForIn) {
node.expression.visit(this)
internalSymbols.add(mutableSetOf(node.item.symbol))
node.block.visit(this)
internalSymbols.removeLast()
}
override fun visitFunctionCall(node: FunctionCall) {
checkAndContribute(node.symbol)
for (argument in node.arguments) {
visit(argument)
}
}
override fun visitIf(node: If) {
node.condition.visit(this)
node.thenBlock.visit(this)
node.elseBlock?.visit(this)
}
override fun visitIndexedBy(node: IndexedBy) {
node.visitChildren(this)
}
override fun visitInfixOperation(node: InfixOperation) {
node.visitChildren(this)
}
override fun visitIntegerLiteral(node: IntegerLiteral) {
node.visitChildren(this)
}
override fun visitLetAssignment(node: LetAssignment) {
internalSymbols.last().add(node.symbol)
node.value.visit(this)
}
override fun visitListLiteral(node: ListLiteral) {
node.visitChildren(this)
}
override fun visitLongLiteral(node: LongLiteral) {
node.visitChildren(this)
}
override fun visitNoneLiteral(node: NoneLiteral) {
node.visitChildren(this)
}
override fun visitParentheses(node: Parentheses) {
node.visitChildren(this)
}
override fun visitPrefixOperation(node: PrefixOperation) {
node.visitChildren(this)
}
override fun visitSetAssignment(node: SetAssignment) {
node.visitChildren(this)
}
override fun visitStringLiteral(node: StringLiteral) {
node.visitChildren(this)
}
override fun visitSuffixOperation(node: SuffixOperation) {
node.visitChildren(this)
}
override fun visitSymbolReference(node: SymbolReference) {
checkAndContribute(node.symbol)
}
override fun visitVarAssignment(node: VarAssignment) {
internalSymbols.last().add(node.symbol)
node.value.visit(this)
}
override fun visitWhile(node: While) {
node.condition.visit(this)
internalSymbols.add(mutableSetOf())
node.block.visit(this)
internalSymbols.removeLast()
}
private fun checkAndContribute(symbol: Symbol) {
if (internalSymbols.none { it.contains(symbol) }) {
symbols.add(symbol)
}
}
}

View File

@ -1,11 +1,9 @@
package gay.pizza.pork.frontend.scope
import gay.pizza.pork.ast.gen.Definition
import gay.pizza.pork.ast.gen.Node
import gay.pizza.pork.ast.gen.Symbol
class ScopeSymbol(
val compilationUnit: Node,
val definition: Definition
) {
val symbol = definition.symbol
class ScopeSymbol(val slabScope: SlabScope, val definition: Definition) {
val symbol: Symbol = definition.symbol
val scope: DefinitionScope by lazy { DefinitionScope(slabScope, definition) }
}

View File

@ -0,0 +1,48 @@
package gay.pizza.pork.frontend.scope
import gay.pizza.pork.ast.gen.Symbol
import gay.pizza.pork.frontend.Slab
class SlabScope(val worldScope: WorldScope, val slab: Slab) {
private val externalSymbolsList = mutableSetOf<ScopeSymbol>()
private val internalSymbolsList = mutableSetOf<ScopeSymbol>()
val externalSymbols: Set<ScopeSymbol>
get() = externalSymbolsList
val internalSymbols: Set<ScopeSymbol>
get() = internalSymbolsList
val internallyVisibleSymbols: List<VisibleScopeSymbol> by lazy { findInternallyVisibleSymbols() }
fun index() {
for (definition in slab.compilationUnit.definitions) {
val scopeSymbol = ScopeSymbol(this, definition)
if (definition.modifiers.export) {
externalSymbolsList.add(scopeSymbol)
}
internalSymbolsList.add(scopeSymbol)
}
}
private fun findInternallyVisibleSymbols(): List<VisibleScopeSymbol> {
val allSymbols = mutableMapOf<Symbol, VisibleScopeSymbol>()
val imports = slab.importedSlabs
for (import in imports) {
val scope = worldScope.index(import)
for (importedSymbol in scope.externalSymbols) {
allSymbols[importedSymbol.symbol] = VisibleScopeSymbol(slab, importedSymbol)
}
}
for (internalSymbol in internalSymbols) {
allSymbols[internalSymbol.symbol] = VisibleScopeSymbol(slab, internalSymbol)
}
return allSymbols.values.toList()
}
fun resolve(symbol: Symbol): ScopeSymbol? = internallyVisibleSymbols.firstOrNull {
it.scopeSymbol.symbol == symbol
}?.scopeSymbol
}

View File

@ -1,8 +1,8 @@
package gay.pizza.pork.frontend.scope
import gay.pizza.pork.ast.gen.CompilationUnit
import gay.pizza.pork.frontend.Slab
class VisibleScopeSymbol(val visibleToUnit: CompilationUnit, val scopeSymbol: ScopeSymbol) {
class VisibleScopeSymbol(val visibleToSlab: Slab, val scopeSymbol: ScopeSymbol) {
val isInternalSymbol: Boolean
get() = visibleToUnit == scopeSymbol.compilationUnit
get() = visibleToSlab == scopeSymbol.slabScope.slab
}

View File

@ -1,24 +1,24 @@
package gay.pizza.pork.frontend.scope
import gay.pizza.pork.ast.gen.CompilationUnit
import gay.pizza.pork.frontend.World
import gay.pizza.pork.frontend.Slab
class WorldScope(val world: World) {
private val compilationUnitScopes = mutableMapOf<CompilationUnit, CompilationUnitScope>()
private val slabScopes = mutableMapOf<Slab, SlabScope>()
fun indexAll() {
for (unit in world.units) {
index(unit)
for (module in world.slabs) {
index(module)
}
}
fun index(unit: CompilationUnit): CompilationUnitScope =
scope(unit).apply {
fun index(slab: Slab): SlabScope =
scope(slab).apply {
index()
}
fun scope(unit: CompilationUnit): CompilationUnitScope =
compilationUnitScopes.computeIfAbsent(unit) {
CompilationUnitScope(this, unit)
fun scope(slab: Slab): SlabScope =
slabScopes.computeIfAbsent(slab) {
SlabScope(this, slab)
}
}