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