Compare commits
2 Commits
230cb0a067
...
30b6ef235b
Author | SHA1 | Date | |
---|---|---|---|
30b6ef235b | |||
9771b40c98 |
@ -7,10 +7,15 @@ import net.sergeych.lyng.pacman.ImportProvider
|
||||
*/
|
||||
class Compiler(
|
||||
val cc: CompilerContext,
|
||||
val importManager: ImportProvider = Script.defaultImportManager,
|
||||
val importManager: ImportProvider,
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
settings: Settings = Settings()
|
||||
) {
|
||||
|
||||
init {
|
||||
println("Compiler initialized: $importManager")
|
||||
}
|
||||
|
||||
var packageName: String? = null
|
||||
|
||||
class Settings
|
||||
@ -1618,8 +1623,8 @@ class Compiler(
|
||||
|
||||
companion object {
|
||||
|
||||
suspend fun compile(source: Source, importProvider: ImportProvider = Script.defaultImportManager): Script {
|
||||
return Compiler(CompilerContext(parseLyng(source)),importProvider).parseScript()
|
||||
suspend fun compile(source: Source,importManager: ImportProvider): Script {
|
||||
return Compiler(CompilerContext(parseLyng(source)),importManager).parseScript()
|
||||
}
|
||||
|
||||
private var lastPriority = 0
|
||||
@ -1742,7 +1747,7 @@ class Compiler(
|
||||
allOps.filter { it.priority == l }.associateBy { it.tokenType }
|
||||
}
|
||||
|
||||
suspend fun compile(code: String): Script = compile(Source("<eval>", code))
|
||||
suspend fun compile(code: String): Script = compile(Source("<eval>", code), Script.defaultImportManager)
|
||||
|
||||
/**
|
||||
* The keywords that stop processing of expression term
|
||||
|
@ -7,7 +7,7 @@ import net.sergeych.lyng.pacman.ImportProvider
|
||||
* used in [Compiler];
|
||||
*/
|
||||
class ModuleScope(
|
||||
val importProvider: ImportProvider,
|
||||
var importProvider: ImportProvider,
|
||||
pos: Pos = Pos.builtIn,
|
||||
override val packageName: String
|
||||
) : Scope(importProvider.rootScope, Arguments.EMPTY, pos) {
|
||||
@ -29,11 +29,19 @@ class ModuleScope(
|
||||
?.also { symbolsToImport!!.remove(it) }
|
||||
?: scope.raiseError("internal error: symbol $symbol not found though the module is cached")
|
||||
} ?: symbol
|
||||
if (newName in scope.objects)
|
||||
scope.raiseError("symbol $newName already exists, redefinition on import is not allowed")
|
||||
val existing = scope.objects[newName]
|
||||
if (existing != null ) {
|
||||
if (existing.importedFrom != record.importedFrom)
|
||||
scope.raiseError("symbol ${existing.importedFrom?.packageName}.$newName already exists, redefinition on import is not allowed")
|
||||
// already imported
|
||||
}
|
||||
else {
|
||||
// when importing records, we keep track of its package (not otherwise needed)
|
||||
if (record.importedFrom == null) record.importedFrom = this
|
||||
scope.objects[newName] = record
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!symbolsToImport.isNullOrEmpty())
|
||||
scope.raiseSymbolNotFound("symbols $packageName.{$symbolsToImport} are.were not found")
|
||||
}
|
||||
|
@ -15,8 +15,13 @@ import kotlin.contracts.ExperimentalContracts
|
||||
data class ObjRecord(
|
||||
var value: Obj,
|
||||
val isMutable: Boolean,
|
||||
val visibility: Visibility = Visibility.Public
|
||||
)
|
||||
val visibility: Visibility = Visibility.Public,
|
||||
var importedFrom: Scope? = null
|
||||
) {
|
||||
@Suppress("unused")
|
||||
fun qualifiedName(name: String): String =
|
||||
"${importedFrom?.packageName ?: "anonymous"}.$name"
|
||||
}
|
||||
|
||||
/**
|
||||
* When we need read-write access to an object in some abstract storage, we need Accessor,
|
||||
|
@ -1,15 +1,16 @@
|
||||
package net.sergeych.lyng
|
||||
|
||||
import net.sergeych.lyng.pacman.ImportManager
|
||||
import net.sergeych.lyng.pacman.ImportProvider
|
||||
|
||||
/**
|
||||
* Scope is where local variables and methods are stored. Scope is also a parent scope for other scopes.
|
||||
* Each block usually creates a scope. Accessing Lyng closures usually is done via a scope.
|
||||
*
|
||||
* To create default scope, use default `Scope()` constructor, it will create a scope with a parent
|
||||
* module scope with default [ImportManager], you can access with [importManager] as needed.
|
||||
* module scope with default [ImportManager], you can access with [currentImportProvider] as needed.
|
||||
*
|
||||
* If you want to create [ModuleScope] by hand, try [importManager] and [ImportManager.newModule],
|
||||
* If you want to create [ModuleScope] by hand, try [currentImportProvider] and [ImportManager.newModule],
|
||||
* or [ImportManager.newModuleAt].
|
||||
*
|
||||
* There are special types of scopes:
|
||||
@ -29,7 +30,7 @@ open class Scope(
|
||||
args: Arguments = Arguments.EMPTY,
|
||||
pos: Pos = Pos.builtIn,
|
||||
)
|
||||
: this(Script.defaultImportManager.newModuleAt(pos), args, pos)
|
||||
: this(Script.defaultImportManager.copy().newModuleAt(pos), args, pos)
|
||||
|
||||
fun raiseNotImplemented(what: String = "operation"): Nothing = raiseError("$what is not implemented")
|
||||
|
||||
@ -141,12 +142,12 @@ open class Scope(
|
||||
fun addConst(name: String, value: Obj) = addItem(name, false, value)
|
||||
|
||||
suspend fun eval(code: String): Obj =
|
||||
Compiler.compile(code.toSource()).execute(this)
|
||||
Compiler.compile(code.toSource(), currentImportProvider).execute(this)
|
||||
|
||||
suspend fun eval(source: Source): Obj =
|
||||
Compiler.compile(
|
||||
source,
|
||||
(this as? ModuleScope)?.importProvider ?: Script.defaultImportManager
|
||||
currentImportProvider
|
||||
).execute(this)
|
||||
|
||||
fun containsLocal(name: String): Boolean = name in objects
|
||||
@ -168,10 +169,19 @@ open class Scope(
|
||||
* @throws IllegalStateException if there is no such manager (if you create some specific scope with no manager,
|
||||
* then you knew what you did)
|
||||
*/
|
||||
val importManager: ImportManager by lazy {
|
||||
val currentImportProvider: ImportProvider by lazy {
|
||||
if (this is ModuleScope)
|
||||
(importProvider as? ImportManager)?.let { return@lazy it }
|
||||
parent?.importManager ?: throw IllegalStateException("this scope has no manager in the chain")
|
||||
importProvider.getActualProvider()
|
||||
else
|
||||
parent?.currentImportProvider ?: throw IllegalStateException("this scope has no manager in the chain")
|
||||
}
|
||||
|
||||
val importManager by lazy { (currentImportProvider as? ImportManager)
|
||||
?: throw IllegalStateException("this scope has no manager in the chain (provided $currentImportProvider") }
|
||||
|
||||
companion object {
|
||||
|
||||
fun new(): Scope =
|
||||
Script.defaultImportManager.copy().newModuleAt(Pos.builtIn)
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,8 @@ class ImportManager(
|
||||
securityManager: SecurityManager = SecurityManager.allowAll
|
||||
) : ImportProvider(rootScope, securityManager) {
|
||||
|
||||
val packageNames: List<String> get() = imports.keys.toList()
|
||||
|
||||
private inner class Entry(
|
||||
val packageName: String,
|
||||
val builder: suspend (ModuleScope) -> Unit,
|
||||
@ -43,6 +45,10 @@ class ImportManager(
|
||||
override suspend fun createModuleScope(pos: Pos, packageName: String): ModuleScope {
|
||||
return doImport(packageName, pos)
|
||||
}
|
||||
|
||||
override fun getActualProvider(): ImportProvider {
|
||||
return this@ImportManager
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -95,6 +101,7 @@ class ImportManager(
|
||||
*/
|
||||
private suspend fun doImport(packageName: String, pos: Pos): ModuleScope {
|
||||
val entry = imports[packageName] ?: throw ImportException(pos, "package not found: $packageName")
|
||||
println("import enrty found: $packageName")
|
||||
return entry.getScope(pos)
|
||||
}
|
||||
|
||||
@ -125,4 +132,11 @@ class ImportManager(
|
||||
}
|
||||
}
|
||||
|
||||
fun copy(): ImportManager =
|
||||
op.withLock {
|
||||
ImportManager(rootScope, securityManager).apply {
|
||||
imports.putAll(this@ImportManager.imports)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -14,6 +14,9 @@ abstract class ImportProvider(
|
||||
val rootScope: Scope,
|
||||
val securityManager: SecurityManager = SecurityManager.allowAll
|
||||
) {
|
||||
|
||||
open fun getActualProvider() = this
|
||||
|
||||
/**
|
||||
* Find an import and create a scope for it. This method must implement caching so repeated
|
||||
* imports are not repeatedly loaded and parsed and should be cheap.
|
||||
|
@ -2443,7 +2443,7 @@ class ScriptTest {
|
||||
|
||||
@Test
|
||||
fun testDefaultImportManager() = runTest {
|
||||
val scope = Scope()
|
||||
val scope = Scope.new()
|
||||
assertFails {
|
||||
scope.eval("""
|
||||
import foo
|
||||
@ -2574,4 +2574,42 @@ class ScriptTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDoubleImports() = runTest {
|
||||
val s = Scope.new()
|
||||
println(Script.defaultImportManager.packageNames)
|
||||
println(s.importManager.packageNames)
|
||||
|
||||
s.importManager.addTextPackages("""
|
||||
package foo
|
||||
|
||||
import lyng.time
|
||||
|
||||
fun foo() {
|
||||
println("foo: %s"(Instant()))
|
||||
}
|
||||
""".trimIndent())
|
||||
s.importManager.addTextPackages("""
|
||||
package bar
|
||||
|
||||
import lyng.time
|
||||
|
||||
fun bar() {
|
||||
println("bar: %s"(Instant()))
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
println(s.importManager.packageNames)
|
||||
|
||||
s.eval("""
|
||||
import foo
|
||||
import bar
|
||||
|
||||
foo()
|
||||
bar()
|
||||
|
||||
""".trimIndent())
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user