All Downloads are FREE. Search and download functionalities are using the official Maven repository.

kotlin.script.experimental.api.scriptCompilation.kt Maven / Gradle / Ivy

There is a newer version: 2.0.20-Beta2
Show newest version
/*
 * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
 * 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", "RemoveExplicitTypeArguments")

package kotlin.script.experimental.api

import java.io.File
import java.io.Serializable
import kotlin.reflect.KClass
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.util.PropertiesCollection

interface ScriptCompilationConfigurationKeys

/**
 * The container for script compilation configuration
 * For usages see {@link KotlinScript} and actual code examples
 */
open class ScriptCompilationConfiguration(baseConfigurations: Iterable, body: Builder.() -> Unit) :
    PropertiesCollection(Builder(baseConfigurations).apply(body).data) {

    constructor(body: Builder.() -> Unit = {}) : this(emptyList(), body)
    constructor(
        vararg baseConfigurations: ScriptCompilationConfiguration, body: Builder.() -> Unit = {}
    ) : this(baseConfigurations.asIterable(), body)

    class Builder internal constructor(baseConfigurations: Iterable) :
        ScriptCompilationConfigurationKeys,
        PropertiesCollection.Builder(baseConfigurations)

    // inherited from script compilationConfiguration for using as a keys anchor
    companion object : ScriptCompilationConfigurationKeys

    object Default : ScriptCompilationConfiguration()

    override fun toString(): String {
        return "ScriptCompilationConfiguration($properties)"
    }
}


/**
 * An alternative to the constructor with base configuration, which returns a new configuration only if [body] adds anything
 * to the original one, otherwise returns original
 */
fun ScriptCompilationConfiguration?.with(body: ScriptCompilationConfiguration.Builder.() -> Unit): ScriptCompilationConfiguration {
    val newConfiguration =
        if (this == null) ScriptCompilationConfiguration(body = body)
        else ScriptCompilationConfiguration(this, body = body)
    return if (newConfiguration == this) this else newConfiguration
}

/**
 * The script type display name
 */
val ScriptCompilationConfigurationKeys.displayName by PropertiesCollection.key()

/**
 * The default script class name
 */
val ScriptCompilationConfigurationKeys.defaultIdentifier by PropertiesCollection.key("Script")

/**
 * The script filename extension
 * Used for the primary script definition selection as well as to assign a kotlin-specific file type to the files with the extension in Intellij IDEA
 * For Intellij IDEA support, it is important to have this extension set to a non-ambiguous name.
 * See also {@link ScriptCompilationConfigurationKeys#filePathPattern} parameter for more fine-grained script definition selection
 */
val ScriptCompilationConfigurationKeys.fileExtension by PropertiesCollection.key("kts")

/**
 * Additional (to the filename extension) RegEx pattern with that the script file path is checked
 * It is used in the hosts that may have several script definitions registered and need to distinguish script file types not only by extension
 * The argument passed to the RegEx matcher is equivalent to the File.path, taken relatively from a base path defined by the host
 * (usually should be the project root or the current directory)
 * See also {@link ScriptCompilationConfigurationKeys#fileExtension} parameter for the primary script definition selection
 */
val ScriptCompilationConfigurationKeys.filePathPattern by PropertiesCollection.key()

/**
 * The superclass for target script class
 */
val ScriptCompilationConfigurationKeys.baseClass by PropertiesCollection.key(KotlinType(Any::class)) // script base class

