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

com.strumenta.kolasu.semantics.scope.provider.declarative.DeclarativeScopeProvider.kt Maven / Gradle / Ivy

package com.strumenta.kolasu.semantics.scope.provider.declarative

import com.strumenta.kolasu.model.Node
import com.strumenta.kolasu.model.PossiblyNamed
import com.strumenta.kolasu.model.ReferenceByName
import com.strumenta.kolasu.semantics.scope.description.ScopeDescription
import com.strumenta.kolasu.semantics.scope.description.ScopeDescriptionApi
import com.strumenta.kolasu.semantics.scope.provider.ScopeProvider
import kotlin.reflect.KClass
import kotlin.reflect.KProperty1
import kotlin.reflect.full.isSuperclassOf

/**
 * Utility function for defining scoping rules.
 **/
inline fun <
    reified NodeTy : Node,
    PropertyTy : KProperty1?>
    > scopeFor(
    property: PropertyTy,
    ignoreCase: Boolean = false,
    noinline specification: ScopeDescriptionApi.(DeclarativeScopeProviderRuleContext) -> Unit
) = DeclarativeScopeProviderRule(NodeTy::class, property.name, ignoreCase, specification)

/**
 * Declarative scope provider instances can be used to specify language-specific
 * scoping rules. Using these, the provider computes scope descriptions
 * for node reference properties, which can then be used to perform symbol resolution.
 * Scoping rules can be provided as `DeclarativeScopeProviderRule` instances during instantiation.
 *
 * Given a specific language, we suggest to define an object extending this class to specify
 * language-specific scoping rules:
 * ```kotlin
 * object MyScopeProvider: DeclarativeScopeProvider(
 *     scopeFor(ANode::aReference) {
 *         name("theSymbolName")
 *         // local symbols (ScopeDescription API)
 *         include(somePossiblyNamedNode)
 *         include("explicitName", someNode)
 *         // global symbols (ScopeDescription API)
 *         val symbols: SymbolRepository = ASymbolRepository();
 *         symbols.allOfType(BNode::class).forEach { include(it) }
 *         symbols.allOfType(BNode::class).forEach { include( `explicitNameWith${it.someProperty}`, it) }
 *     }
 * )
 * ```
 **/
open class DeclarativeScopeProvider(
    vararg rules: DeclarativeScopeProviderRule
) : ScopeProvider {
    private val rules: MutableList> = rules.sorted().toMutableList()

    fun addRule(rule: DeclarativeScopeProviderRule) {
        rules.add(rule)
        rules.sort()
    }

    override fun  scopeFor(
        node: NodeType,
        reference: KProperty1?>
    ): ScopeDescription {
        return this.rules.firstOrNull { it.canBeInvokedWith(node::class, reference) }
            ?.invoke(this, node)
            ?: throw RuntimeException(
                "Cannot find scoping rule for reference ${node::class.qualifiedName}::${reference.name}"
            )
    }
}

/**
 * Represents a scoping rule, i.e. the body of `scopeFor(...)` definitions.
 **/
class DeclarativeScopeProviderRule(
    private val nodeType: KClass,
    private val propertyName: String,
    private val ignoreCase: Boolean,
    private val specification: ScopeDescription.(DeclarativeScopeProviderRuleContext) -> Unit
) : (ScopeProvider, Node) -> ScopeDescription, Comparable> {
    override fun invoke(
        scopeProvider: ScopeProvider,
        node: Node
    ): ScopeDescription {
        return ScopeDescription(this.ignoreCase).apply {
            @Suppress("UNCHECKED_CAST")
            val context = DeclarativeScopeProviderRuleContext(node as NodeTy, scopeProvider)
            this.specification(context)
        }
    }

    fun canBeInvokedWith(
        nodeType: KClass<*>,
        property: KProperty1<*, ReferenceByName?>
    ): Boolean {
        return this.nodeType.isSuperclassOf(nodeType) && this.propertyName == property.name
    }

    override fun compareTo(other: DeclarativeScopeProviderRule): Int {
        return when {
            this.nodeType.isSuperclassOf(other.nodeType) -> 1
            other.nodeType.isSuperclassOf(this.nodeType) -> -1
            else -> this.describeForComparison() compareTo other.describeForComparison()
        }
    }

    private fun describeForComparison(): String {
        return "${this.nodeType.qualifiedName ?: ""}::${this.propertyName}"
    }
}

/**
 * Represents the available context when defining scoping rules,
 * i.e. the `it` variable in `scopeFor(...)` bodies.
 **/
data class DeclarativeScopeProviderRuleContext(
    val node: NodeTy,
    val scopeProvider: ScopeProvider
)




© 2015 - 2025 Weber Informatics LLC | Privacy Policy