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

commonMain.it.unibo.tuprolog.solve.Signature.kt Maven / Gradle / Ivy

package it.unibo.tuprolog.solve

import it.unibo.tuprolog.core.Atom
import it.unibo.tuprolog.core.Indicator
import it.unibo.tuprolog.core.Integer
import it.unibo.tuprolog.core.Struct
import it.unibo.tuprolog.core.Term
import it.unibo.tuprolog.core.ToTermConvertible
import kotlin.js.JsName

/** The signature of a query Struct or a Primitive */
data class Signature(
    @JsName("name")
    val name: String,
    @JsName("arity")
    val arity: Int,
    @JsName("vararg")
    val vararg: Boolean = false
) : ToTermConvertible {

    init {
        require(arity >= 0) { "Signature arity should be greater than or equals to 0: $arity" }
    }

    /** Converts this signature to a Struct `'/'([name], [arity])` or `'/'([name],'+'([arity], vararg))` */
    override fun toTerm(): Struct =
        when {
            vararg -> {
                Struct.of(
                    FUNCTOR,
                    Atom.of(name),
                    Struct.of(
                        varargStructFunctor,
                        Integer.of(arity),
                        varargAtom
                    )
                )
            }
            else -> {
                Struct.of(FUNCTOR, Atom.of(name), Integer.of(arity))
            }
        }

    /** Converts this Signature to [Indicator], if possible without loosing information, otherwise throws an exception */
    @JsName("toIndicator")
    fun toIndicator(): Indicator =
        when {
            this.vararg -> TODO("Implement conversion to indicator in case of vararg signature")
            else -> Indicator.of(this.name, this.arity)
        }

    /** Creates corresponding Struct of this Signature with provided arguments */
    @JsName("withArgs")
    infix fun withArgs(arguments: Iterable): Struct = when {
        vararg -> require(arguments.count() >= this.arity) {
            "Trying to create Struct of signature `$this` with not enough arguments ${arguments.toList()}"
        }
        else -> require(arguments.count() == this.arity) {
            "Trying to create Struct of signature `$this` with wrong number of arguments ${arguments.toList()}"
        }
    }.let { Struct.of(name, arguments.asSequence()) }

    // TODO: 24/09/2019 maybe, to fully support vararg signatures, a method to check if a non vararg signature can be treated as another vararg one, should be added
    // for example: if user query is `ciao(1, 2)` with Signature("ciao", 2, false), it should be matched with
    // all signatures having same functor, vararg flag set to true and *less or equals* arity

    // a concept of precedence/inclusion should be enforced; maybe "non vararg exact match and vararg greater arity match" should win,
    // in this matching system
    // the method doing this could be called "includedBy(Signature)"

    // a method to retrieve matching vararg Signatures should also be added to [Library] interface, and return only one result;
    // this method should use "includedBy(Signature)" to retrieve matches and the sort them with the rule above

    companion object {

        /** An atom to denote vararg presence */
        private val varargAtom = Atom.of("vararg")
        private const val varargStructFunctor = "+"

        /** The functor of a Signature struct */
        const val FUNCTOR = Indicator.FUNCTOR

        /** Creates a Signature instance from a well-formed Signature Struct, or returns `null` if it cannot be interpreted as Signature */
        @JsName("fromSignatureStruct")
        fun fromSignatureTerm(term: Struct): Signature? = try {
            with(term) {
                when {
                    functor == FUNCTOR && arity == 2 && args.first().isAtom -> when {
                        args.last().isInt -> {
                            Signature(
                                args.first().`as`().value,
                                args.last().`as`().intValue.toInt()
                            )
                        }
                        args.last().let {
                            it is Struct && it.functor == varargStructFunctor && it.arity == 2 &&
                                it.args.first().isInt &&
                                it.args.last() == varargAtom
                        } -> {
                            Signature(
                                args.first().`as`().value,
                                args.last().`as`()[0].`as`().intValue.toInt(),
                                true
                            )
                        }
                        else -> null
                    }
                    else -> null
                }
            }
        } catch (ex: IllegalArgumentException) { // caught when parsed arity is negative
            null
        }

        /** Creates a Signature instance from a well-formed Signature Term, or returns `null` if it cannot be interpreted as Signature */
        @JsName("fromSignatureTerm")
        fun fromSignatureTerm(term: Term): Signature? = when (term) {
            is Struct -> fromSignatureTerm(term)
            else -> null
        }

        /** Creates a Signature instance from a well-formed Indicator, or returns `null` if it wasn't */
        @JsName("fromIndicator")
        fun fromIndicator(indicator: Indicator): Signature? = when {
            indicator.isWellFormed -> {
                Signature(
                    indicator.indicatedName!!,
                    indicator.indicatedArity!!
                )
            }
            else -> null
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy