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

com.strumenta.kolasu.semantics.scope.description.ScopeDescription.kt Maven / Gradle / Ivy

package com.strumenta.kolasu.semantics.scope.description

import com.strumenta.kolasu.model.Node
import com.strumenta.kolasu.model.PossiblyNamed
import com.strumenta.kolasu.model.ReferenceByName
import com.strumenta.kolasu.semantics.symbol.description.SymbolDescription

/**
 * Utility function for defining scope descriptions.
 **/
fun scope(
    ignoreCase: Boolean = false,
    init: ScopeDescriptionApi.() -> Unit
): ScopeDescription = ScopeDescription(ignoreCase).apply(init)

/**
 * A scope description containing all relevant information
 * concerning the visible elements at a given point in space or time.
 * - `ignoreCase`: whether the names associated to the symbols
 * in this scope should be handled as case-sensitive or not;
 * - `nodes`: the local symbols contained in this scope and
 * their associated name;
 * - `identifiers`: the external symbols contained in this scope
 * and their associated name;
 * - `parent`: the scope description *preceding* the current one - used
 * as delegate if no symbol can be found for a given name in the current scope;
 *
 * Each scope description can be configured through the methods defined in
 * the `ScopeDescriptionApi`. References can be resolved invoking `resolve(reference)`.
 *
 * Given a reference, the scope provider will follow the following plan:
 * - try to retrieve a local symbol associated with the given reference name;
 * - if not found, try to retrieve a global symbol associated with the given reference name;
 * - if not found, delegate the resolution to its parent (if any)
 *
 **/
class ScopeDescription(
    private val ignoreCase: Boolean = false
) : ScopeDescriptionApi {
    private var parent: ScopeDescription? = null
    private val namesToExternalSymbolIdentifiers: MutableMap = mutableMapOf()
    private val namesToLocalSymbolNodes: MutableMap = mutableMapOf()

    /**
     * Resolves the given reference in the current scope (or its parents).
     **/
    fun resolve(reference: ReferenceByName) {
        val name = reference.name.asKey()
        val node by lazy { this.namesToLocalSymbolNodes[name] }
        val identifier by lazy { this.namesToExternalSymbolIdentifiers[name] }
        when {
            node != null -> {
                @Suppress("UNCHECKED_CAST")
                (reference as ReferenceByName).referred = node
            }
            identifier != null -> {
                reference.identifier = identifier
            }
            else -> {
                this.parent?.resolve(reference)
            }
        }
    }

    override fun define(
        name: String,
        symbol: Node
    ) {
        when (symbol) {
            is SymbolDescription -> this.namesToExternalSymbolIdentifiers[name.asKey()] = symbol.identifier
            is PossiblyNamed -> this.namesToLocalSymbolNodes[name.asKey()] = symbol
        }
    }

    override fun define(symbol: PossiblyNamed) {
        if (symbol is Node && symbol.name != null) {
            this.define(symbol.name!!, symbol)
        } else {
            throw RuntimeException("Symbols must be SymbolDescription or Node instances with a non-null name property.")
        }
    }

    override fun parent(parent: ScopeDescription) {
        this.parent = parent
    }

    override fun parent(
        ignoreCase: Boolean,
        init: ScopeDescriptionApi.() -> Unit
    ) {
        this.parent = scope(ignoreCase, init)
    }

    /**
     * Handle case sensitivity if enabled.
     **/
    private fun String.asKey(): String {
        return if (ignoreCase) this.lowercase() else this
    }
}

/**
 * Interface for defining scope descriptions. It provides
 * the following methods:
 * - `define(name, symbol)` - to associate the given local or global symbol with the given name;
 * - `parent(parent)` - to update the parent scope from another description;
 * - `parent(ignoreCase, init)` - to update the parent scope from a literal description;
 **/
interface ScopeDescriptionApi {
    /**
     * Associates the given symbol with the given name.
     **/
    fun define(
        name: String,
        symbol: Node
    )

    /**
     * Associates the given symbol with its name.
     *
     **/
    fun define(symbol: PossiblyNamed)

    /**
     * Updates the parent of this scope from another description.
     **/
    fun parent(parent: ScopeDescription)

    /**
     * Updates the parent of this scope from a literal description.
     **/
    fun parent(
        ignoreCase: Boolean = false,
        init: ScopeDescriptionApi.() -> Unit
    )
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy