fixed double imports bug
added clean import scope Scope.new()
This commit is contained in:
parent
230cb0a067
commit
9771b40c98
@ -7,10 +7,15 @@ import net.sergeych.lyng.pacman.ImportProvider
|
|||||||
*/
|
*/
|
||||||
class Compiler(
|
class Compiler(
|
||||||
val cc: CompilerContext,
|
val cc: CompilerContext,
|
||||||
val importManager: ImportProvider = Script.defaultImportManager,
|
val importManager: ImportProvider,
|
||||||
@Suppress("UNUSED_PARAMETER")
|
@Suppress("UNUSED_PARAMETER")
|
||||||
settings: Settings = Settings()
|
settings: Settings = Settings()
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
println("Compiler initialized: $importManager")
|
||||||
|
}
|
||||||
|
|
||||||
var packageName: String? = null
|
var packageName: String? = null
|
||||||
|
|
||||||
class Settings
|
class Settings
|
||||||
@ -1618,8 +1623,8 @@ class Compiler(
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
suspend fun compile(source: Source, importProvider: ImportProvider = Script.defaultImportManager): Script {
|
suspend fun compile(source: Source,importManager: ImportProvider): Script {
|
||||||
return Compiler(CompilerContext(parseLyng(source)),importProvider).parseScript()
|
return Compiler(CompilerContext(parseLyng(source)),importManager).parseScript()
|
||||||
}
|
}
|
||||||
|
|
||||||
private var lastPriority = 0
|
private var lastPriority = 0
|
||||||
@ -1742,7 +1747,7 @@ class Compiler(
|
|||||||
allOps.filter { it.priority == l }.associateBy { it.tokenType }
|
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
|
* The keywords that stop processing of expression term
|
||||||
|
@ -7,7 +7,7 @@ import net.sergeych.lyng.pacman.ImportProvider
|
|||||||
* used in [Compiler];
|
* used in [Compiler];
|
||||||
*/
|
*/
|
||||||
class ModuleScope(
|
class ModuleScope(
|
||||||
val importProvider: ImportProvider,
|
var importProvider: ImportProvider,
|
||||||
pos: Pos = Pos.builtIn,
|
pos: Pos = Pos.builtIn,
|
||||||
override val packageName: String
|
override val packageName: String
|
||||||
) : Scope(importProvider.rootScope, Arguments.EMPTY, pos) {
|
) : Scope(importProvider.rootScope, Arguments.EMPTY, pos) {
|
||||||
@ -29,9 +29,17 @@ class ModuleScope(
|
|||||||
?.also { symbolsToImport!!.remove(it) }
|
?.also { symbolsToImport!!.remove(it) }
|
||||||
?: scope.raiseError("internal error: symbol $symbol not found though the module is cached")
|
?: scope.raiseError("internal error: symbol $symbol not found though the module is cached")
|
||||||
} ?: symbol
|
} ?: symbol
|
||||||
if (newName in scope.objects)
|
val existing = scope.objects[newName]
|
||||||
scope.raiseError("symbol $newName already exists, redefinition on import is not allowed")
|
if (existing != null ) {
|
||||||
scope.objects[newName] = record
|
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())
|
if (!symbolsToImport.isNullOrEmpty())
|
||||||
|
@ -15,8 +15,13 @@ import kotlin.contracts.ExperimentalContracts
|
|||||||
data class ObjRecord(
|
data class ObjRecord(
|
||||||
var value: Obj,
|
var value: Obj,
|
||||||
val isMutable: Boolean,
|
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,
|
* When we need read-write access to an object in some abstract storage, we need Accessor,
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
package net.sergeych.lyng
|
package net.sergeych.lyng
|
||||||
|
|
||||||
import net.sergeych.lyng.pacman.ImportManager
|
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.
|
* 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.
|
* 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
|
* 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].
|
* or [ImportManager.newModuleAt].
|
||||||
*
|
*
|
||||||
* There are special types of scopes:
|
* There are special types of scopes:
|
||||||
@ -141,12 +142,12 @@ open class Scope(
|
|||||||
fun addConst(name: String, value: Obj) = addItem(name, false, value)
|
fun addConst(name: String, value: Obj) = addItem(name, false, value)
|
||||||
|
|
||||||
suspend fun eval(code: String): Obj =
|
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 =
|
suspend fun eval(source: Source): Obj =
|
||||||
Compiler.compile(
|
Compiler.compile(
|
||||||
source,
|
source,
|
||||||
(this as? ModuleScope)?.importProvider ?: Script.defaultImportManager
|
currentImportProvider
|
||||||
).execute(this)
|
).execute(this)
|
||||||
|
|
||||||
fun containsLocal(name: String): Boolean = name in objects
|
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,
|
* @throws IllegalStateException if there is no such manager (if you create some specific scope with no manager,
|
||||||
* then you knew what you did)
|
* then you knew what you did)
|
||||||
*/
|
*/
|
||||||
val importManager: ImportManager by lazy {
|
val currentImportProvider: ImportProvider by lazy {
|
||||||
if( this is ModuleScope )
|
if (this is ModuleScope)
|
||||||
(importProvider as? ImportManager)?.let { return@lazy it }
|
importProvider.getActualProvider()
|
||||||
parent?.importManager ?: throw IllegalStateException("this scope has no manager in the chain")
|
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
|
securityManager: SecurityManager = SecurityManager.allowAll
|
||||||
) : ImportProvider(rootScope, securityManager) {
|
) : ImportProvider(rootScope, securityManager) {
|
||||||
|
|
||||||
|
val packageNames: List<String> get() = imports.keys.toList()
|
||||||
|
|
||||||
private inner class Entry(
|
private inner class Entry(
|
||||||
val packageName: String,
|
val packageName: String,
|
||||||
val builder: suspend (ModuleScope) -> Unit,
|
val builder: suspend (ModuleScope) -> Unit,
|
||||||
@ -43,6 +45,10 @@ class ImportManager(
|
|||||||
override suspend fun createModuleScope(pos: Pos, packageName: String): ModuleScope {
|
override suspend fun createModuleScope(pos: Pos, packageName: String): ModuleScope {
|
||||||
return doImport(packageName, pos)
|
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 {
|
private suspend fun doImport(packageName: String, pos: Pos): ModuleScope {
|
||||||
val entry = imports[packageName] ?: throw ImportException(pos, "package not found: $packageName")
|
val entry = imports[packageName] ?: throw ImportException(pos, "package not found: $packageName")
|
||||||
|
println("import enrty found: $packageName")
|
||||||
return entry.getScope(pos)
|
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 rootScope: Scope,
|
||||||
val securityManager: SecurityManager = SecurityManager.allowAll
|
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
|
* 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.
|
* imports are not repeatedly loaded and parsed and should be cheap.
|
||||||
|
@ -2443,7 +2443,7 @@ class ScriptTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testDefaultImportManager() = runTest {
|
fun testDefaultImportManager() = runTest {
|
||||||
val scope = Scope()
|
val scope = Scope.new()
|
||||||
assertFails {
|
assertFails {
|
||||||
scope.eval("""
|
scope.eval("""
|
||||||
import foo
|
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