mirror of
https://github.com/GayPizzaSpecifications/pork.git
synced 2025-08-03 13:11:32 +00:00
vm: very basic virtual machine
This commit is contained in:
@ -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
|
||||
}
|
||||
|
@ -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] ?:
|
||||
|
@ -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 {
|
||||
|
12
frontend/src/main/kotlin/gay/pizza/pork/frontend/Slab.kt
Normal file
12
frontend/src/main/kotlin/gay/pizza/pork/frontend/Slab.kt
Normal 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) }
|
||||
}
|
@ -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)
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package gay.pizza.pork.frontend
|
||||
|
||||
data class StableSourceKey(val form: String, val path: String) {
|
||||
fun asSourceLocation(): SourceLocation = SourceLocation(form, path)
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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) }
|
||||
}
|
||||
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user