fix #38 ImportManager integrated into Scope tree and all systems
This commit is contained in:
		
							parent
							
								
									ce4ed5c819
								
							
						
					
					
						commit
						26282d3e22
					
				@ -1,3 +1,3 @@
 | 
				
			|||||||
#!/bin/env jlyng
 | 
					#!/bin/env jlyng
 | 
				
			||||||
 | 
					
 | 
				
			||||||
println("Hello, world2! "+ARGV);
 | 
					println("Hello, world!");
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
 | 
				
			|||||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
 | 
					import org.jetbrains.kotlin.gradle.dsl.JvmTarget
 | 
				
			||||||
 | 
					
 | 
				
			||||||
group = "net.sergeych"
 | 
					group = "net.sergeych"
 | 
				
			||||||
version = "0.7.1-SNAPSHOT"
 | 
					version = "0.7.2-SNAPSHOT"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
buildscript {
 | 
					buildscript {
 | 
				
			||||||
    repositories {
 | 
					    repositories {
 | 
				
			||||||
 | 
				
			|||||||
@ -54,8 +54,8 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
 | 
				
			|||||||
                    i < callArgs.size -> callArgs[i]
 | 
					                    i < callArgs.size -> callArgs[i]
 | 
				
			||||||
                    a.defaultValue != null -> a.defaultValue.execute(scope)
 | 
					                    a.defaultValue != null -> a.defaultValue.execute(scope)
 | 
				
			||||||
                    else -> {
 | 
					                    else -> {
 | 
				
			||||||
                        println("callArgs: ${callArgs.joinToString()}")
 | 
					//                        println("callArgs: ${callArgs.joinToString()}")
 | 
				
			||||||
                        println("tailBlockMode: ${arguments.tailBlockMode}")
 | 
					//                        println("tailBlockMode: ${arguments.tailBlockMode}")
 | 
				
			||||||
                        scope.raiseIllegalArgument("too few arguments for the call")
 | 
					                        scope.raiseIllegalArgument("too few arguments for the call")
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
				
			|||||||
@ -7,7 +7,7 @@ import net.sergeych.lyng.pacman.ImportProvider
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
class Compiler(
 | 
					class Compiler(
 | 
				
			||||||
    val cc: CompilerContext,
 | 
					    val cc: CompilerContext,
 | 
				
			||||||
    val importProvider: ImportProvider = ImportProvider.emptyAllowAll,
 | 
					    val importManager: ImportProvider = Script.defaultImportManager,
 | 
				
			||||||
    @Suppress("UNUSED_PARAMETER")
 | 
					    @Suppress("UNUSED_PARAMETER")
 | 
				
			||||||
    settings: Settings = Settings()
 | 
					    settings: Settings = Settings()
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
@ -43,7 +43,7 @@ class Compiler(
 | 
				
			|||||||
                        cc.next()
 | 
					                        cc.next()
 | 
				
			||||||
                        val pos = cc.currentPos()
 | 
					                        val pos = cc.currentPos()
 | 
				
			||||||
                        val name = loadQualifiedName()
 | 
					                        val name = loadQualifiedName()
 | 
				
			||||||
                        val module = importProvider.prepareImport(pos, name, null)
 | 
					                        val module = importManager.prepareImport(pos, name, null)
 | 
				
			||||||
                        statements += statement {
 | 
					                        statements += statement {
 | 
				
			||||||
                            module.importInto(this, null)
 | 
					                            module.importInto(this, null)
 | 
				
			||||||
                            ObjVoid
 | 
					                            ObjVoid
 | 
				
			||||||
@ -1620,7 +1620,7 @@ class Compiler(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    companion object {
 | 
					    companion object {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        suspend fun compile(source: Source, importProvider: ImportProvider = ImportProvider.emptyAllowAll): Script {
 | 
					        suspend fun compile(source: Source, importProvider: ImportProvider = Script.defaultImportManager): Script {
 | 
				
			||||||
            return Compiler(CompilerContext(parseLyng(source)),importProvider).parseScript()
 | 
					            return Compiler(CompilerContext(parseLyng(source)),importProvider).parseScript()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -109,7 +109,7 @@ private class Parser(fromPos: Pos) {
 | 
				
			|||||||
            '/' -> when (currentChar) {
 | 
					            '/' -> when (currentChar) {
 | 
				
			||||||
                '/' -> {
 | 
					                '/' -> {
 | 
				
			||||||
                    pos.advance()
 | 
					                    pos.advance()
 | 
				
			||||||
                    Token(loadToEnd().trim(), from, Token.Type.SINLGE_LINE_COMMENT)
 | 
					                    Token(loadToEndOfLine().trim(), from, Token.Type.SINLGE_LINE_COMMENT)
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                '*' -> {
 | 
					                '*' -> {
 | 
				
			||||||
@ -445,7 +445,7 @@ private class Parser(fromPos: Pos) {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private fun loadToEnd(): String {
 | 
					    private fun loadToEndOfLine(): String {
 | 
				
			||||||
        val result = StringBuilder()
 | 
					        val result = StringBuilder()
 | 
				
			||||||
        val l = pos.line
 | 
					        val l = pos.line
 | 
				
			||||||
        do {
 | 
					        do {
 | 
				
			||||||
@ -479,4 +479,10 @@ private class Parser(fromPos: Pos) {
 | 
				
			|||||||
        return null
 | 
					        return null
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    init {
 | 
				
			||||||
 | 
					        // skip shebang
 | 
				
			||||||
 | 
					        if( pos.readFragment("#!") )
 | 
				
			||||||
 | 
					            loadToEndOfLine()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -1,15 +1,20 @@
 | 
				
			|||||||
package net.sergeych.lyng
 | 
					package net.sergeych.lyng
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import net.sergeych.lyng.pacman.ImportProvider
 | 
					import net.sergeych.lyng.pacman.ImportManager
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 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.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * There are special types of scopes:
 | 
					 * 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.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * - [Script.defaultScope] - root scope for a script, safe one
 | 
					 * If you want to create [ModuleScope] by hand, try [importManager] and [ImportManager.newModule],
 | 
				
			||||||
 * - [AppliedScope] - scope used to apply a closure to some thisObj scope
 | 
					 * or [ImportManager.newModuleAt].
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *  There are special types of scopes:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *  - [AppliedScope] - scope used to apply a closure to some thisObj scope
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
open class Scope(
 | 
					open class Scope(
 | 
				
			||||||
    val parent: Scope?,
 | 
					    val parent: Scope?,
 | 
				
			||||||
@ -24,7 +29,7 @@ open class Scope(
 | 
				
			|||||||
        args: Arguments = Arguments.EMPTY,
 | 
					        args: Arguments = Arguments.EMPTY,
 | 
				
			||||||
        pos: Pos = Pos.builtIn,
 | 
					        pos: Pos = Pos.builtIn,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
            : this(Script.defaultScope, args, pos)
 | 
					            : this(Script.defaultImportManager.newModuleAt(pos), args, pos)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun raiseNotImplemented(what: String = "operation"): Nothing = raiseError("$what is not implemented")
 | 
					    fun raiseNotImplemented(what: String = "operation"): Nothing = raiseError("$what is not implemented")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -141,7 +146,7 @@ open class Scope(
 | 
				
			|||||||
    suspend fun eval(source: Source): Obj =
 | 
					    suspend fun eval(source: Source): Obj =
 | 
				
			||||||
        Compiler.compile(
 | 
					        Compiler.compile(
 | 
				
			||||||
            source,
 | 
					            source,
 | 
				
			||||||
            (this as? ModuleScope)?.importProvider ?: ImportProvider.emptyAllowAll
 | 
					            (this as? ModuleScope)?.importProvider ?: Script.defaultImportManager
 | 
				
			||||||
        ).execute(this)
 | 
					        ).execute(this)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun containsLocal(name: String): Boolean = name in objects
 | 
					    fun containsLocal(name: String): Boolean = name in objects
 | 
				
			||||||
@ -154,4 +159,19 @@ open class Scope(
 | 
				
			|||||||
    open suspend fun importInto(scope: Scope, symbols: Map<String, String>? = null) {
 | 
					    open suspend fun importInto(scope: Scope, symbols: Map<String, String>? = null) {
 | 
				
			||||||
        scope.raiseError(ObjIllegalOperationException(scope, "Import is not allowed here: import $packageName"))
 | 
					        scope.raiseError(ObjIllegalOperationException(scope, "Import is not allowed here: import $packageName"))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Find a first [ImportManager] in this Scope hierarchy. Normally there should be one. Found instance is cached.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Use it to register your package sources, see [ImportManager] features.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @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 {
 | 
				
			||||||
 | 
					        if( this is ModuleScope )
 | 
				
			||||||
 | 
					            (importProvider as? ImportManager)?.let { return@lazy it }
 | 
				
			||||||
 | 
					        parent?.importManager ?: throw IllegalStateException("this scope has no manager in the chain")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,7 @@
 | 
				
			|||||||
package net.sergeych.lyng
 | 
					package net.sergeych.lyng
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import kotlinx.coroutines.delay
 | 
					import kotlinx.coroutines.delay
 | 
				
			||||||
 | 
					import net.sergeych.lyng.pacman.ImportManager
 | 
				
			||||||
import kotlin.math.*
 | 
					import kotlin.math.*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Script(
 | 
					class Script(
 | 
				
			||||||
@ -17,10 +18,11 @@ class Script(
 | 
				
			|||||||
        return lastResult
 | 
					        return lastResult
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    suspend fun execute() = execute(defaultScope.copy(pos = pos))
 | 
					    suspend fun execute() = execute(defaultImportManager.newModule())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    companion object {
 | 
					    companion object {
 | 
				
			||||||
        val defaultScope: Scope = Scope().apply {
 | 
					
 | 
				
			||||||
 | 
					        private val rootScope: Scope = Scope(null).apply {
 | 
				
			||||||
            ObjException.addExceptionsToContext(this)
 | 
					            ObjException.addExceptionsToContext(this)
 | 
				
			||||||
            addFn("println") {
 | 
					            addFn("println") {
 | 
				
			||||||
                for ((i, a) in args.withIndex()) {
 | 
					                for ((i, a) in args.withIndex()) {
 | 
				
			||||||
@ -170,8 +172,9 @@ class Script(
 | 
				
			|||||||
            getOrCreateNamespace("Math").apply {
 | 
					            getOrCreateNamespace("Math").apply {
 | 
				
			||||||
                addConst("PI", pi)
 | 
					                addConst("PI", pi)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        val defaultImportManager: ImportManager by lazy { ImportManager(rootScope, SecurityManager.allowAll) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -0,0 +1,126 @@
 | 
				
			|||||||
 | 
					package net.sergeych.lyng.pacman
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import kotlinx.coroutines.sync.Mutex
 | 
				
			||||||
 | 
					import kotlinx.coroutines.sync.withLock
 | 
				
			||||||
 | 
					import net.sergeych.lyng.*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Import manager allow to register packages with builder lambdas and act as an
 | 
				
			||||||
 | 
					 * [ImportProvider]. Note that packages _must be registered_ first with [addPackage],
 | 
				
			||||||
 | 
					 * [addSourcePackages] or [addTextPackages]. Registration is cheap, actual package
 | 
				
			||||||
 | 
					 * building is lazily performed on [createModuleScope], when the package will
 | 
				
			||||||
 | 
					 * be first imported.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * It is possible to register new packages at any time, but it is not allowed to override
 | 
				
			||||||
 | 
					 * packages already registered.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class ImportManager(
 | 
				
			||||||
 | 
					    rootScope: Scope = Script.defaultImportManager.newModule(),
 | 
				
			||||||
 | 
					    securityManager: SecurityManager = SecurityManager.allowAll
 | 
				
			||||||
 | 
					): ImportProvider(rootScope, securityManager) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private inner class Entry(
 | 
				
			||||||
 | 
					        val packageName: String,
 | 
				
			||||||
 | 
					        val builder: suspend (ModuleScope) -> Unit,
 | 
				
			||||||
 | 
					        var cachedScope: ModuleScope? = null
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        suspend fun getScope(pos: Pos): ModuleScope {
 | 
				
			||||||
 | 
					            cachedScope?.let { return it }
 | 
				
			||||||
 | 
					            return ModuleScope(inner, pos, packageName).apply {
 | 
				
			||||||
 | 
					                cachedScope = this
 | 
				
			||||||
 | 
					                builder(this)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Inner provider does not lock [access], the only difference; it is meant to be used
 | 
				
			||||||
 | 
					     * exclusively by the coroutine that starts actual import chain
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private inner class InternalProvider : ImportProvider(rootScope) {
 | 
				
			||||||
 | 
					        override suspend fun createModuleScope(pos: Pos, packageName: String): ModuleScope {
 | 
				
			||||||
 | 
					            return doImport(packageName, pos)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Inner module import provider used to prepare lazily prepared modules
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private val inner = InternalProvider()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private val imports = mutableMapOf<String, Entry>()
 | 
				
			||||||
 | 
					    private val access = Mutex()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Register new package that can be imported. It is not possible to unregister or
 | 
				
			||||||
 | 
					     * update package already registered.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Packages are lazily created when first imported somewhere, so the registration is
 | 
				
			||||||
 | 
					     * cheap; the recommended procedure is to register all available packages prior to
 | 
				
			||||||
 | 
					     * compile with this.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param name package name
 | 
				
			||||||
 | 
					     * @param builder lambda to create actual package using the given [ModuleScope]
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    suspend fun addPackage(name: String, builder: suspend (ModuleScope) -> Unit) {
 | 
				
			||||||
 | 
					        access.withLock {
 | 
				
			||||||
 | 
					            if (name in imports)
 | 
				
			||||||
 | 
					                throw IllegalArgumentException("Package $name already exists")
 | 
				
			||||||
 | 
					            imports[name] = Entry(name, builder)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Bulk [addPackage] with slightly better performance
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Suppress("unused")
 | 
				
			||||||
 | 
					    suspend fun addPackages(registrationData: List<Pair<String, suspend (ModuleScope) -> Unit>>) {
 | 
				
			||||||
 | 
					        access.withLock {
 | 
				
			||||||
 | 
					            for (pp in registrationData) {
 | 
				
			||||||
 | 
					                if (pp.first in imports)
 | 
				
			||||||
 | 
					                    throw IllegalArgumentException("Package ${pp.first} already exists")
 | 
				
			||||||
 | 
					                imports[pp.first] = Entry(pp.first, pp.second)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Perform actual import or return ready scope. __It must only be called when
 | 
				
			||||||
 | 
					     * [access] is locked__, e.g. only internally
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private suspend fun doImport(packageName: String, pos: Pos): ModuleScope {
 | 
				
			||||||
 | 
					        val entry = imports[packageName] ?: throw ImportException(pos, "package not found: $packageName")
 | 
				
			||||||
 | 
					        return entry.getScope(pos)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override suspend fun createModuleScope(pos: Pos, packageName: String): ModuleScope =
 | 
				
			||||||
 | 
					        doImport(packageName, pos)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Add packages that only need to compile [Source].
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    suspend fun addSourcePackages(vararg sources: Source) {
 | 
				
			||||||
 | 
					        for( s in sources) {
 | 
				
			||||||
 | 
					            addPackage(s.extractPackageName()) {
 | 
				
			||||||
 | 
					                it.eval(s)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Add source packages using package name as [Source.fileName], for simplicity
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    suspend fun addTextPackages(vararg sourceTexts: String) {
 | 
				
			||||||
 | 
					        for( s in sourceTexts) {
 | 
				
			||||||
 | 
					            var source = Source("tmp", s)
 | 
				
			||||||
 | 
					            val packageName = source.extractPackageName()
 | 
				
			||||||
 | 
					            source = Source(packageName, s)
 | 
				
			||||||
 | 
					            addPackage(packageName) { it.eval(source)}
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -11,7 +11,7 @@ import net.sergeych.lyng.*
 | 
				
			|||||||
 * caching strategy depends on the import provider
 | 
					 * caching strategy depends on the import provider
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
abstract class ImportProvider(
 | 
					abstract class ImportProvider(
 | 
				
			||||||
    val rootScope: Scope = Script.defaultScope,
 | 
					    val rootScope: Scope,
 | 
				
			||||||
    val securityManager: SecurityManager = SecurityManager.allowAll
 | 
					    val securityManager: SecurityManager = SecurityManager.allowAll
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@ -38,11 +38,11 @@ abstract class ImportProvider(
 | 
				
			|||||||
        return createModuleScope(pos, name)
 | 
					        return createModuleScope(pos, name)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    companion object {
 | 
					    fun newModule() = newModuleAt(Pos.builtIn)
 | 
				
			||||||
        val emptyAllowAll = object : ImportProvider(rootScope = Script.defaultScope, securityManager = SecurityManager.allowAll) {
 | 
					
 | 
				
			||||||
            override suspend fun createModuleScope(pos: Pos,packageName: String): ModuleScope {
 | 
					    fun newModuleAt(pos: Pos): ModuleScope =
 | 
				
			||||||
                throw ImportException(pos, "Empty import provider can't be used directly")
 | 
					        ModuleScope(this, pos, "unknown")
 | 
				
			||||||
            }
 | 
					}
 | 
				
			||||||
        }
 | 
					
 | 
				
			||||||
    }
 | 
					
 | 
				
			||||||
}
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,63 +1,35 @@
 | 
				
			|||||||
package net.sergeych.lyng.pacman
 | 
					package net.sergeych.lyng.pacman
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import kotlinx.coroutines.sync.Mutex
 | 
					import kotlinx.coroutines.CompletableDeferred
 | 
				
			||||||
import kotlinx.coroutines.sync.withLock
 | 
					 | 
				
			||||||
import net.sergeych.lyng.*
 | 
					import net.sergeych.lyng.*
 | 
				
			||||||
 | 
					import net.sergeych.mp_tools.globalLaunch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * The import provider that imports sources available in memory.
 | 
					 * The sample import provider that imports sources available in memory.
 | 
				
			||||||
 | 
					 * on construction time.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Actually it is left here only as a demo.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class InlineSourcesImportProvider(val sources: List<Source>,
 | 
					class InlineSourcesImportProvider(sources: List<Source>,
 | 
				
			||||||
                                  rootScope: ModuleScope = ModuleScope(emptyAllowAll, Source.builtIn),
 | 
					                                  rootScope: ModuleScope = Script.defaultImportManager.newModule(),
 | 
				
			||||||
                                  securityManager: SecurityManager = SecurityManager.allowAll
 | 
					                                  securityManager: SecurityManager = SecurityManager.allowAll
 | 
				
			||||||
) : ImportProvider(rootScope, securityManager) {
 | 
					) : ImportProvider(rootScope) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private class Entry(
 | 
					    private val manager = ImportManager(rootScope, securityManager)
 | 
				
			||||||
        val source: Source,
 | 
					 | 
				
			||||||
        var scope: ModuleScope? = null,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private val inner = InitMan()
 | 
					    private val readyManager = CompletableDeferred<ImportManager>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private val modules = run {
 | 
					    /**
 | 
				
			||||||
        val result = mutableMapOf<String, Entry>()
 | 
					     * This implementation only
 | 
				
			||||||
        for (source in sources) {
 | 
					     */
 | 
				
			||||||
            val name = source.extractPackageName()
 | 
					    override suspend fun createModuleScope(pos: Pos, packageName: String): ModuleScope {
 | 
				
			||||||
            result[name] = Entry(source)
 | 
					        return readyManager.await().createModuleScope(pos, packageName)
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        result
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private var access = Mutex()
 | 
					    init {
 | 
				
			||||||
 | 
					        globalLaunch {
 | 
				
			||||||
    /**
 | 
					            manager.addSourcePackages(*sources.toTypedArray())
 | 
				
			||||||
     * Inner provider does not lock [access], the only difference; it is meant to be used
 | 
					            readyManager.complete(manager)
 | 
				
			||||||
     * exclusively by the coroutine that starts actual import chain
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private inner class InitMan : ImportProvider() {
 | 
					 | 
				
			||||||
        override suspend fun createModuleScope(pos: Pos,packageName: String): ModuleScope {
 | 
					 | 
				
			||||||
            return doImport(packageName, pos)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * External interface, thread-safe. Can suspend until actual import is done. implements caching.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    override suspend fun createModuleScope(pos: Pos,packageName: String): ModuleScope =
 | 
					 | 
				
			||||||
        access.withLock {
 | 
					 | 
				
			||||||
            doImport(packageName, pos)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Perform actual import or return ready scope. __It must only be called when
 | 
					 | 
				
			||||||
     * [access] is locked__, e.g. only internally
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private suspend fun doImport(packageName: String, pos: Pos): ModuleScope {
 | 
					 | 
				
			||||||
        modules[packageName]?.scope?.let { return it }
 | 
					 | 
				
			||||||
        val entry = modules[packageName] ?: throw ImportException(pos, "Unknown package $packageName")
 | 
					 | 
				
			||||||
        return ModuleScope(inner, pos, packageName).apply {
 | 
					 | 
				
			||||||
            entry.scope = this
 | 
					 | 
				
			||||||
            eval(entry.source)
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -604,7 +604,7 @@ class ScriptTest {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Test
 | 
					    @Test
 | 
				
			||||||
    fun testAssignArgumentsmiddleEllipsis() = runTest {
 | 
					    fun testAssignArgumentsMiddleEllipsis() = runTest {
 | 
				
			||||||
        val ttEnd = Token.Type.RBRACE
 | 
					        val ttEnd = Token.Type.RBRACE
 | 
				
			||||||
        val pa = ArgsDeclaration(
 | 
					        val pa = ArgsDeclaration(
 | 
				
			||||||
            listOf(
 | 
					            listOf(
 | 
				
			||||||
@ -2440,4 +2440,24 @@ class ScriptTest {
 | 
				
			|||||||
        assertEquals("foo1 / bar1", scope.eval(src).toString())
 | 
					        assertEquals("foo1 / bar1", scope.eval(src).toString())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    fun testDefaultImportManager() = runTest {
 | 
				
			||||||
 | 
					        val scope = Scope()
 | 
				
			||||||
 | 
					        assertFails {
 | 
				
			||||||
 | 
					            scope.eval("""
 | 
				
			||||||
 | 
					                import foo
 | 
				
			||||||
 | 
					                foo()
 | 
				
			||||||
 | 
					                """.trimIndent())
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        scope.importManager.addTextPackages("""
 | 
				
			||||||
 | 
					            package foo
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            fun foo() { "bar" }
 | 
				
			||||||
 | 
					        """.trimIndent())
 | 
				
			||||||
 | 
					        scope.eval("""
 | 
				
			||||||
 | 
					                import foo
 | 
				
			||||||
 | 
					                assertEquals( "bar", foo())
 | 
				
			||||||
 | 
					                """.trimIndent())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user