Add new experimental scripting API
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package kotlin.script.experimental.api
|
||||
|
||||
interface ScriptCompiler {
|
||||
|
||||
suspend fun compile(
|
||||
configuration: ScriptCompileConfiguration,
|
||||
configurator: ScriptConfigurator? = null
|
||||
): ResultWithDiagnostics<CompiledScript<*>>
|
||||
}
|
||||
|
||||
|
||||
interface CompiledScript<out ScriptBase : Any> {
|
||||
|
||||
val configuration: ScriptCompileConfiguration
|
||||
|
||||
suspend fun instantiate(scriptEvaluationEnvironment: ScriptEvaluationEnvironment): ResultWithDiagnostics<ScriptBase>
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("unused")
|
||||
|
||||
package kotlin.script.experimental.api
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
object ScriptCompileConfigurationParams {
|
||||
|
||||
val scriptSourceFragments by typedKey<ScriptSourceFragments>()
|
||||
|
||||
val scriptSignature by typedKey<ScriptSignature>()
|
||||
|
||||
val importedPackages by typedKey<Iterable<String>>()
|
||||
|
||||
val restrictions by typedKey<ResolvingRestrictions>()
|
||||
|
||||
val importedScripts by typedKey<Iterable<ScriptSource>>()
|
||||
|
||||
val dependencies by typedKey<Iterable<ScriptDependency>>()
|
||||
|
||||
val compilerOptions by typedKey<Iterable<String>>() // Q: CommonCompilerOptions instead?
|
||||
|
||||
val updateConfigurationOnAnnotations by typedKey<Iterable<KClass<out Annotation>>>()
|
||||
|
||||
val updateConfigurationOnSections by typedKey<Iterable<String>>()
|
||||
}
|
||||
|
||||
typealias ScriptCompileConfiguration = HeterogeneousMap
|
||||
|
||||
fun ScriptSource.toScriptCompileConfiguration(vararg pairs: Pair<TypedKey<*>, Any?>) =
|
||||
ScriptCompileConfiguration(ScriptCompileConfigurationParams.scriptSourceFragments to ScriptSourceFragments(this, null), *pairs)
|
||||
|
||||
object ProcessedScriptDataParams {
|
||||
val annotations by typedKey<Iterable<Annotation>>()
|
||||
|
||||
val fragments by typedKey<Iterable<ScriptSourceNamedFragment>>()
|
||||
}
|
||||
|
||||
typealias ProcessedScriptData = HeterogeneousMap
|
||||
|
||||
|
||||
interface ScriptConfigurator {
|
||||
|
||||
// with null scriptSource should return a generic configuration for the script type
|
||||
suspend fun baseConfiguration(scriptSource: ScriptSource?) : ResultWithDiagnostics<ScriptCompileConfiguration>
|
||||
|
||||
suspend fun refineConfiguration(
|
||||
configuration: ScriptCompileConfiguration,
|
||||
processedScriptData: ProcessedScriptData = ProcessedScriptData()
|
||||
): ResultWithDiagnostics<ScriptCompileConfiguration>
|
||||
}
|
||||
|
||||
fun ScriptSource?.toConfigEntry(): Pair<TypedKey<*>, Any?> =
|
||||
ScriptCompileConfigurationParams.scriptSourceFragments to this?.let { ScriptSourceFragments(this, null) }
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("unused")
|
||||
|
||||
package kotlin.script.experimental.api
|
||||
|
||||
import java.net.URL
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KType
|
||||
|
||||
interface ScriptSource {
|
||||
val location: URL?
|
||||
val text: String?
|
||||
|
||||
data class Position(val line: Int, val col: Int, val absolutePos: Int? = null)
|
||||
data class Range(val start: Position, val end: Position)
|
||||
data class Location(val start: Position, val end: Position? = null)
|
||||
}
|
||||
|
||||
data class ScriptSourceNamedFragment(val name: String?, val range: ScriptSource.Range)
|
||||
|
||||
open class ScriptSourceFragments(
|
||||
val originalSource: ScriptSource,
|
||||
val fragments: List<ScriptSourceNamedFragment>?)
|
||||
|
||||
open class ProvidedDeclarations(
|
||||
val implicitReceivers: List<KType> = emptyList(), // previous scripts, etc.
|
||||
val contextVariables: Map<String, KType> = emptyMap() // external variables
|
||||
// Q: do we need context constants and/or types here, e.g.
|
||||
// val contextConstants: Map<String, Any?> // or with KType as well
|
||||
// val contextTypes: List<KType> // additional (to the classpath) types provided by the environment
|
||||
// alternatively:
|
||||
// val contextDeclarations: List<Tuple<DeclarationKind, String?, KType, Any?> // kind, name, type, value
|
||||
// OR: it should be a HeterogeneousMap too
|
||||
) {
|
||||
object Empty : ProvidedDeclarations()
|
||||
}
|
||||
|
||||
open class ScriptSignature(
|
||||
val scriptBase: KClass<*>,
|
||||
val providedDeclarations: ProvidedDeclarations
|
||||
)
|
||||
|
||||
open class ResolvingRestrictions {
|
||||
data class Rule(
|
||||
val allow: Boolean,
|
||||
val pattern: String // FQN wildcard
|
||||
)
|
||||
|
||||
val rules: Iterable<Rule> = arrayListOf()
|
||||
}
|
||||
|
||||
interface ScriptDependency {
|
||||
// Q: anything generic here?
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("unused")
|
||||
|
||||
package kotlin.script.experimental.api
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
interface ScriptDefinition {
|
||||
val baseClass: KClass<*>
|
||||
val selector: ScriptSelector
|
||||
val configurator: ScriptConfigurator
|
||||
val runner: ScriptRunner<*>?
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("unused")
|
||||
|
||||
package kotlin.script.experimental.api
|
||||
|
||||
data class ScriptDiagnostic(
|
||||
val message: String,
|
||||
val severity: Severity = Severity.ERROR,
|
||||
val location: ScriptSource.Location? = null,
|
||||
val exception: Throwable? = null
|
||||
) {
|
||||
enum class Severity { ERROR, WARNING, INFO, DEBUG }
|
||||
}
|
||||
|
||||
sealed class ResultWithDiagnostics<out R> {
|
||||
abstract val reports: List<ScriptDiagnostic>
|
||||
|
||||
data class Success<out R>(
|
||||
val value: R,
|
||||
override val reports: List<ScriptDiagnostic> = listOf()
|
||||
) : ResultWithDiagnostics<R>()
|
||||
|
||||
data class Failure(
|
||||
override val reports: List<ScriptDiagnostic>
|
||||
) : ResultWithDiagnostics<Nothing>() {
|
||||
constructor(vararg reports: ScriptDiagnostic) : this(reports.asList())
|
||||
}
|
||||
}
|
||||
|
||||
operator fun <R> List<ScriptDiagnostic>.plus(res: ResultWithDiagnostics<R>): ResultWithDiagnostics<R> = when (res) {
|
||||
is ResultWithDiagnostics.Success -> ResultWithDiagnostics.Success(res.value, this + res.reports)
|
||||
is ResultWithDiagnostics.Failure -> ResultWithDiagnostics.Failure(this + res.reports)
|
||||
}
|
||||
|
||||
fun <R : Any> R.asSuccess(reports: List<ScriptDiagnostic> = listOf()): ResultWithDiagnostics.Success<R> =
|
||||
ResultWithDiagnostics.Success(this, reports)
|
||||
|
||||
fun Throwable.asDiagnostics(customMessage: String? = null, location: ScriptSource.Location? = null): ScriptDiagnostic =
|
||||
ScriptDiagnostic(customMessage ?: message ?: "$this", ScriptDiagnostic.Severity.ERROR, location, this)
|
||||
|
||||
fun String.asErrorDiagnostics(location: ScriptSource.Location? = null): ScriptDiagnostic =
|
||||
ScriptDiagnostic(this, ScriptDiagnostic.Severity.ERROR, location)
|
||||
|
||||
fun<R> ResultWithDiagnostics<R>.resultOrNull(): R? = when (this) {
|
||||
is ResultWithDiagnostics.Success<R> -> value
|
||||
else -> null
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("unused")
|
||||
|
||||
package kotlin.script.experimental.api
|
||||
|
||||
object ScriptEvaluationEnvironmentParams {
|
||||
val implicitReceivers by typedKey<List<Any>>()
|
||||
val contextVariables by typedKey<Map<String, Any?>>() // external variables
|
||||
val constructorArgs by typedKey<List<Any?>>()
|
||||
val runArgs by typedKey<List<Any?>>()
|
||||
}
|
||||
|
||||
typealias ScriptEvaluationEnvironment = HeterogeneousMap
|
||||
|
||||
data class EvaluationResult(val returnValue: Any?, val environment: ScriptEvaluationEnvironment)
|
||||
|
||||
// NOTE: name inconsistency: run vs evaluate
|
||||
interface ScriptRunner<in ScriptBase : Any> {
|
||||
|
||||
suspend fun run(
|
||||
compiledScript: CompiledScript<ScriptBase>,
|
||||
scriptEvaluationEnvironment: ScriptEvaluationEnvironment
|
||||
): ResultWithDiagnostics<EvaluationResult>
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("unused")
|
||||
|
||||
package kotlin.script.experimental.api
|
||||
|
||||
interface ScriptSelector {
|
||||
|
||||
val name: String
|
||||
|
||||
val fileExtension: String // for preliminary selection by file type, e.g. in ide
|
||||
|
||||
fun makeScriptName(scriptFileName: String?): String
|
||||
|
||||
fun isKnownScript(script: ScriptSource): Boolean
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package kotlin.script.experimental.api
|
||||
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
data class TypedKey<T>(val name: String)
|
||||
|
||||
class TypedKeyDelegate<T> {
|
||||
operator fun getValue(thisRef: Any?, property: KProperty<*>): TypedKey<T> = TypedKey(property.name)
|
||||
}
|
||||
|
||||
fun <T> typedKey() = TypedKeyDelegate<T>()
|
||||
|
||||
class HeterogeneousMap(val data: Map<TypedKey<*>, Any?> = hashMapOf()) {
|
||||
constructor(vararg pairs: Pair<TypedKey<*>, Any?>) : this(hashMapOf(*pairs))
|
||||
}
|
||||
|
||||
fun HeterogeneousMap.cloneWith(vararg pairs: Pair<TypedKey<*>, Any?>) = HeterogeneousMap(HashMap(data).apply { putAll(pairs) })
|
||||
|
||||
operator fun <T> HeterogeneousMap.get(key: TypedKey<T>): T =
|
||||
if (data.containsKey(key)) data[key] as T
|
||||
else throw IllegalArgumentException("Unknown key $key")
|
||||
|
||||
fun <T> HeterogeneousMap.getOrNull(key: TypedKey<T>): T? = data[key] as T?
|
||||
|
||||
Reference in New Issue
Block a user