From d0ed86c11ced8608431d4c74f62c69b583e4965d Mon Sep 17 00:00:00 2001 From: Ilya Chernikov Date: Mon, 16 Jul 2018 10:31:58 +0200 Subject: [PATCH] Add REPL interfaces to scripting API --- .../experimental/api/scriptEvaluation.kt | 10 +++- .../experimental/host/BasicScriptingHost.kt | 1 - .../experimental/repl/replCompilation.kt | 30 ++++++++++ .../script/experimental/repl/replData.kt | 11 ++++ .../experimental/repl/replEvaluation.kt | 25 +++++++++ .../script/experimental/repl/replHistory.kt | 55 +++++++++++++++++++ .../script/experimental/repl/replHost.kt | 48 ++++++++++++++++ .../jvm/runners/BasicJvmScriptEvaluator.kt | 3 +- 8 files changed, 180 insertions(+), 3 deletions(-) create mode 100644 libraries/scripting/common/src/kotlin/script/experimental/repl/replCompilation.kt create mode 100644 libraries/scripting/common/src/kotlin/script/experimental/repl/replData.kt create mode 100644 libraries/scripting/common/src/kotlin/script/experimental/repl/replEvaluation.kt create mode 100644 libraries/scripting/common/src/kotlin/script/experimental/repl/replHistory.kt create mode 100644 libraries/scripting/common/src/kotlin/script/experimental/repl/replHost.kt diff --git a/libraries/scripting/common/src/kotlin/script/experimental/api/scriptEvaluation.kt b/libraries/scripting/common/src/kotlin/script/experimental/api/scriptEvaluation.kt index e1b69d5b149..af768633099 100644 --- a/libraries/scripting/common/src/kotlin/script/experimental/api/scriptEvaluation.kt +++ b/libraries/scripting/common/src/kotlin/script/experimental/api/scriptEvaluation.kt @@ -22,7 +22,15 @@ object ScriptEvaluationEnvironmentParams { typealias ScriptEvaluationEnvironment = ChainedPropertyBag -data class EvaluationResult(val returnValue: Any?, val environment: ScriptEvaluationEnvironment) +sealed class ResultValue { + class Value(val name: String, val value: Any?, val type: String) : ResultValue() { + override fun toString(): String = "$name: $type = $value" + } + + object Unit : ResultValue() +} + +data class EvaluationResult(val returnValue: ResultValue, val environment: ScriptEvaluationEnvironment) interface ScriptEvaluator { diff --git a/libraries/scripting/common/src/kotlin/script/experimental/host/BasicScriptingHost.kt b/libraries/scripting/common/src/kotlin/script/experimental/host/BasicScriptingHost.kt index 5a07e2e5aa4..6f583bbdb4e 100644 --- a/libraries/scripting/common/src/kotlin/script/experimental/host/BasicScriptingHost.kt +++ b/libraries/scripting/common/src/kotlin/script/experimental/host/BasicScriptingHost.kt @@ -24,7 +24,6 @@ import kotlin.script.experimental.api.* abstract class BasicScriptingHost( val compiler: ScriptCompiler, - // TODO: does it belong here or to the definition? val evaluator: ScriptEvaluator ) { open fun runInCoroutineContext(block: suspend CoroutineScope.() -> T): T = runBlocking { block() } diff --git a/libraries/scripting/common/src/kotlin/script/experimental/repl/replCompilation.kt b/libraries/scripting/common/src/kotlin/script/experimental/repl/replCompilation.kt new file mode 100644 index 00000000000..b4128b3b62b --- /dev/null +++ b/libraries/scripting/common/src/kotlin/script/experimental/repl/replCompilation.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2010-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.repl + +import kotlin.script.experimental.api.* + +interface ReplSnippetChecker { + + suspend operator fun invoke( + snippet: ReplSnippetSource, + state: ReplStageState<*>, + scriptDefinition: ScriptDefinition, + additionalConfiguration: ScriptCompileConfiguration? = null // overrides properties from definition + ): ResultWithDiagnostics +} + +interface ReplSnippetCompiler { + + suspend operator fun invoke( + snippet: ReplSnippetSource, + state: ReplStageState<*>, + scriptDefinition: ScriptDefinition, + additionalConfiguration: ScriptCompileConfiguration? = null // overrides properties from definition + ): ResultWithDiagnostics> +} + +interface CompiledReplSnippet : CompiledScript diff --git a/libraries/scripting/common/src/kotlin/script/experimental/repl/replData.kt b/libraries/scripting/common/src/kotlin/script/experimental/repl/replData.kt new file mode 100644 index 00000000000..9295eebf7a4 --- /dev/null +++ b/libraries/scripting/common/src/kotlin/script/experimental/repl/replData.kt @@ -0,0 +1,11 @@ +/* + * Copyright 2010-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.repl + +import kotlin.script.experimental.api.ScriptSource + +interface ReplSnippetSource : ScriptSource, ReplSnippetId + diff --git a/libraries/scripting/common/src/kotlin/script/experimental/repl/replEvaluation.kt b/libraries/scripting/common/src/kotlin/script/experimental/repl/replEvaluation.kt new file mode 100644 index 00000000000..6c1c9bb3e5a --- /dev/null +++ b/libraries/scripting/common/src/kotlin/script/experimental/repl/replEvaluation.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2010-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.repl + +import kotlin.script.experimental.api.EvaluationResult +import kotlin.script.experimental.api.ResultWithDiagnostics +import kotlin.script.experimental.util.ChainedPropertyBag + + +object ReplEvaluationEnvironmentParams { + +} + +typealias ReplEvaluationEnvironment = ChainedPropertyBag + +interface ReplSnippetEvaluator { + + suspend operator fun invoke( + compiledSnippet: CompiledReplSnippet<*>, + replEvaluationEnvironment: ReplEvaluationEnvironment + ): ResultWithDiagnostics +} diff --git a/libraries/scripting/common/src/kotlin/script/experimental/repl/replHistory.kt b/libraries/scripting/common/src/kotlin/script/experimental/repl/replHistory.kt new file mode 100644 index 00000000000..c246dc109fc --- /dev/null +++ b/libraries/scripting/common/src/kotlin/script/experimental/repl/replHistory.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2010-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.repl + +import java.util.concurrent.locks.ReentrantReadWriteLock +import kotlin.concurrent.read +import kotlin.concurrent.write + +const val REPL_CODE_LINE_FIRST_NO = 1 +const val REPL_CODE_LINE_FIRST_GEN = 1 + +interface ReplSnippetId : Comparable { + val no: Int + val generation: Int +} + +data class ReplHistoryRecord(val id: ReplSnippetId, val item: T) + +interface IReplStageHistory : List> { + + fun peek(): ReplHistoryRecord? = lock.read { lastOrNull() } + + fun push(id: ReplSnippetId, item: T) + + fun pop(): ReplHistoryRecord? + + fun verifiedPop(id: ReplSnippetId): ReplHistoryRecord? = lock.write { + if (lastOrNull()?.id == id) pop() + else null + } + + fun reset(): Iterable + + fun resetTo(id: ReplSnippetId): Iterable + + val lock: ReentrantReadWriteLock +} + +interface ReplStageState { + val history: IReplStageHistory + + val lock: ReentrantReadWriteLock + + val currentGeneration: Int + + fun getNextLineNo(): Int = + history.peek()?.id?.no?.let { it + 1 } ?: REPL_CODE_LINE_FIRST_NO // TODO: it should be more robust downstream (e.g. use atomic) + + fun > asState(target: Class): StateT = + if (target.isAssignableFrom(this::class.java)) this as StateT + else throw IllegalArgumentException("$this is not an expected instance of IReplStageState") +} diff --git a/libraries/scripting/common/src/kotlin/script/experimental/repl/replHost.kt b/libraries/scripting/common/src/kotlin/script/experimental/repl/replHost.kt new file mode 100644 index 00000000000..4c4b3b94628 --- /dev/null +++ b/libraries/scripting/common/src/kotlin/script/experimental/repl/replHost.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2010-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.repl + +import kotlinx.coroutines.experimental.CoroutineScope +import kotlinx.coroutines.experimental.runBlocking +import kotlin.script.experimental.api.EvaluationResult +import kotlin.script.experimental.api.ResultWithDiagnostics +import kotlin.script.experimental.api.ScriptCompileConfiguration +import kotlin.script.experimental.api.ScriptDefinition +import kotlin.script.experimental.util.ChainedPropertyBag +import kotlin.script.experimental.util.typedKey + +interface ReplCommandProcessor { + suspend operator fun invoke( + command: String, + state: ReplStageState<*> + ): ResultWithDiagnostics +} + +data class ReplCommand(val commandName: String, val processor: ReplCommandProcessor) + +object ReplHostEnvironmentParams { + + val replCommands by typedKey>() +} + +typealias ReplHostEnvironment = ChainedPropertyBag + +abstract class ReplHost( + val environment: ReplHostEnvironment, + val checker: ReplSnippetChecker, + val compiler: ReplSnippetCompiler, + val evaluator: ReplSnippetEvaluator +) { + open fun runInCoroutineContext(block: suspend CoroutineScope.() -> T): T = runBlocking { block() } + + abstract fun eval( + snippet: ReplSnippetSource, + state: ReplStageState<*>, + scriptDefinition: ScriptDefinition, + additionalConfiguration: ScriptCompileConfiguration? = null, // overrides properties from definition + replEvaluationEnvironment: ReplEvaluationEnvironment + ): ResultWithDiagnostics +} diff --git a/libraries/scripting/jvm/src/kotlin/script/experimental/jvm/runners/BasicJvmScriptEvaluator.kt b/libraries/scripting/jvm/src/kotlin/script/experimental/jvm/runners/BasicJvmScriptEvaluator.kt index 247efdba94e..42b56be75dd 100644 --- a/libraries/scripting/jvm/src/kotlin/script/experimental/jvm/runners/BasicJvmScriptEvaluator.kt +++ b/libraries/scripting/jvm/src/kotlin/script/experimental/jvm/runners/BasicJvmScriptEvaluator.kt @@ -32,7 +32,8 @@ open class BasicJvmScriptEvaluator() : ScriptEvaluator { scriptObject.getConstructor(Array::class.java).newInstance(receivers.toTypedArray()) } - ResultWithDiagnostics.Success(EvaluationResult(instance, scriptEvaluationEnvironment)) + // TODO: fix result value + ResultWithDiagnostics.Success(EvaluationResult(ResultValue.Value("", instance, ""), scriptEvaluationEnvironment)) } } }