/**
 * The list of classes that will be used as implicit receivers in the script body, as if the whole body is wrapped with "with" calls:
 * 
 * {@code
 *   with (receiver1) {
 *     ...
 *       with (receiverN) {
 *         // script body
 *       }
 *   }
 * }
 * 
* * Note: the actual receivers values should be passed to the constructor of the generated script class */ val ScriptCompilationConfigurationKeys.implicitReceivers by PropertiesCollection.key>() // in the order from outer to inner scope /** * The map of names to the types */ val ScriptCompilationConfigurationKeys.providedProperties by PropertiesCollection.key>() // external variables /** * Variable name that holds a {@link File} instance pointing to the location of the script file */ val ScriptCompilationConfigurationKeys.scriptFileLocationVariable by PropertiesCollection.key() /** * File pointing to the location of the script file. Note that in some cases it might not be possible * to determine script file location properly - in this case the file is an empty file */ val ScriptCompilationConfigurationKeys.scriptFileLocation by PropertiesCollection.key() /** * The list of import expressions that will be implicitly applied to the script body, the syntax is the same as for the "import" statement */ val ScriptCompilationConfigurationKeys.defaultImports by PropertiesCollection.key>() /** * The list of script sources that should be compiled along with the script and imported into it */ val ScriptCompilationConfigurationKeys.importScripts by PropertiesCollection.key>() /** * The name of the generated script class field to assign the script results to, empty means disabled * see also ReplScriptCompilationConfigurationKeys.resultFieldPrefix */ val ScriptCompilationConfigurationKeys.resultField by PropertiesCollection.key("\$\$result") /** * The list of script dependencies - platform specific */ val ScriptCompilationConfigurationKeys.dependencies by PropertiesCollection.key>() /** * The list of compiler options that will be applied on script compilation, the syntax is the same as for CLI compiler */ val ScriptCompilationConfigurationKeys.compilerOptions by PropertiesCollection.key>() // Q: CommonCompilerOptions instead? /** * The callback that will be called on the script compilation before parsing the script */ val ScriptCompilationConfigurationKeys.refineConfigurationBeforeParsing by PropertiesCollection.key>(isTransient = true) /** * The callback that will be called on the script compilation after parsing script file annotations */ val ScriptCompilationConfigurationKeys.refineConfigurationOnAnnotations by PropertiesCollection.key>(isTransient = true) /** * The callback that will be called on the script compilation immediately before starting the compilation */ val ScriptCompilationConfigurationKeys.refineConfigurationBeforeCompiling by PropertiesCollection.key>(isTransient = true) /** * The list of script fragments that should be compiled instead of the whole text * (for use primary with the refinement callbacks) */ val ScriptCompilationConfigurationKeys.sourceFragments by PropertiesCollection.key>() /** * Scripting host configuration */ val ScriptCompilationConfigurationKeys.hostConfiguration by PropertiesCollection.key(isTransient = true) /** * Should the script be always considered standalone * If true, it is ignored when compiled along with other sources (starting from 1.9, according to SkipStandaloneScriptsInSourceRoots language feature) * true by default */ val ScriptCompilationConfigurationKeys.isStandalone by PropertiesCollection.key(true) /** * The sub-builder DSL for configuring refinement callbacks */ val ScriptCompilationConfiguration.Builder.refineConfiguration get() = RefineConfigurationBuilder() class RefineConfigurationBuilder : PropertiesCollection.Builder() { /** * The callback that will be called on the script compilation before parsing the script * @param handler the callback that will be called */ fun beforeParsing(handler: RefineScriptCompilationConfigurationHandler) { ScriptCompilationConfiguration.refineConfigurationBeforeParsing.append(RefineConfigurationUnconditionallyData(handler)) } /** * The callback that will be called on the script compilation after parsing script file annotations * @param annotations the list of annotations to trigger the callback on * @param handler the callback that will be called */ fun onAnnotations(annotations: List, handler: RefineScriptCompilationConfigurationHandler) { // TODO: implement handlers composition ScriptCompilationConfiguration.refineConfigurationOnAnnotations.append(RefineConfigurationOnAnnotationsData(annotations, handler)) } /** * The callback that will be called on the script compilation after parsing script file annotations * @param annotations the list of annotations to trigger the callback on * @param handler the callback that will be called */ fun onAnnotations(vararg annotations: KotlinType, handler: RefineScriptCompilationConfigurationHandler) { onAnnotations(annotations.asList(), handler) } /** * The callback that will be called on the script compilation after parsing script file annotations * @param T the annotation to trigger the callback on * @param handler the callback that will be called */ inline fun onAnnotations(noinline handler: RefineScriptCompilationConfigurationHandler) { onAnnotations(listOf(KotlinType(T::class)), handler) } /** * The callback that will be called on the script compilation after parsing script file annotations * @param annotations the list of annotations to trigger the callback on * @param handler the callback that will be called */ fun onAnnotations(vararg annotations: KClass, handler: RefineScriptCompilationConfigurationHandler) { onAnnotations(annotations.map { KotlinType(it) }, handler) } /** * The callback that will be called on the script compilation after parsing script file annotations * @param annotations the list of annotations to trigger the callback on * @param handler the callback that will be called */ fun onAnnotations(annotations: Iterable>, handler: RefineScriptCompilationConfigurationHandler) { onAnnotations(annotations.map { KotlinType(it) }, handler) } /** * The callback that will be called on the script compilation immediately before starting the compilation * @param handler the callback that will be called */ fun beforeCompiling(handler: RefineScriptCompilationConfigurationHandler) { ScriptCompilationConfiguration.refineConfigurationBeforeCompiling.append(RefineConfigurationUnconditionallyData(handler)) } } /** * The refinement callback function signature */ typealias RefineScriptCompilationConfigurationHandler = (ScriptConfigurationRefinementContext) -> ResultWithDiagnostics /** * The refinement callback function signature for simple handlers (without diagnostics or errors) */ typealias SimpleRefineScriptCompilationConfigurationHandler = (ScriptConfigurationRefinementContext) -> ScriptCompilationConfiguration data class RefineConfigurationUnconditionallyData( val handler: RefineScriptCompilationConfigurationHandler ) : Serializable { companion object { private const val serialVersionUID: Long = 1L } } data class RefineConfigurationOnAnnotationsData( val annotations: List, val handler: RefineScriptCompilationConfigurationHandler ) : Serializable { companion object { private const val serialVersionUID: Long = 1L } } fun ScriptCompilationConfiguration.refineBeforeParsing( script: SourceCode, collectedData: ScriptCollectedData? = null ): ResultWithDiagnostics = simpleRefineImpl(ScriptCompilationConfiguration.refineConfigurationBeforeParsing) { config, refineData -> refineData.handler.invoke(ScriptConfigurationRefinementContext(script, config, collectedData)) } fun ScriptCompilationConfiguration.refineOnAnnotations( script: SourceCode, collectedData: ScriptCollectedData ): ResultWithDiagnostics { val foundAnnotationNames = collectedData[ScriptCollectedData.foundAnnotations]?.mapTo(HashSet()) { it.annotationClass.java.name } if (foundAnnotationNames.isNullOrEmpty()) return this.asSuccess() val thisResult: ResultWithDiagnostics = this.asSuccess() return this[ScriptCompilationConfiguration.refineConfigurationOnAnnotations] ?.fold(thisResult) { config, (annotations, handler) -> config.onSuccess { // checking that the collected data contains expected annotations if (annotations.none { foundAnnotationNames.contains(it.typeName) }) it.asSuccess() else handler.invoke(ScriptConfigurationRefinementContext(script, it, collectedData)) } } ?: thisResult } fun ScriptCompilationConfiguration.refineBeforeCompiling( script: SourceCode, collectedData: ScriptCollectedData? = null ): ResultWithDiagnostics = simpleRefineImpl(ScriptCompilationConfiguration.refineConfigurationBeforeCompiling) { config, refineData -> refineData.handler.invoke(ScriptConfigurationRefinementContext(script, config, collectedData)) } internal inline fun Configuration.simpleRefineImpl( key: PropertiesCollection.Key>, refineFn: (Configuration, RefineData) -> ResultWithDiagnostics ): ResultWithDiagnostics = ( this[key] ?.fold(this) { config, refineData -> refineFn(config, refineData).valueOr { return it } } ?: this ).asSuccess() /** * The functional interface to the script compiler */ interface ScriptCompiler { /** * Compiles the [script] according to the [scriptCompilationConfiguration] * @param script the interface to the script source code * @param scriptCompilationConfiguration the script compilation configuration properties * @return result wrapper, if successful - with compiled script */ suspend operator fun invoke( script: SourceCode, scriptCompilationConfiguration: ScriptCompilationConfiguration ): ResultWithDiagnostics } /** * The interface to the compiled script */ interface CompiledScript { /** * The location identifier for the script source, taken from SourceCode.locationId */ val sourceLocationId: String? get() = null /** * The compilation configuration used for script compilation */ val compilationConfiguration: ScriptCompilationConfiguration /** * The function that loads compiled script class * @param scriptEvaluationConfiguration the script evaluation configuration properties * @return result wrapper, if successful - with loaded KClass */ suspend fun getClass(scriptEvaluationConfiguration: ScriptEvaluationConfiguration?): ResultWithDiagnostics> /** * The scripts compiled along with this one in one module, imported or otherwise included into compilation */ val otherScripts: List get() = emptyList() /** * The name and the type of the script's result field, if any */ val resultField: Pair? get() = null }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy