From 82ec8c687b9d9b273345668a5c705e84bfcfc32d Mon Sep 17 00:00:00 2001 From: Giuseppe Pisano Date: Fri, 12 Jun 2020 09:48:03 +0200 Subject: [PATCH 01/14] Built-ins functor/3 and =../2 implementation --- .../tuprolog/solve/stdlib/CommonBuiltins.kt | 2 ++ .../tuprolog/solve/stdlib/primitive/Univ.kt | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/primitive/Univ.kt diff --git a/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/CommonBuiltins.kt b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/CommonBuiltins.kt index 4f3420434..90a774275 100644 --- a/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/CommonBuiltins.kt +++ b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/CommonBuiltins.kt @@ -29,6 +29,7 @@ object CommonBuiltins : AliasedLibrary by Library.of( EnsureExecutable, FindAll, FloatPrimitive, + Functor, Ground, Halt, Integer, @@ -43,6 +44,7 @@ object CommonBuiltins : AliasedLibrary by Library.of( TermIdentical, TermNotIdentical, UnifiesWith, + Univ, Var, Write ).map { it.descriptionPair }.toMap(), diff --git a/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/primitive/Univ.kt b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/primitive/Univ.kt new file mode 100644 index 000000000..f04edd12a --- /dev/null +++ b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/primitive/Univ.kt @@ -0,0 +1,30 @@ +package it.unibo.tuprolog.solve.stdlib.primitive + +import it.unibo.tuprolog.core.Struct +import it.unibo.tuprolog.core.Substitution +import it.unibo.tuprolog.core.Var +import it.unibo.tuprolog.core.List +import it.unibo.tuprolog.core.Atom +import it.unibo.tuprolog.solve.ExecutionContext +import it.unibo.tuprolog.solve.Solve + +/** + * Implementation of '=..'/2 predicate + */ +object Univ : TermRelation("=..") { + override fun Solve.Request.computeSingleResponse(): Solve.Response { + val functor = arguments[0] + val list = arguments[1] + if (functor is Struct && list is Var) + return replySuccess(Substitution.of(list, + List.of(listOf(Atom.of(functor.functor)) + functor.argsList))) + if (functor is Var && list is List && list.size == 0) + return replyFail() + if (functor is Var && list is List) + return replySuccess(Substitution.of(functor, + Struct.of( + (list.toList().take(1).first() as Atom).value, + list.toList().drop(1)))) + return replyFail() + } +} From e01180deca8c00dddf11f50421c66b5b92388b26 Mon Sep 17 00:00:00 2001 From: Giuseppe Pisano Date: Fri, 12 Jun 2020 09:48:24 +0200 Subject: [PATCH 02/14] Built-ins functor/3 and =../2 implementation --- .../solve/stdlib/primitive/Functor.kt | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/primitive/Functor.kt diff --git a/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/primitive/Functor.kt b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/primitive/Functor.kt new file mode 100644 index 000000000..04ab0c496 --- /dev/null +++ b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/primitive/Functor.kt @@ -0,0 +1,40 @@ +package it.unibo.tuprolog.solve.stdlib.primitive + +import it.unibo.tuprolog.core.* +import it.unibo.tuprolog.core.Atom +import it.unibo.tuprolog.core.Substitution +import it.unibo.tuprolog.core.Integer +import it.unibo.tuprolog.core.Var +import it.unibo.tuprolog.solve.ExecutionContext +import it.unibo.tuprolog.solve.Solve +import it.unibo.tuprolog.solve.exception.error.InstantiationError + +/** + * Implementation of 'functor'/3 predicate + */ +object Functor : NonBacktrackableTernaryRelation("functor") { + override fun Solve.Request.computeOne(x: Term, y: Term, z: Term): Solve.Response { + + if (x is Struct && y is Atom && z is Integer) + return replyWith(y.value == x.functor && z.intValue.toInt() == x.arity) + if (x is Struct && y is Var && z is Var) + return replySuccess(Substitution.of( + y to Atom.of(x.functor), + z to Integer.of(x.arity)) as Substitution.Unifier) + if (x is Struct && y is Var && z is Integer && z.intValue.toInt() != x.arity) + return replyFail() + if (x is Struct && y is Var && z is Integer) + return replySuccess(Substitution.of(y, Atom.of(x.functor))) + if (x is Struct && y is Atom && z is Var && y.value != x.functor) + return replyFail() + if (x is Struct && y is Atom && z is Var) + return replySuccess(Substitution.of(z, Integer.of(x.arity))) + if (x is Var && y is Atom && z is Integer) + return replySuccess(Substitution.of(x, createFunctor(y.value, z.intValue.toInt()))) + + return replyException(InstantiationError.forArgument(context, signature)) + } + + private fun createFunctor(name: String, arity: Int) : Struct = + Struct.of(name, (1..arity).map { Var.of("X") }) +} \ No newline at end of file From 27d8537f7adf15c534bd1390ed6be9a9987ca949 Mon Sep 17 00:00:00 2001 From: Giovanni Ciatto Date: Fri, 12 Jun 2020 10:49:11 +0200 Subject: [PATCH 03/14] improve implementation of functor/3 and =../2 --- .../solve/stdlib/primitive/Functor.kt | 103 +++++++++++++----- .../tuprolog/solve/stdlib/primitive/Univ.kt | 73 +++++++++---- 2 files changed, 132 insertions(+), 44 deletions(-) diff --git a/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/primitive/Functor.kt b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/primitive/Functor.kt index 04ab0c496..b9ce06b1c 100644 --- a/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/primitive/Functor.kt +++ b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/primitive/Functor.kt @@ -2,39 +2,92 @@ package it.unibo.tuprolog.solve.stdlib.primitive import it.unibo.tuprolog.core.* import it.unibo.tuprolog.core.Atom -import it.unibo.tuprolog.core.Substitution import it.unibo.tuprolog.core.Integer import it.unibo.tuprolog.core.Var import it.unibo.tuprolog.solve.ExecutionContext import it.unibo.tuprolog.solve.Solve import it.unibo.tuprolog.solve.exception.error.InstantiationError +import it.unibo.tuprolog.solve.exception.error.TypeError +import it.unibo.tuprolog.solve.primitive.TernaryRelation /** * Implementation of 'functor'/3 predicate */ -object Functor : NonBacktrackableTernaryRelation("functor") { - override fun Solve.Request.computeOne(x: Term, y: Term, z: Term): Solve.Response { - - if (x is Struct && y is Atom && z is Integer) - return replyWith(y.value == x.functor && z.intValue.toInt() == x.arity) - if (x is Struct && y is Var && z is Var) - return replySuccess(Substitution.of( - y to Atom.of(x.functor), - z to Integer.of(x.arity)) as Substitution.Unifier) - if (x is Struct && y is Var && z is Integer && z.intValue.toInt() != x.arity) - return replyFail() - if (x is Struct && y is Var && z is Integer) - return replySuccess(Substitution.of(y, Atom.of(x.functor))) - if (x is Struct && y is Atom && z is Var && y.value != x.functor) - return replyFail() - if (x is Struct && y is Atom && z is Var) - return replySuccess(Substitution.of(z, Integer.of(x.arity))) - if (x is Var && y is Atom && z is Integer) - return replySuccess(Substitution.of(x, createFunctor(y.value, z.intValue.toInt()))) - - return replyException(InstantiationError.forArgument(context, signature)) +object Functor : TernaryRelation.Functional("functor") { + override fun Solve.Request.computeOneSubstitution( + first: Term, + second: Term, + third: Term + ): Substitution = when (first) { + is Struct -> { + when (second) { + is Atom -> { + when (third) { + is Numeric -> { + if (second.value == first.functor && third.intValue.toInt() == first.arity) + Substitution.empty() + else + Substitution.failed() + } + is Var -> { + Substitution.of(third to Integer.of(first.arity)) + } + else -> { + // TODO expected here should be INTEGER | VARIABLE + throw TypeError.forArgument(context, signature, TypeError.Expected.INTEGER, third, 2) + } + } + } + is Var -> { + when (third) { + is Numeric -> { + Substitution.of(second to Atom.of(first.functor)) + } + is Var -> { + Substitution.of( + second to Atom.of(first.functor), + third to Integer.of(first.arity) + ) + } + else -> { + // TODO expected here should be INTEGER | VARIABLE + throw TypeError.forArgument(context, signature, TypeError.Expected.INTEGER, third, 2) + } + } + } + else -> { + // TODO expected here should be ATOM | VARIABLE + throw TypeError.forArgument(context, signature, TypeError.Expected.ATOM, second, 1) + } + } + } + is Var -> { + when (second) { + is Atom -> { + when (third) { + is Numeric -> { + Substitution.of(first to Struct.template(second.value, third.intValue.toInt())) + } + is Var -> { + throw InstantiationError.forArgument(context, signature, 2, third) + } + else -> { + // TODO expected here should be INTEGER | VARIABLE + throw TypeError.forArgument(context, signature, TypeError.Expected.INTEGER, third, 2) + } + } + } + is Var -> { + throw InstantiationError.forArgument(context, signature, 1, second) + } + else -> { + // TODO expected here should be ATOM | VARIABLE + throw TypeError.forArgument(context, signature, TypeError.Expected.ATOM, second, 1) + } + } + } + else -> { + throw TypeError.forArgument(context, signature, TypeError.Expected.CALLABLE, first, 0) + } } - - private fun createFunctor(name: String, arity: Int) : Struct = - Struct.of(name, (1..arity).map { Var.of("X") }) } \ No newline at end of file diff --git a/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/primitive/Univ.kt b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/primitive/Univ.kt index f04edd12a..3b5b39074 100644 --- a/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/primitive/Univ.kt +++ b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/primitive/Univ.kt @@ -1,30 +1,65 @@ package it.unibo.tuprolog.solve.stdlib.primitive -import it.unibo.tuprolog.core.Struct -import it.unibo.tuprolog.core.Substitution -import it.unibo.tuprolog.core.Var -import it.unibo.tuprolog.core.List +import it.unibo.tuprolog.core.* import it.unibo.tuprolog.core.Atom +import it.unibo.tuprolog.core.Var import it.unibo.tuprolog.solve.ExecutionContext import it.unibo.tuprolog.solve.Solve +import it.unibo.tuprolog.solve.exception.error.InstantiationError +import it.unibo.tuprolog.solve.exception.error.TypeError +import it.unibo.tuprolog.solve.primitive.BinaryRelation +import it.unibo.tuprolog.unify.Unificator.Companion.mguWith +import it.unibo.tuprolog.core.List as LogicList +import kotlin.collections.listOf as ktListOf /** * Implementation of '=..'/2 predicate */ -object Univ : TermRelation("=..") { - override fun Solve.Request.computeSingleResponse(): Solve.Response { - val functor = arguments[0] - val list = arguments[1] - if (functor is Struct && list is Var) - return replySuccess(Substitution.of(list, - List.of(listOf(Atom.of(functor.functor)) + functor.argsList))) - if (functor is Var && list is List && list.size == 0) - return replyFail() - if (functor is Var && list is List) - return replySuccess(Substitution.of(functor, - Struct.of( - (list.toList().take(1).first() as Atom).value, - list.toList().drop(1)))) - return replyFail() +object Univ : BinaryRelation.Functional("=..") { + private fun univ(first: Term, second: it.unibo.tuprolog.core.List): Substitution { + val list = second.toList() + return if (list.isNotEmpty() && list[0] is Atom) { + first mguWith Struct.of(list[0].castTo().value, list.subList(1, list.lastIndex)) + } else { + Substitution.failed() + } + } + + override fun Solve.Request.computeOneSubstitution(first: Term, second: Term): Substitution { + return when (first) { + is Struct -> { + when (second) { + is LogicList -> { + univ(first, second) + } + is Var -> { + second mguWith LogicList.of( + ktListOf(Atom.of(first.functor)) + first.argsList + ) + } + else -> { + // TODO expected here should be LIST | VARIABLE or something like that + throw TypeError.forArgument(context, signature, TypeError.Expected.LIST, second, 1) + } + } + } + is Var -> { + when (second) { + is LogicList -> { + univ(first, second) + } + is Var -> { + throw InstantiationError.forArgument(context, signature, 0, first) + } + else -> { + // TODO expected here should be LIST | VARIABLE or something like that + throw TypeError.forArgument(context, signature, TypeError.Expected.LIST, second, 1) + } + } + } + else -> { + throw TypeError.forArgument(context, signature, TypeError.Expected.CALLABLE, second, 0) + } + } } } From b38867b369b1c8da45a114c18af38467b8138a5e Mon Sep 17 00:00:00 2001 From: Giovanni Ciatto Date: Fri, 12 Jun 2020 12:53:28 +0200 Subject: [PATCH 04/14] fix DynamicOpListener in JVM --- .../tuprolog/core/parsing/Conversions.kt | 26 +++++++++ .../core/parsing/DynamicOpListener.kt | 58 ++++++++++--------- .../core/parsing/PrologParserFactory.kt | 5 +- 3 files changed, 58 insertions(+), 31 deletions(-) create mode 100644 parser-core/src/jvmMain/kotlin/it/unibo/tuprolog/core/parsing/Conversions.kt diff --git a/parser-core/src/jvmMain/kotlin/it/unibo/tuprolog/core/parsing/Conversions.kt b/parser-core/src/jvmMain/kotlin/it/unibo/tuprolog/core/parsing/Conversions.kt new file mode 100644 index 000000000..0b1262e76 --- /dev/null +++ b/parser-core/src/jvmMain/kotlin/it/unibo/tuprolog/core/parsing/Conversions.kt @@ -0,0 +1,26 @@ +package it.unibo.tuprolog.core.parsing + +import it.unibo.tuprolog.core.operators.Specifier +import it.unibo.tuprolog.parser.dynamic.Associativity + +fun Associativity.toSpecifier(): Specifier = + when (this) { + Associativity.FX -> Specifier.FX + Associativity.FY -> Specifier.FY + Associativity.XF -> Specifier.XF + Associativity.YF -> Specifier.YF + Associativity.XFX -> Specifier.XFX + Associativity.YFX -> Specifier.YFX + Associativity.XFY -> Specifier.XFY + } + +fun Specifier.toAssociativity(): Associativity = + when (this) { + Specifier.FX -> Associativity.FX + Specifier.FY -> Associativity.FY + Specifier.XF -> Associativity.XF + Specifier.YF -> Associativity.YF + Specifier.XFX -> Associativity.XFX + Specifier.YFX -> Associativity.YFX + Specifier.XFY -> Associativity.XFY + } \ No newline at end of file diff --git a/parser-core/src/jvmMain/kotlin/it/unibo/tuprolog/core/parsing/DynamicOpListener.kt b/parser-core/src/jvmMain/kotlin/it/unibo/tuprolog/core/parsing/DynamicOpListener.kt index ea072f795..16ae9c384 100644 --- a/parser-core/src/jvmMain/kotlin/it/unibo/tuprolog/core/parsing/DynamicOpListener.kt +++ b/parser-core/src/jvmMain/kotlin/it/unibo/tuprolog/core/parsing/DynamicOpListener.kt @@ -1,9 +1,10 @@ package it.unibo.tuprolog.core.parsing +import it.unibo.tuprolog.core.Atom +import it.unibo.tuprolog.core.Directive import it.unibo.tuprolog.core.Numeric import it.unibo.tuprolog.core.Struct import it.unibo.tuprolog.core.operators.Operator -import it.unibo.tuprolog.core.operators.OperatorSet import it.unibo.tuprolog.core.operators.Specifier import it.unibo.tuprolog.parser.PrologParser import it.unibo.tuprolog.parser.PrologParserBaseListener @@ -11,10 +12,11 @@ import it.unibo.tuprolog.parser.dynamic.Associativity import java.lang.ref.WeakReference import kotlin.math.max import kotlin.math.min +import it.unibo.tuprolog.core.List as LogicList class DynamicOpListener private constructor( parser: PrologParser, - private val operatorDefinedCallback: (Operator) -> OperatorSet + private val operatorDefinedCallback: PrologParser?.(Operator) -> Unit ) : PrologParserBaseListener() { private val parser: WeakReference = WeakReference(parser) @@ -24,45 +26,45 @@ class DynamicOpListener private constructor( if (ctx.exception != null) { return } - if (expr.op != null && ":-" == expr.op.symbol.text && Associativity.PREFIX.contains(expr.associativity)) { - val directive = ctx.accept(PrologExpressionVisitor()) as Struct - if (directive.arity == 1 && directive.getArgAt(0) is Struct) { - val op: Struct = directive.getArgAt(0) as Struct - if ("op" == op.functor && op.arity == 3 && op.getArgAt(0) is Number && op.getArgAt(1).isAtom && op.getArgAt( - 2 - ).isAtom - ) { - val number = op.getArgAt(0) as Numeric - val priority: Int = min( - PrologParser.TOP, - max( - PrologParser.BOTTOM, - number.intValue.toInt() - ) + if (expr.op != null && ":-" == expr.op.symbol.text && expr.associativity in Associativity.PREFIX) { + val directive = ctx.accept(PrologExpressionVisitor()) as Directive + val op = directive.body + if (op is Struct && op.arity == 3 && op.functor == "op" && op[0] is Numeric && op[1] is Atom && op.isGround) { + val priority = min( + PrologParser.TOP, + max( + PrologParser.BOTTOM, + (op[0] as Numeric).intValue.toInt() ) - val struct1 = op.getArgAt(1) as Struct - val associativity: Associativity = - Associativity.valueOf(struct1.functor.toUpperCase()) - val struct2 = op.getArgAt(2) as Struct - val functor: String = struct2.functor - (parser.get()) - ?.addOperator(functor, associativity, priority) - onOperatorDefined(Operator(functor, Specifier.valueOf(associativity.name), priority)) + ) + val specifier = Specifier.fromTerm(op[1]) + when (val operator = op[2]) { + is Atom -> { + onOperatorDefined(Operator(operator.value, specifier, priority)) + } + is LogicList -> { + if (operator.toSequence().all { it is Atom }) { + for (it in operator.toSequence().map { it as Atom }) { + onOperatorDefined(Operator(it.value, specifier, priority)) + } + } + } } } } } private fun onOperatorDefined(operator: Operator) { - operatorDefinedCallback.invoke(operator) + parser.get()?.addOperator(operator.functor, operator.specifier.toAssociativity(), operator.priority) + parser.get().operatorDefinedCallback(operator) } companion object { fun of(parser: PrologParser): DynamicOpListener { - return DynamicOpListener(parser) { OperatorSet.EMPTY } + return DynamicOpListener(parser) { } } - fun of(parser: PrologParser, operatorDefinedCallback: (Operator) -> OperatorSet): DynamicOpListener { + fun of(parser: PrologParser, operatorDefinedCallback: PrologParser?.(Operator) -> Unit): DynamicOpListener { return DynamicOpListener(parser, operatorDefinedCallback) } } diff --git a/parser-core/src/jvmMain/kotlin/it/unibo/tuprolog/core/parsing/PrologParserFactory.kt b/parser-core/src/jvmMain/kotlin/it/unibo/tuprolog/core/parsing/PrologParserFactory.kt index c076ac741..11b4b43ff 100644 --- a/parser-core/src/jvmMain/kotlin/it/unibo/tuprolog/core/parsing/PrologParserFactory.kt +++ b/parser-core/src/jvmMain/kotlin/it/unibo/tuprolog/core/parsing/PrologParserFactory.kt @@ -3,7 +3,6 @@ package it.unibo.tuprolog.core.parsing import it.unibo.tuprolog.core.operators.OperatorSet import it.unibo.tuprolog.parser.PrologLexer import it.unibo.tuprolog.parser.PrologParser -import it.unibo.tuprolog.parser.dynamic.Associativity import org.antlr.v4.runtime.* import org.antlr.v4.runtime.atn.PredictionMode import org.antlr.v4.runtime.misc.ParseCancellationException @@ -189,9 +188,9 @@ object PrologParserFactory { fun addOperators(prologParser: PrologParser, operators: OperatorSet): PrologParser { operators.forEach { - prologParser.addOperator(it.functor, Associativity.valueOf(it.specifier.name), it.priority) + prologParser.addOperator(it.functor, it.specifier.toAssociativity(), it.priority) } -// prologParser.addParseListener(DynamicOpListener.of(prologParser, OperatorSet()::plus)) + prologParser.addParseListener(DynamicOpListener.of(prologParser)) return prologParser } From 538c7b2fdc11905d5a31457d757fd1639770dd91 Mon Sep 17 00:00:00 2001 From: Giovanni Ciatto Date: Fri, 12 Jun 2020 16:31:13 +0200 Subject: [PATCH 05/14] add op/3 support in js as well --- .../tuprolog/core/parsing/Conversions.kt | 9 +++ .../core/parsing/DynamicOpListener.kt | 64 ++++++++++++++++++ .../core/parsing/PrologParserFactory.kt | 18 +---- parser-js/build.gradle.kts | 2 +- .../kotlin/it/unibo/tuprolog/parser/Antlr4.kt | 2 + .../it/unibo/tuprolog/parser/PrologParser.kt | 3 +- .../tuprolog/parser/PrologParserListener.kt | 40 ++++++++++++ .../theory/parsing/TestClausesParser.kt | 65 +++++++++++++++++++ 8 files changed, 186 insertions(+), 17 deletions(-) create mode 100644 parser-core/src/jsMain/kotlin/it/unibo/tuprolog/core/parsing/Conversions.kt create mode 100644 parser-core/src/jsMain/kotlin/it/unibo/tuprolog/core/parsing/DynamicOpListener.kt create mode 100644 parser-js/src/main/kotlin/it/unibo/tuprolog/parser/PrologParserListener.kt diff --git a/parser-core/src/jsMain/kotlin/it/unibo/tuprolog/core/parsing/Conversions.kt b/parser-core/src/jsMain/kotlin/it/unibo/tuprolog/core/parsing/Conversions.kt new file mode 100644 index 000000000..f2bbee745 --- /dev/null +++ b/parser-core/src/jsMain/kotlin/it/unibo/tuprolog/core/parsing/Conversions.kt @@ -0,0 +1,9 @@ +package it.unibo.tuprolog.core.parsing + +import it.unibo.tuprolog.core.operators.Specifier + +fun String.toSpecifier(): Specifier = + Specifier.valueOf(toUpperCase()) + +fun Specifier.toAssociativity(): String = + toString() \ No newline at end of file diff --git a/parser-core/src/jsMain/kotlin/it/unibo/tuprolog/core/parsing/DynamicOpListener.kt b/parser-core/src/jsMain/kotlin/it/unibo/tuprolog/core/parsing/DynamicOpListener.kt new file mode 100644 index 000000000..cb127674c --- /dev/null +++ b/parser-core/src/jsMain/kotlin/it/unibo/tuprolog/core/parsing/DynamicOpListener.kt @@ -0,0 +1,64 @@ +package it.unibo.tuprolog.core.parsing + +import it.unibo.tuprolog.core.Atom +import it.unibo.tuprolog.core.Directive +import it.unibo.tuprolog.core.Numeric +import it.unibo.tuprolog.core.Struct +import it.unibo.tuprolog.core.operators.Operator +import it.unibo.tuprolog.core.operators.Specifier +import it.unibo.tuprolog.parser.Associativity +import it.unibo.tuprolog.parser.ClauseContext +import it.unibo.tuprolog.parser.PrologParser +import it.unibo.tuprolog.parser.PrologParserListener +import kotlin.math.max +import kotlin.math.min +import it.unibo.tuprolog.core.List as LogicList + +class DynamicOpListener private constructor( + private val parser: PrologParser, + private val operatorDefinedCallback: PrologParser?.(Operator) -> Unit +) : PrologParserListener() { + + override fun exitClause(ctx: ClauseContext) { + val expr = ctx.expression() + if (ctx.exception != null) { + return + } + if (expr._op != null && ":-" == expr._op?.symbol?.text && expr.associativity in Associativity.PREFIX) { + val directive = ctx.accept(PrologVisitor()) as Directive + val op = directive.body + if (op is Struct && op.arity == 3 && op.functor == "op" && op[0] is Numeric && op[1] is Atom && op.isGround) { + val priority = min(1200, max(0, (op[0] as Numeric).intValue.toInt())) + val specifier = Specifier.fromTerm(op[1]) + when (val operator = op[2]) { + is Atom -> { + onOperatorDefined(Operator(operator.value, specifier, priority)) + } + is LogicList -> { + if (operator.toSequence().all { it is Atom }) { + for (it in operator.toSequence().map { it as Atom }) { + onOperatorDefined(Operator(it.value, specifier, priority)) + } + } + } + } + } + } + } + + private fun onOperatorDefined(operator: Operator) { + parser.addOperator(operator.functor, operator.specifier.toAssociativity(), operator.priority) + parser.operatorDefinedCallback(operator) + } + + companion object { + fun of(parser: PrologParser): DynamicOpListener { + return DynamicOpListener(parser) { } + } + + fun of(parser: PrologParser, operatorDefinedCallback: PrologParser?.(Operator) -> Unit): DynamicOpListener { + return DynamicOpListener(parser, operatorDefinedCallback) + } + } + +} \ No newline at end of file diff --git a/parser-core/src/jsMain/kotlin/it/unibo/tuprolog/core/parsing/PrologParserFactory.kt b/parser-core/src/jsMain/kotlin/it/unibo/tuprolog/core/parsing/PrologParserFactory.kt index 222fe77aa..98b07b58a 100644 --- a/parser-core/src/jsMain/kotlin/it/unibo/tuprolog/core/parsing/PrologParserFactory.kt +++ b/parser-core/src/jsMain/kotlin/it/unibo/tuprolog/core/parsing/PrologParserFactory.kt @@ -107,23 +107,11 @@ object PrologParserFactory { return addOperators(parser, operators) to originalErrorStrategy } - fun addOperators(prologParser: PrologParser, operators: OperatorSet): PrologParser { - val ops = mutableListOf() - for (it in operators) { - val op = when (it.specifier.name.toUpperCase()) { - "FX" -> Associativity.FX - "FY" -> Associativity.FY - "YF" -> Associativity.YF - "YFX" -> Associativity.YFX - "XFY" -> Associativity.XFY - "XF" -> Associativity.XF - "XFX" -> Associativity.XFX - else -> Associativity.YFX - } - ops.add(it.functor) - prologParser.addOperator(it.functor, op, it.priority) + operators.forEach { + prologParser.addOperator(it.functor, it.specifier.toAssociativity(), it.priority) } + prologParser.addParseListener(DynamicOpListener.of(prologParser)) return prologParser } diff --git a/parser-js/build.gradle.kts b/parser-js/build.gradle.kts index ab2407158..82b0b9b33 100644 --- a/parser-js/build.gradle.kts +++ b/parser-js/build.gradle.kts @@ -96,7 +96,7 @@ with(fileTree("src/main/antlr")) { "-o", outputDir, "-message-format", "antlr", "-long-messages", - "-no-listener", + "-listener", "-visitor", "-package", "${rootProject.group}.parsing", antlrFile.absolutePath diff --git a/parser-js/src/main/kotlin/it/unibo/tuprolog/parser/Antlr4.kt b/parser-js/src/main/kotlin/it/unibo/tuprolog/parser/Antlr4.kt index 544fdb4e5..55a42f17c 100644 --- a/parser-js/src/main/kotlin/it/unibo/tuprolog/parser/Antlr4.kt +++ b/parser-js/src/main/kotlin/it/unibo/tuprolog/parser/Antlr4.kt @@ -124,4 +124,6 @@ open external class ParserRuleContext(parent: dynamic, invokingStateNumber: Int) val start: Token? val stop: Token? + + val exception: dynamic } \ No newline at end of file diff --git a/parser-js/src/main/kotlin/it/unibo/tuprolog/parser/PrologParser.kt b/parser-js/src/main/kotlin/it/unibo/tuprolog/parser/PrologParser.kt index fc849d2c7..e6eee74d1 100644 --- a/parser-js/src/main/kotlin/it/unibo/tuprolog/parser/PrologParser.kt +++ b/parser-js/src/main/kotlin/it/unibo/tuprolog/parser/PrologParser.kt @@ -59,6 +59,7 @@ external class PrologParser(input: TokenStream) { fun reset() + fun addParseListener(listener: PrologParserListener) fun addErrorListener(listener: dynamic) fun removeErrorListeners() @@ -112,7 +113,7 @@ external class ExpressionContext : ParserRuleContext { val associativity: String val bottom: Int val left: TermContext? - val _op: OpContext + val _op: OpContext? val operators: Array val _expression: ExpressionContext? val right: Array diff --git a/parser-js/src/main/kotlin/it/unibo/tuprolog/parser/PrologParserListener.kt b/parser-js/src/main/kotlin/it/unibo/tuprolog/parser/PrologParserListener.kt new file mode 100644 index 000000000..36d1dcb09 --- /dev/null +++ b/parser-js/src/main/kotlin/it/unibo/tuprolog/parser/PrologParserListener.kt @@ -0,0 +1,40 @@ +@file:JsModule("./PrologParserListener") +@file:JsNonModule + +package it.unibo.tuprolog.parser + +open external class PrologParserListener { + + open fun enterSingletonTerm(ctx: SingletonTermContext) + open fun exitSingletonTerm(ctx: SingletonTermContext) + open fun enterSingletonExpression(ctx: SingletonExpressionContext) + open fun exitSingletonExpression(ctx: SingletonExpressionContext) + open fun enterTheory(ctx: TheoryContext) + open fun exitTheory(ctx: TheoryContext) + open fun enterOptClause(ctx: OptClauseContext) + open fun exitOptClause(ctx: OptClauseContext) + open fun enterClause(ctx: ClauseContext) + open fun exitClause(ctx: ClauseContext) + open fun enterExpression(ctx: ExpressionContext) + open fun exitExpression(ctx: ExpressionContext) + open fun enterOuter(ctx: OuterContext) + open fun exitOuter(ctx: OuterContext) + open fun enterOp(ctx: OpContext) + open fun exitOp(ctx: OpContext) + open fun enterTerm(ctx: TermContext) + open fun exitTerm(ctx: TermContext) + open fun enterNumber(ctx: NumberContext) + open fun exitNumber(ctx: NumberContext) + open fun enterInteger(ctx: IntegerContext) + open fun exitInteger(ctx: IntegerContext) + open fun enterReal(ctx: RealContext) + open fun exitReal(ctx: RealContext) + open fun enterVariable(ctx: VariableContext) + open fun exitVariable(ctx: VariableContext) + open fun enterStructure(ctx: StructureContext) + open fun exitStructure(ctx: StructureContext) + open fun enterList(ctx: ListContext) + open fun exitList(ctx: ListContext) + open fun enterSet(ctx: SetContext) + open fun exitSet(ctx: SetContext) +} \ No newline at end of file diff --git a/parser-theory/src/commonTest/kotlin/it/unibo/tuprolog/theory/parsing/TestClausesParser.kt b/parser-theory/src/commonTest/kotlin/it/unibo/tuprolog/theory/parsing/TestClausesParser.kt index 97ed24d43..3ce2ff9fd 100644 --- a/parser-theory/src/commonTest/kotlin/it/unibo/tuprolog/theory/parsing/TestClausesParser.kt +++ b/parser-theory/src/commonTest/kotlin/it/unibo/tuprolog/theory/parsing/TestClausesParser.kt @@ -62,4 +62,69 @@ class TestClausesParser { fail("Unexpected exception of type ${e::class}: $e") } } + + @Test + fun testTheoryWithCustomOperator() { + val input = """ + |:- op(900, xfy, '::'). + |nil. + |1 :: nil. + |1 :: 2 :: nil. + """.trimMargin() + + val th = with(ClausesParser.withStandardOperators) { + parseTheory(input) + } + + assertMatch(th.elementAt(0)) { + directive { "op"(900, "xfy", "::") } + } + + assertMatch(th.elementAt(1)) { + fact { "nil" } + } + + assertMatch(th.elementAt(2)) { + fact { "::"(1, "nil") } + } + + assertMatch(th.elementAt(3)) { + fact { "::"(1, "::"(2, "nil")) } + } + } + + @Test + fun testTheoryWithCustomOperators() { + val input = """ + |:- op(900, yfx, ['++', '--']). + |1 ++ 2. + |1 -- 2. + |1 -- 2 ++ 3. + |1 ++ 2 -- 3 ++ 4. + """.trimMargin() + + val th = with(ClausesParser.withStandardOperators) { + parseTheory(input) + } + + assertMatch(th.elementAt(0)) { + directive { "op"(900, "yfx", listOf("++", "--")) } + } + + assertMatch(th.elementAt(1)) { + fact { "++"(1, 2) } + } + + assertMatch(th.elementAt(2)) { + fact { "--"(1, 2) } + } + + assertMatch(th.elementAt(3)) { + fact { "++"("--"(1, 2), 3) } + } + + assertMatch(th.elementAt(4)) { + fact { "++"("--"("++"(1, 2), 3), 4) } + } + } } \ No newline at end of file From c16ba2b52e0d2bb850369e0b260b16ae261dc76c Mon Sep 17 00:00:00 2001 From: Giovanni Ciatto Date: Fri, 12 Jun 2020 18:54:22 +0200 Subject: [PATCH 06/14] add support for operators table in Solver --- .../tuprolog/solve/ClassicExecutionContext.kt | 22 ++-- .../it/unibo/tuprolog/solve/ClassicSolver.kt | 9 +- .../solve/fsm/StatePrimitiveExecution.kt | 3 +- .../it/unibo/tuprolog/solve/StreamsSolver.kt | 4 + .../tuprolog/solve/solver/SolverUtils.kt | 5 +- .../solve/solver/StreamsExecutionContext.kt | 22 ++-- .../solve/solver/fsm/impl/StateEnd.kt | 14 ++- .../tuprolog/solve/ExecutionContextAware.kt | 5 + .../kotlin/it/unibo/tuprolog/solve/Solve.kt | 106 ++++++++++++++---- .../kotlin/it/unibo/tuprolog/solve/Utils.kt | 34 ++++++ 10 files changed, 175 insertions(+), 49 deletions(-) diff --git a/solve-classic/src/commonMain/kotlin/it/unibo/tuprolog/solve/ClassicExecutionContext.kt b/solve-classic/src/commonMain/kotlin/it/unibo/tuprolog/solve/ClassicExecutionContext.kt index 3ca2b4dff..a21e93f6b 100644 --- a/solve-classic/src/commonMain/kotlin/it/unibo/tuprolog/solve/ClassicExecutionContext.kt +++ b/solve-classic/src/commonMain/kotlin/it/unibo/tuprolog/solve/ClassicExecutionContext.kt @@ -1,6 +1,7 @@ package it.unibo.tuprolog.solve import it.unibo.tuprolog.core.* +import it.unibo.tuprolog.core.operators.OperatorSet import it.unibo.tuprolog.solve.channel.InputChannel import it.unibo.tuprolog.solve.channel.OutputChannel import it.unibo.tuprolog.solve.exception.PrologWarning @@ -16,6 +17,7 @@ data class ClassicExecutionContext( override val flags: PrologFlags = emptyMap(), override val staticKb: Theory = Theory.empty(), override val dynamicKb: Theory = Theory.empty(), + override val operators: OperatorSet = getAllOperators(libraries, staticKb, dynamicKb).toOperatorSet(), override val inputChannels: Map> = ExecutionContextAware.defaultInputChannels(), override val outputChannels: Map> = ExecutionContextAware.defaultOutputChannels(), override val substitution: Substitution.Unifier = Substitution.empty(), @@ -78,17 +80,15 @@ data class ClassicExecutionContext( stdOut: OutputChannel, stdErr: OutputChannel, warnings: OutputChannel - ): Solver { - return ClassicSolverFactory.solverOf( - libraries, - flags, - staticKb, - dynamicKb, - stdIn, - stdOut, - stdErr - ) - } + ): Solver = ClassicSolverFactory.solverOf( + libraries, + flags, + staticKb, + dynamicKb, + stdIn, + stdOut, + stdErr + ) override fun toString(): String { return "ClassicExecutionContext(" + diff --git a/solve-classic/src/commonMain/kotlin/it/unibo/tuprolog/solve/ClassicSolver.kt b/solve-classic/src/commonMain/kotlin/it/unibo/tuprolog/solve/ClassicSolver.kt index 868a0098e..9c2ba594f 100644 --- a/solve-classic/src/commonMain/kotlin/it/unibo/tuprolog/solve/ClassicSolver.kt +++ b/solve-classic/src/commonMain/kotlin/it/unibo/tuprolog/solve/ClassicSolver.kt @@ -1,9 +1,7 @@ package it.unibo.tuprolog.solve import it.unibo.tuprolog.core.Struct -import it.unibo.tuprolog.core.Substitution -import it.unibo.tuprolog.core.Var -import it.unibo.tuprolog.solve.fsm.EndState +import it.unibo.tuprolog.core.operators.OperatorSet import it.unibo.tuprolog.solve.fsm.State import it.unibo.tuprolog.solve.fsm.StateInit import it.unibo.tuprolog.solve.fsm.clone @@ -25,6 +23,7 @@ internal open class ClassicSolver( flags = flags, staticKb = staticKb, dynamicKb = dynamicKb, + operators = getAllOperators(libraries, staticKb, dynamicKb).toOperatorSet(), inputChannels = inputChannels, outputChannels = outputChannels ) @@ -45,6 +44,7 @@ internal open class ClassicSolver( flags = flags, staticKb = staticKb, dynamicKb = dynamicKb, + operators = operators, inputChannels = inputChannels, outputChannels = outputChannels, maxDuration = maxDuration, @@ -77,4 +77,7 @@ internal open class ClassicSolver( override val outputChannels: PrologOutputChannels<*> get() = state.context.outputChannels + + override val operators: OperatorSet + get() = state.context.operators } diff --git a/solve-classic/src/commonMain/kotlin/it/unibo/tuprolog/solve/fsm/StatePrimitiveExecution.kt b/solve-classic/src/commonMain/kotlin/it/unibo/tuprolog/solve/fsm/StatePrimitiveExecution.kt index 081a45d81..a005ce6d2 100644 --- a/solve-classic/src/commonMain/kotlin/it/unibo/tuprolog/solve/fsm/StatePrimitiveExecution.kt +++ b/solve-classic/src/commonMain/kotlin/it/unibo/tuprolog/solve/fsm/StatePrimitiveExecution.kt @@ -17,9 +17,10 @@ internal data class StatePrimitiveExecution(override val context: ClassicExecuti procedure = if (parentProcedure) parent?.procedure else procedure, primitives = Cursor.empty(), libraries = primitives.current?.libraries ?: libraries, + flags = primitives.current?.flags ?: flags, staticKb = primitives.current?.staticKb ?: staticKb, dynamicKb = primitives.current?.dynamicKb ?: dynamicKb, - flags = primitives.current?.flags ?: flags, + operators = primitives.current?.operators ?: operators, inputChannels = primitives.current?.inputChannels ?: inputChannels, outputChannels = primitives.current?.outputChannels ?: outputChannels, substitution = (substitution ?: this.substitution) as Substitution.Unifier, diff --git a/solve-streams/src/commonMain/kotlin/it/unibo/tuprolog/solve/StreamsSolver.kt b/solve-streams/src/commonMain/kotlin/it/unibo/tuprolog/solve/StreamsSolver.kt index 0b9e7bdb4..37d531535 100644 --- a/solve-streams/src/commonMain/kotlin/it/unibo/tuprolog/solve/StreamsSolver.kt +++ b/solve-streams/src/commonMain/kotlin/it/unibo/tuprolog/solve/StreamsSolver.kt @@ -2,6 +2,7 @@ package it.unibo.tuprolog.solve import it.unibo.tuprolog.core.Struct import it.unibo.tuprolog.core.Var +import it.unibo.tuprolog.core.operators.OperatorSet import it.unibo.tuprolog.solve.library.Libraries import it.unibo.tuprolog.solve.solver.StreamsExecutionContext import it.unibo.tuprolog.solve.solver.fsm.FinalState @@ -28,6 +29,7 @@ internal class StreamsSolver constructor( flags, staticKb, dynamicKb, + getAllOperators(libraries, staticKb, dynamicKb).toOperatorSet(), inputChannels, outputChannels ) @@ -73,6 +75,8 @@ internal class StreamsSolver constructor( override val outputChannels: PrologOutputChannels<*> get() = executionContext.outputChannels + override val operators: OperatorSet + get() = executionContext.operators internal companion object { diff --git a/solve-streams/src/commonMain/kotlin/it/unibo/tuprolog/solve/solver/SolverUtils.kt b/solve-streams/src/commonMain/kotlin/it/unibo/tuprolog/solve/solver/SolverUtils.kt index a56344e7a..330e018c4 100644 --- a/solve-streams/src/commonMain/kotlin/it/unibo/tuprolog/solve/solver/SolverUtils.kt +++ b/solve-streams/src/commonMain/kotlin/it/unibo/tuprolog/solve/solver/SolverUtils.kt @@ -92,7 +92,8 @@ internal fun Solve.Request.newSolveRequest( fun Solve.Request.replyWith(otherResponse: Solve.Response): Solve.Response = with(otherResponse) { replyWith( - solution, libraries, flags, staticKb, dynamicKb, sideEffectManager - ?: this@replyWith.context.getSideEffectManager() + solution, libraries, flags, staticKb, dynamicKb, + sideEffectManager ?: this@replyWith.context.getSideEffectManager(), + operators ) } diff --git a/solve-streams/src/commonMain/kotlin/it/unibo/tuprolog/solve/solver/StreamsExecutionContext.kt b/solve-streams/src/commonMain/kotlin/it/unibo/tuprolog/solve/solver/StreamsExecutionContext.kt index b3f6d0a5c..f766b21c4 100644 --- a/solve-streams/src/commonMain/kotlin/it/unibo/tuprolog/solve/solver/StreamsExecutionContext.kt +++ b/solve-streams/src/commonMain/kotlin/it/unibo/tuprolog/solve/solver/StreamsExecutionContext.kt @@ -2,6 +2,7 @@ package it.unibo.tuprolog.solve.solver import it.unibo.tuprolog.core.Struct import it.unibo.tuprolog.core.Substitution +import it.unibo.tuprolog.core.operators.OperatorSet import it.unibo.tuprolog.solve.* import it.unibo.tuprolog.solve.channel.InputChannel import it.unibo.tuprolog.solve.channel.OutputChannel @@ -19,6 +20,7 @@ internal data class StreamsExecutionContext( override val flags: PrologFlags = emptyMap(), override val staticKb: Theory = Theory.empty(), override val dynamicKb: Theory = Theory.empty(), + override val operators: OperatorSet = getAllOperators(libraries, staticKb, dynamicKb).toOperatorSet(), override val inputChannels: Map> = ExecutionContextAware.defaultInputChannels(), override val outputChannels: Map> = ExecutionContextAware.defaultOutputChannels(), override val substitution: Substitution.Unifier = Substitution.empty(), @@ -44,17 +46,15 @@ internal data class StreamsExecutionContext( stdOut: OutputChannel, stdErr: OutputChannel, warnings: OutputChannel - ): Solver { - return StreamsSolverFactory.solverOf( - libraries, - flags, - staticKb, - dynamicKb, - stdIn, - stdOut, - stdErr - ) - } + ) = StreamsSolverFactory.solverOf( + libraries, + flags, + staticKb, + dynamicKb, + stdIn, + stdOut, + stdErr + ) } diff --git a/solve-streams/src/commonMain/kotlin/it/unibo/tuprolog/solve/solver/fsm/impl/StateEnd.kt b/solve-streams/src/commonMain/kotlin/it/unibo/tuprolog/solve/solver/fsm/impl/StateEnd.kt index e78d64e07..4a0518760 100644 --- a/solve-streams/src/commonMain/kotlin/it/unibo/tuprolog/solve/solver/fsm/impl/StateEnd.kt +++ b/solve-streams/src/commonMain/kotlin/it/unibo/tuprolog/solve/solver/fsm/impl/StateEnd.kt @@ -1,6 +1,7 @@ package it.unibo.tuprolog.solve.solver.fsm.impl import it.unibo.tuprolog.core.Substitution +import it.unibo.tuprolog.core.operators.OperatorSet import it.unibo.tuprolog.solve.* import it.unibo.tuprolog.solve.exception.HaltException import it.unibo.tuprolog.solve.exception.TuPrologRuntimeException @@ -31,6 +32,7 @@ internal sealed class StateEnd(override val solve: Solve.Response) : AbstractSta flags ?: emptyMap(), staticKb ?: Theory.empty(), dynamicKb ?: Theory.empty(), + operators ?: OperatorSet.EMPTY, inputChannels ?: ExecutionContextAware.defaultInputChannels(), outputChannels ?: ExecutionContextAware.defaultOutputChannels(), solution.substitution as? Substitution.Unifier ?: Substitution.empty() @@ -70,6 +72,7 @@ internal fun IntermediateState.stateEndTrue( staticKb: Theory? = null, dynamicKb: Theory? = null, sideEffectManager: SideEffectManager? = null, + operators: OperatorSet? = null, inputChannels: PrologInputChannels<*>? = null, outputChannels: PrologOutputChannels<*>? = null ) = StateEnd.True( @@ -80,6 +83,7 @@ internal fun IntermediateState.stateEndTrue( staticKb ?: solve.context.staticKb, dynamicKb ?: solve.context.dynamicKb, sideEffectManager ?: solve.context.getSideEffectManager(), + operators ?: solve.context.operators, inputChannels ?: solve.context.inputChannels, outputChannels ?: solve.context.outputChannels ) @@ -92,6 +96,7 @@ internal fun IntermediateState.stateEndFalse( staticKb: Theory? = null, dynamicKb: Theory? = null, sideEffectManager: SideEffectManager? = null, + operators: OperatorSet? = null, inputChannels: PrologInputChannels<*>? = null, outputChannels: PrologOutputChannels<*>? = null ) = StateEnd.False( @@ -101,6 +106,7 @@ internal fun IntermediateState.stateEndFalse( staticKb ?: solve.context.staticKb, dynamicKb ?: solve.context.dynamicKb, sideEffectManager ?: solve.context.getSideEffectManager(), + operators ?: solve.context.operators, inputChannels ?: solve.context.inputChannels, outputChannels ?: solve.context.outputChannels ) @@ -114,6 +120,7 @@ internal fun IntermediateState.stateEndHalt( staticKb: Theory? = null, dynamicKb: Theory? = null, sideEffectManager: SideEffectManager? = null, + operators: OperatorSet? = null, inputChannels: PrologInputChannels<*>? = null, outputChannels: PrologOutputChannels<*>? = null ) = StateEnd.Halt( @@ -126,6 +133,7 @@ internal fun IntermediateState.stateEndHalt( sideEffectManager ?: exception.context.getSideEffectManager() ?: solve.context.getSideEffectManager(), + operators ?: solve.context.operators, inputChannels ?: solve.context.inputChannels, outputChannels ?: solve.context.outputChannels ) @@ -139,6 +147,7 @@ internal fun IntermediateState.stateEnd( staticKb: Theory? = null, dynamicKb: Theory? = null, sideEffectManager: SideEffectManager? = null, + operators: OperatorSet? = null, inputChannels: PrologInputChannels<*>? = null, outputChannels: PrologOutputChannels<*>? = null ): StateEnd = when (solution) { @@ -150,11 +159,12 @@ internal fun IntermediateState.stateEnd( staticKb, dynamicKb, sideEffectManager, + operators, inputChannels, outputChannels ) is Solution.No -> - stateEndFalse(libraries, flags, staticKb, dynamicKb, sideEffectManager, inputChannels, outputChannels) + stateEndFalse(libraries, flags, staticKb, dynamicKb, sideEffectManager, operators, inputChannels, outputChannels) is Solution.Halt -> stateEndHalt( solution.exception, @@ -163,6 +173,7 @@ internal fun IntermediateState.stateEnd( staticKb, dynamicKb, sideEffectManager, + operators, inputChannels, outputChannels ) @@ -177,6 +188,7 @@ internal fun IntermediateState.stateEnd(response: Solve.Response) = with(respons staticKb, dynamicKb, sideEffectManager, + operators, inputChannels, outputChannels ) diff --git a/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/ExecutionContextAware.kt b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/ExecutionContextAware.kt index 7c3d28a6b..620ae97d9 100644 --- a/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/ExecutionContextAware.kt +++ b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/ExecutionContextAware.kt @@ -1,5 +1,6 @@ package it.unibo.tuprolog.solve +import it.unibo.tuprolog.core.operators.OperatorSet import it.unibo.tuprolog.solve.channel.InputChannel import it.unibo.tuprolog.solve.channel.OutputChannel import it.unibo.tuprolog.solve.exception.PrologWarning @@ -55,6 +56,10 @@ interface ExecutionContextAware { @JsName("dynamicKb") val dynamicKb: Theory + /** Loaded operators */ + @JsName("operators") + val operators: OperatorSet + /** The currently open input channels */ @JsName("inputChannels") val inputChannels: PrologInputChannels<*> diff --git a/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/Solve.kt b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/Solve.kt index 23f91c629..af0f084c4 100644 --- a/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/Solve.kt +++ b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/Solve.kt @@ -3,6 +3,7 @@ package it.unibo.tuprolog.solve import it.unibo.tuprolog.core.Struct import it.unibo.tuprolog.core.Substitution import it.unibo.tuprolog.core.Term +import it.unibo.tuprolog.core.operators.OperatorSet import it.unibo.tuprolog.solve.exception.TuPrologRuntimeException import it.unibo.tuprolog.solve.library.Libraries import it.unibo.tuprolog.theory.Theory @@ -55,6 +56,7 @@ sealed class Solve { staticKb: Theory? = null, dynamicKb: Theory? = null, sideEffectManager: SideEffectManager? = null, + operators: OperatorSet? = null, inputChannels: PrologInputChannels<*>? = null, outputChannels: PrologOutputChannels<*>? = null ) = when (substitution) { @@ -66,12 +68,22 @@ sealed class Solve { staticKb, dynamicKb, sideEffectManager, + operators, inputChannels, outputChannels ) } else -> { - replyFail(libraries, flags, staticKb, dynamicKb, sideEffectManager, inputChannels, outputChannels) + replyFail( + libraries, + flags, + staticKb, + dynamicKb, + sideEffectManager, + operators, + inputChannels, + outputChannels + ) } } @@ -84,18 +96,48 @@ sealed class Solve { staticKb: Theory? = null, dynamicKb: Theory? = null, sideEffectManager: SideEffectManager? = null, + operators: OperatorSet? = null, inputChannels: PrologInputChannels<*>? = null, outputChannels: PrologOutputChannels<*>? = null ) = when (solution) { - is Solution.Yes -> replySuccess( - solution.substitution, - libraries, flags, staticKb, dynamicKb, sideEffectManager, inputChannels, outputChannels - ) - is Solution.No -> replyFail(libraries, flags, staticKb, dynamicKb, sideEffectManager, inputChannels, outputChannels) - is Solution.Halt -> replyException( - solution.exception, - libraries, flags, staticKb, dynamicKb, sideEffectManager, inputChannels, outputChannels - ) + is Solution.Yes -> { + replySuccess( + solution.substitution, + libraries, + flags, + staticKb, + dynamicKb, + sideEffectManager, + operators, + inputChannels, + outputChannels + ) + } + is Solution.No -> { + replyFail( + libraries, + flags, + staticKb, + dynamicKb, + sideEffectManager, + operators, + inputChannels, + outputChannels + ) + } + is Solution.Halt -> { + replyException( + solution.exception, + libraries, + flags, + staticKb, + dynamicKb, + sideEffectManager, + operators, + inputChannels, + outputChannels + ) + } } /** Creates a new successful or failed [Response] depending on [condition]; to be used when the substitution doesn't change */ @@ -107,19 +149,34 @@ sealed class Solve { staticKb: Theory? = null, dynamicKb: Theory? = null, sideEffectManager: SideEffectManager? = null, + operators: OperatorSet? = null, inputChannels: PrologInputChannels<*>? = null, outputChannels: PrologOutputChannels<*>? = null ) = when (condition) { - true -> replySuccess( - libraries = libraries, - flags = flags, - staticKb = staticKb, - dynamicKb = dynamicKb, - sideEffectManager = sideEffectManager, - inputChannels = inputChannels, - outputChannels = outputChannels - ) - false -> replyFail(libraries, flags, staticKb, dynamicKb, sideEffectManager, inputChannels, outputChannels) + true -> { + replySuccess( + libraries = libraries, + flags = flags, + staticKb = staticKb, + dynamicKb = dynamicKb, + sideEffectManager = sideEffectManager, + operators = operators, + inputChannels = inputChannels, + outputChannels = outputChannels + ) + } + false -> { + replyFail( + libraries, + flags, + staticKb, + dynamicKb, + sideEffectManager, + operators, + inputChannels, + outputChannels + ) + } } /** Creates a new successful [Response] to this Request, with substitution */ @@ -131,6 +188,7 @@ sealed class Solve { staticKb: Theory? = null, dynamicKb: Theory? = null, sideEffectManager: SideEffectManager? = null, + operators: OperatorSet? = null, inputChannels: PrologInputChannels<*>? = null, outputChannels: PrologOutputChannels<*>? = null ) = Response( @@ -140,6 +198,7 @@ sealed class Solve { staticKb, dynamicKb, sideEffectManager, + operators, inputChannels, outputChannels ) @@ -152,6 +211,7 @@ sealed class Solve { staticKb: Theory? = null, dynamicKb: Theory? = null, sideEffectManager: SideEffectManager? = null, + operators: OperatorSet? = null, inputChannels: PrologInputChannels<*>? = null, outputChannels: PrologOutputChannels<*>? = null ) = Response( @@ -161,6 +221,7 @@ sealed class Solve { staticKb, dynamicKb, sideEffectManager, + operators, inputChannels, outputChannels ) @@ -174,6 +235,7 @@ sealed class Solve { staticKb: Theory? = null, dynamicKb: Theory? = null, sideEffectManager: SideEffectManager? = null, + operators: OperatorSet? = null, inputChannels: PrologInputChannels<*>? = null, outputChannels: PrologOutputChannels<*>? = null ) = Response( @@ -183,6 +245,7 @@ sealed class Solve { staticKb, dynamicKb, sideEffectManager, + operators, inputChannels, outputChannels ) @@ -219,6 +282,9 @@ sealed class Solve { /** The Prolog flow modification manager after request execution (use `null` in case nothing changed) */ @JsName("sideEffectManager") val sideEffectManager: SideEffectManager? = null, + /** The set of loaded operators after request execution (use `null` in case nothing changed) */ + @JsName("operators") + val operators: OperatorSet? = null, /** The input channels modification after request execution (use `null` in case nothing changed) */ @JsName("inputChannels") val inputChannels: PrologInputChannels<*>? = null, diff --git a/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/Utils.kt b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/Utils.kt index 3d77b0b14..b75b5c884 100644 --- a/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/Utils.kt +++ b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/Utils.kt @@ -1,6 +1,14 @@ @file:JvmName("Utils") package it.unibo.tuprolog.solve +import it.unibo.tuprolog.core.Clause +import it.unibo.tuprolog.core.Directive +import it.unibo.tuprolog.core.Struct +import it.unibo.tuprolog.core.operators.Operator +import it.unibo.tuprolog.core.operators.OperatorSet +import it.unibo.tuprolog.solve.library.Libraries +import it.unibo.tuprolog.solve.library.Library +import it.unibo.tuprolog.theory.Theory import kotlin.jvm.JvmName /** Performs the given [action] on each element, giving a lookahead hint (i.e. if there's another element to process after). */ @@ -17,3 +25,29 @@ inline fun Iterable.forEachWithLookahead(action: (T, Boolean) -> Unit) = /** Performs the given [action] on each element, giving a lookahead hint (i.e. if there's another element to process after). */ inline fun Sequence.forEachWithLookahead(action: (T, Boolean) -> Unit) = iterator().forEachWithLookahead(action) + +fun Iterable.getAllOperators(): Sequence { + return asSequence() + .filterIsInstance() + .map { it.body } + .filterIsInstance() + .filter { it.arity == 3 && it.functor == "op" } + .map { Operator.fromTerm(it) } + .filterNotNull() +} + +fun Library.getAllOperators(): Sequence { + return operators.asSequence() +} + +fun Libraries.getAllOperators(): Sequence { + return operators.asSequence() +} + +fun getAllOperators(libraries: Libraries, vararg theories: Theory): Sequence { + return libraries.getAllOperators() + sequenceOf(*theories).flatMap { it.getAllOperators() } +} + +fun Sequence.toOperatorSet(): OperatorSet { + return OperatorSet(this) +} \ No newline at end of file From 507574bb160709d0cb8e667f0d4f6ce0569abd2d Mon Sep 17 00:00:00 2001 From: Giovanni Ciatto Date: Fri, 12 Jun 2020 20:08:26 +0200 Subject: [PATCH 07/14] automatically generate Info.kt file in :core --- .gitignore | 5 +---- core/build.gradle.kts | 25 ++++++++++++++++++++++++- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 29fe951ef..84011dbba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,4 @@ - -# Created by https://www.gitignore.io/api/gradle,kotlin,intellij -# Edit at https://www.gitignore.io/?templates=gradle,kotlin,intellij - +core/src/commonMain/kotlin/it/unibo/tuprolog/Info.kt ### Intellij ### # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 diff --git a/core/build.gradle.kts b/core/build.gradle.kts index bdbaefb32..7bf255238 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -1,4 +1,7 @@ -import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinPackageJsonTask +import org.jetbrains.kotlin.gradle.dsl.KotlinCompile + +val tuPrologPackage get() = rootProject.group.toString() +val tuPrologPackageDir get() = tuPrologPackage.replace('.', File.separatorChar) kotlin { @@ -7,6 +10,26 @@ kotlin { dependencies { api(Libs.kt_math) } + + val infoKtFile = kotlin.srcDirs.first().absoluteFile.resolve("$tuPrologPackageDir/Info.kt") + + val createInfoKt by tasks.creating { + doLast { + infoKtFile.writeText(""" + |package $tuPrologPackage + | + |object Info { + | val VERSION = "${rootProject.version}" + |} + """.trimMargin()) + } + outputs.file(infoKtFile) + } + + tasks.withType>().forEach { + it.dependsOn(createInfoKt) + it.inputs.file(infoKtFile) + } } } } \ No newline at end of file From 03eb22ad48b0afc99e9a3a097ad7c4d958bd5818 Mon Sep 17 00:00:00 2001 From: Giovanni Ciatto Date: Fri, 12 Jun 2020 20:08:46 +0200 Subject: [PATCH 08/14] make :repl operators sensitive --- .../it/unibo/tuprolog/ui/repl/TuPrologCmd.kt | 29 ++++++++++++----- .../tuprolog/ui/repl/TuPrologSolveQuery.kt | 4 +-- .../unibo/tuprolog/ui/repl/TuPrologUtils.kt | 31 ++++++++++--------- 3 files changed, 40 insertions(+), 24 deletions(-) diff --git a/repl/src/commonMain/kotlin/it/unibo/tuprolog/ui/repl/TuPrologCmd.kt b/repl/src/commonMain/kotlin/it/unibo/tuprolog/ui/repl/TuPrologCmd.kt index eb65e83e5..d452c584e 100644 --- a/repl/src/commonMain/kotlin/it/unibo/tuprolog/ui/repl/TuPrologCmd.kt +++ b/repl/src/commonMain/kotlin/it/unibo/tuprolog/ui/repl/TuPrologCmd.kt @@ -6,6 +6,7 @@ import com.github.ajalt.clikt.parameters.options.default import com.github.ajalt.clikt.parameters.options.multiple import com.github.ajalt.clikt.parameters.options.option import com.github.ajalt.clikt.parameters.types.int +import it.unibo.tuprolog.Info import it.unibo.tuprolog.core.Struct import it.unibo.tuprolog.core.parsing.ParseException import it.unibo.tuprolog.core.parsing.parse @@ -45,14 +46,26 @@ class TuPrologCmd : CliktCommand( // nota: se subcommand รจ diverso da null, il controllo fluisce automaticamente al metodo run di subcommand } - private fun loadTheory(): Theory { var theory: Theory = Theory.empty() for (file in this.files) { if (isReadableFile(file)) { - theory += loadTheoryFromFile(file) + try { + val t = loadTheoryFromFile(file) + TermUi.echo("# Successfully loaded ${t.size} clauses from $file") + theory += t + } catch (e: ParseException) { + TermUi.echo(""" + |Error while parsing theory file: $file + | Message: ${e.message} + | Line : ${e.line} + | Column : ${e.column} + | Clause : ${e.clauseIndex} + """.trimMargin(), err = true) + } } } + TermUi.echo("") return theory } @@ -60,8 +73,9 @@ class TuPrologCmd : CliktCommand( var query: String? = TuPrologUtils.readQuery() while (query != null) { try { - val solutions = solver.solve(Struct.parse(query), this.getTimeout()).iterator() - TuPrologUtils.printSolutions(solutions) + val goal = Struct.parse(query, solver.operators) + val solutions = solver.solve(goal, this.getTimeout()).iterator() + TuPrologUtils.printSolutions(solutions, solver.operators) } catch (e: ParseException) { TuPrologUtils.printParseException(e) } @@ -71,14 +85,13 @@ class TuPrologCmd : CliktCommand( } fun getTimeout(): TimeDuration { - val duration: TimeDuration = timeout.toLong() - return duration + return timeout.toLong() } fun getSolver(): Solver { + TermUi.echo("# 2P-Kt version ${Info.VERSION}") val theory: Theory = this.loadTheory() - val solve: Solver = Solver.classicWithDefaultBuiltins(staticKb = theory) - return solve + return Solver.classicWithDefaultBuiltins(staticKb = theory) } } diff --git a/repl/src/commonMain/kotlin/it/unibo/tuprolog/ui/repl/TuPrologSolveQuery.kt b/repl/src/commonMain/kotlin/it/unibo/tuprolog/ui/repl/TuPrologSolveQuery.kt index c854fe6d8..457d8444f 100644 --- a/repl/src/commonMain/kotlin/it/unibo/tuprolog/ui/repl/TuPrologSolveQuery.kt +++ b/repl/src/commonMain/kotlin/it/unibo/tuprolog/ui/repl/TuPrologSolveQuery.kt @@ -34,9 +34,9 @@ class TuPrologSolveQuery : CliktCommand(help = "Compute a particular query and t val duration: TimeDuration = parentCommand.getTimeout() val solutions = solver.solve(Struct.parse(query), duration).iterator() if (maxSolutions == 0) { - TuPrologUtils.printSolutions(solutions) + TuPrologUtils.printSolutions(solutions, solver.operators) } else { - TuPrologUtils.printNumSolutions(solutions, maxSolutions) + TuPrologUtils.printNumSolutions(solutions, maxSolutions, solver.operators) } } catch (e: ParseException) { TuPrologUtils.printParseException(e) diff --git a/repl/src/commonMain/kotlin/it/unibo/tuprolog/ui/repl/TuPrologUtils.kt b/repl/src/commonMain/kotlin/it/unibo/tuprolog/ui/repl/TuPrologUtils.kt index 6254abe4a..f8b3bc11a 100644 --- a/repl/src/commonMain/kotlin/it/unibo/tuprolog/ui/repl/TuPrologUtils.kt +++ b/repl/src/commonMain/kotlin/it/unibo/tuprolog/ui/repl/TuPrologUtils.kt @@ -4,7 +4,9 @@ import com.github.ajalt.clikt.core.ProgramResult import com.github.ajalt.clikt.output.TermUi import com.github.ajalt.clikt.output.defaultCliktConsole import it.unibo.tuprolog.core.TermFormatter +import it.unibo.tuprolog.core.TermFormatter.Companion.prettyExpressions import it.unibo.tuprolog.core.format +import it.unibo.tuprolog.core.operators.OperatorSet import it.unibo.tuprolog.core.parsing.ParseException import it.unibo.tuprolog.solve.Solution import it.unibo.tuprolog.solve.exception.HaltException @@ -12,34 +14,34 @@ import it.unibo.tuprolog.solve.exception.TimeOutException object TuPrologUtils { - private fun printSolution(sol: Solution) { + private fun printSolution(sol: Solution, operatorSet: OperatorSet) { when (sol) { is Solution.Yes -> { - printYesSolution(sol) + printYesSolution(sol, operatorSet) } is Solution.No -> { - printNoSolution(sol) + printNoSolution(sol, operatorSet) } is Solution.Halt -> { - printHaltSolution(sol) + printHaltSolution(sol, operatorSet) } } } - private fun printYesSolution(sol: Solution.Yes) { - TermUi.echo("yes: ${sol.solvedQuery.format(TermFormatter.prettyExpressions())}.") + private fun printYesSolution(sol: Solution.Yes, operatorSet: OperatorSet) { + TermUi.echo("yes: ${sol.solvedQuery.format(prettyExpressions(operatorSet))}.") if (sol.substitution.isNotEmpty()) { val sep = "\n " val substitutions = sol.substitution.entries.joinToString(sep) { val prettyVariable = it.key.format(TermFormatter.prettyVariables()) - val prettyValue = it.value.format(TermFormatter.prettyVariables()) + val prettyValue = it.value.format(prettyExpressions(operatorSet)) "$prettyVariable = $prettyValue" } TermUi.echo(" $substitutions") } } - private fun printHaltSolution(sol: Solution.Halt) { + private fun printHaltSolution(sol: Solution.Halt, operatorSet: OperatorSet) { when (val ex = sol.exception) { is TimeOutException -> { TermUi.echo("timeout.") @@ -55,7 +57,7 @@ object TuPrologUtils { TermUi.echo("halt: ${ex.message?.trim()}") } val sep = "\n at " - val stacktrace = ex.prologStackTrace.joinToString(sep) { it.toString() } + val stacktrace = ex.prologStackTrace.joinToString(sep) { it.format(prettyExpressions(operatorSet)) } TermUi.echo(" at $stacktrace") } } @@ -95,11 +97,12 @@ object TuPrologUtils { return longQuery.toString() } - private fun printNoSolution(sol: Solution.No) { + @Suppress("UNUSED_PARAMETER") + private fun printNoSolution(sol: Solution.No, operatorSet: OperatorSet) { TermUi.echo("no.") } - fun printSolutions(solutions: Iterator) { + fun printSolutions(solutions: Iterator, operatorSet: OperatorSet) { var first = true while (solutions.hasNext()) { if (!first) { @@ -108,16 +111,16 @@ object TuPrologUtils { } else { first = false } - printSolution(solutions.next()) + printSolution(solutions.next(), operatorSet) } printEndOfSolutions() } - fun printNumSolutions(solutions: Iterator, maxSolutions: Int) { + fun printNumSolutions(solutions: Iterator, maxSolutions: Int, operatorSet: OperatorSet) { var i = 0 while (i < maxSolutions && solutions.hasNext()) { i++ - printSolution(solutions.next()) + printSolution(solutions.next(), operatorSet) } printEndOfSolutions() } From 30aff0bebbea319073f52fc3c8992f42afbef679 Mon Sep 17 00:00:00 2001 From: Giovanni Ciatto Date: Fri, 12 Jun 2020 20:10:34 +0200 Subject: [PATCH 09/14] fix broken DummyInstances file --- .../commonMain/kotlin/it/unibo/tuprolog/solve/DummyInstances.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test-solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/DummyInstances.kt b/test-solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/DummyInstances.kt index 82e4101ef..977997fdc 100644 --- a/test-solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/DummyInstances.kt +++ b/test-solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/DummyInstances.kt @@ -3,6 +3,7 @@ package it.unibo.tuprolog.solve import it.unibo.tuprolog.core.Atom import it.unibo.tuprolog.core.Struct import it.unibo.tuprolog.core.Substitution +import it.unibo.tuprolog.core.operators.OperatorSet import it.unibo.tuprolog.solve.channel.InputChannel import it.unibo.tuprolog.solve.channel.OutputChannel import it.unibo.tuprolog.solve.exception.PrologWarning @@ -24,6 +25,7 @@ object DummyInstances { override val flags: Nothing by lazy { throw NotImplementedError() } override val staticKb: Nothing by lazy { throw NotImplementedError() } override val dynamicKb: Nothing by lazy { throw NotImplementedError() } + override val operators: OperatorSet by lazy { throw NotImplementedError() } override val inputChannels: Nothing by lazy { throw NotImplementedError() } override val outputChannels: Nothing by lazy { throw NotImplementedError() } override val substitution: Substitution.Unifier = Substitution.empty() From e11115ec22ea761c7aed36a08a57a5e7f58f2bdd Mon Sep 17 00:00:00 2001 From: Giovanni Ciatto Date: Fri, 12 Jun 2020 20:50:06 +0200 Subject: [PATCH 10/14] + current_op/3 --- .../tuprolog/solve/stdlib/CommonBuiltins.kt | 1 + .../solve/stdlib/primitive/CurrentOp.kt | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/primitive/CurrentOp.kt diff --git a/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/CommonBuiltins.kt b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/CommonBuiltins.kt index 90a774275..7d7ef3377 100644 --- a/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/CommonBuiltins.kt +++ b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/CommonBuiltins.kt @@ -26,6 +26,7 @@ object CommonBuiltins : AliasedLibrary by Library.of( Atomic, Callable, Compound, + CurrentOp, EnsureExecutable, FindAll, FloatPrimitive, diff --git a/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/primitive/CurrentOp.kt b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/primitive/CurrentOp.kt new file mode 100644 index 000000000..4e2d196d2 --- /dev/null +++ b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/primitive/CurrentOp.kt @@ -0,0 +1,27 @@ +package it.unibo.tuprolog.solve.stdlib.primitive + +import it.unibo.tuprolog.core.Atom +import it.unibo.tuprolog.core.Integer +import it.unibo.tuprolog.core.Substitution +import it.unibo.tuprolog.core.Term +import it.unibo.tuprolog.solve.ExecutionContext +import it.unibo.tuprolog.solve.Solve +import it.unibo.tuprolog.solve.primitive.TernaryRelation +import it.unibo.tuprolog.unify.Unificator.Companion.mguWith + +object CurrentOp : TernaryRelation.WithoutSideEffects("current_op") { + override fun Solve.Request.computeAllSubstitutions( + first: Term, + second: Term, + third: Term + ): Sequence = + context.operators.asSequence().map { + listOf( + first mguWith Integer.of(it.priority), + second mguWith it.specifier.toTerm(), + third mguWith Atom.of(it.functor) + ) + }.filter { + it.all { sub -> sub is Substitution.Unifier } + }.map { it.reduce(Substitution::plus) } +} \ No newline at end of file From 5cfa5abffea75736cd5881b447335b7b3adbb433 Mon Sep 17 00:00:00 2001 From: Giovanni Ciatto Date: Fri, 12 Jun 2020 23:13:32 +0200 Subject: [PATCH 11/14] add factory Operator.fromTerms --- .../kotlin/it/unibo/tuprolog/core/operators/Operator.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/src/commonMain/kotlin/it/unibo/tuprolog/core/operators/Operator.kt b/core/src/commonMain/kotlin/it/unibo/tuprolog/core/operators/Operator.kt index 8737cb777..fe838d64b 100644 --- a/core/src/commonMain/kotlin/it/unibo/tuprolog/core/operators/Operator.kt +++ b/core/src/commonMain/kotlin/it/unibo/tuprolog/core/operators/Operator.kt @@ -53,6 +53,11 @@ class Operator(val functor: String, val specifier: Specifier, val priority: Int) @JvmField val TEMPLATE = Struct.of(FUNCTOR, Var.of("P"), Var.of("A"), Var.of("F")) + @JvmStatic + @JsName("fromTerms") + fun fromTerms(priority: Integer, specifier: Atom, functor: Atom): Operator? = + fromTerm(Struct.of(FUNCTOR, priority, specifier, functor)) + /** Creates an Operator instance from a well-formed Struct, or returns `null` if it cannot be interpreted as Operator */ @JvmStatic @JsName("fromTerm") From c54b23b7b0516577a7ff9e6bfca0f3330f9f8d8e Mon Sep 17 00:00:00 2001 From: Giovanni Ciatto Date: Fri, 12 Jun 2020 23:15:32 +0200 Subject: [PATCH 12/14] add DomainError and its relative type ensuring methods --- .../tuprolog/solve/exception/PrologError.kt | 2 + .../solve/exception/error/DomainError.kt | 106 ++++++++++++++++++ .../solve/primitive/PrimitiveWrapper.kt | 38 +++++++ 3 files changed, 146 insertions(+) create mode 100644 solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/exception/error/DomainError.kt diff --git a/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/exception/PrologError.kt b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/exception/PrologError.kt index de9900fb9..580b728c6 100644 --- a/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/exception/PrologError.kt +++ b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/exception/PrologError.kt @@ -62,6 +62,8 @@ abstract class PrologError( functor == MetaError.typeFunctor && cause is TuPrologRuntimeException -> MetaError(message, cause, context, extraData) functor == InstantiationError.typeFunctor -> InstantiationError(message, cause, context, extraData) functor == SystemError.typeFunctor -> SystemError(message, cause, context, extraData) + functor == DomainError.typeFunctor && arity == 2 && DomainError.Expected.fromTerm(args.first()) != null -> + DomainError(message, cause, context, DomainError.Expected.fromTerm(args.first())!!, args[1], extraData) functor == TypeError.typeFunctor && arity == 2 && TypeError.Expected.fromTerm(args.first()) != null -> TypeError(message, cause, context, TypeError.Expected.fromTerm(args.first())!!, args[1], extraData) functor == EvaluationError.typeFunctor && arity == 1 && EvaluationError.Type.fromTerm(args.single()) != null -> diff --git a/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/exception/error/DomainError.kt b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/exception/error/DomainError.kt new file mode 100644 index 000000000..6f10d27d3 --- /dev/null +++ b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/exception/error/DomainError.kt @@ -0,0 +1,106 @@ +package it.unibo.tuprolog.solve.exception.error + +import it.unibo.tuprolog.core.Atom +import it.unibo.tuprolog.core.Struct +import it.unibo.tuprolog.core.Term +import it.unibo.tuprolog.core.ToTermConvertible +import it.unibo.tuprolog.solve.ExecutionContext +import it.unibo.tuprolog.solve.Signature +import it.unibo.tuprolog.solve.exception.PrologError + +/** + * The domain error occurs when something has the correct type but the value is not amissible + * + * @param message the detail message string. + * @param cause the cause of this exception. + * @param context The current context at exception creation + * @param expectedDomain The expected domain, that wouldn't have raised the error + * @param actualValue The value not respecting [expectedDomain] + * @param extraData The possible extra data to be carried with the error + * + * @author Enrico + */ +class DomainError( + message: String? = null, + cause: Throwable? = null, + context: ExecutionContext, + val expectedDomain: Expected, + val actualValue: Term, + extraData: Term? = null +) : PrologError(message, cause, context, Atom.of(typeFunctor), extraData) { + + override val type: Struct by lazy { Struct.of(super.type.functor, expectedDomain.toTerm(), actualValue) } + + companion object { + + fun forArgument( + context: ExecutionContext, + procedure: Signature, + expectedDomain: Expected, + actualValue: Term, + index: Int? = null + ) = DomainError( + message = "Argument ${index?.toString()?.plus(" ") ?: ""}" + + "of `${procedure.toIndicator()}` should be `$expectedDomain`, " + + "but `$actualValue` has been provided instead", + context = context, + expectedDomain = expectedDomain, + actualValue = actualValue, + extraData = actualValue + ) + + fun forGoal( + context: ExecutionContext, + procedure: Signature, + expectedDomain: Expected, + actualValue: Term + ) = "Subgoal `$actualValue` of ${procedure.toIndicator()} is not $expectedDomain term".let { + DomainError( + message = it, + context = context, + expectedDomain = expectedDomain, + actualValue = actualValue, + extraData = Atom.of(it) + ) + } + + /** The domain error Struct functor */ + const val typeFunctor = "domain_error" + } + + /** + * A class describing the expected domain whose absence caused the error + * + * @param domain the expected domain string description + */ + enum class Expected constructor(private val domain: String) : ToTermConvertible { + // TODO add more cases + NOT_LESS_THAN_ZERO("not_less_than_zero"), + STREAM_PROPERTY("stream_property"), + STREAM_POSITION("stream_position"), + STREAM_OR_ALIAS("stream_or_alias"), + READ_OPTION("read_option"), + PROLOG_FLAG("prolog_flag"), + FLAG_VALUE("flag_value"), + OPERATOR_SPECIFIER("operator_specifier"), + OPERATOR_PRIORITY("operator_priority"); + + + /** A function to transform the type to corresponding [Atom] representation */ + override fun toTerm(): Atom = Atom.of(domain) + + override fun toString(): String = domain + + companion object { + + /** Returns the Expected instance described by [domain]; creates a new instance only if [domain] was not predefined */ + fun of(domain: String): Expected = valueOf(domain.toUpperCase()) + + /** Gets [Expected] instance from [term] representation, if possible */ + fun fromTerm(term: Term): Expected? = when (term) { + is Atom -> of(term.value) + else -> null + } + } + } +} \ No newline at end of file diff --git a/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/primitive/PrimitiveWrapper.kt b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/primitive/PrimitiveWrapper.kt index 0b4cd935d..a2790d327 100644 --- a/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/primitive/PrimitiveWrapper.kt +++ b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/primitive/PrimitiveWrapper.kt @@ -1,12 +1,15 @@ package it.unibo.tuprolog.solve.primitive import it.unibo.tuprolog.core.* +import it.unibo.tuprolog.core.operators.Specifier import it.unibo.tuprolog.solve.AbstractWrapper import it.unibo.tuprolog.solve.ExecutionContext import it.unibo.tuprolog.solve.Signature import it.unibo.tuprolog.solve.Solve +import it.unibo.tuprolog.solve.exception.error.DomainError import it.unibo.tuprolog.solve.exception.error.InstantiationError import it.unibo.tuprolog.solve.exception.error.TypeError +import org.gciatto.kt.math.BigInteger /** * Wrapper class for [Primitive] implementation @@ -98,10 +101,45 @@ abstract class PrimitiveWrapper : AbstractWrapper this } + fun Solve.Request.ensuringArgumentIsAtom(index: Int): Solve.Request = + when (val arg = arguments[index]) { + !is Atom -> throw TypeError.forArgument(context, signature, TypeError.Expected.ATOM, arg, index) + else -> this + } + + fun Solve.Request.ensuringArgumentIsSpecifier(index: Int): Solve.Request = + arguments[index].let { arg -> + when { + arg !is Atom -> throw DomainError.forArgument(context, signature, DomainError.Expected.OPERATOR_SPECIFIER, arg, index) + else -> { + try { + Specifier.fromTerm(arg) + this + } catch (e: IllegalArgumentException) { + throw DomainError.forArgument(context, signature, DomainError.Expected.OPERATOR_SPECIFIER, arg, index) + } + } + } + } + fun Solve.Request.ensuringArgumentIsInteger(index: Int): Solve.Request = when (val arg = arguments[index]) { !is Integer -> throw TypeError.forArgument(context, signature, TypeError.Expected.INTEGER, arg, index) else -> this } + + fun Solve.Request.ensuringArgumentIsNonNegativeInteger(index: Int): Solve.Request = + arguments[index].let { arg -> + when { + arg !is Integer || arg.intValue < BigInteger.ZERO -> throw DomainError.forArgument( + context, + signature, + DomainError.Expected.NOT_LESS_THAN_ZERO, + arg, + index + ) + else -> this + } + } } } From dea79b0ce83c5061776b86623fff17fc024c849c Mon Sep 17 00:00:00 2001 From: Giovanni Ciatto Date: Fri, 12 Jun 2020 23:15:49 +0200 Subject: [PATCH 13/14] + op/3 --- .../tuprolog/solve/stdlib/CommonBuiltins.kt | 1 + .../tuprolog/solve/stdlib/primitive/Op.kt | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/primitive/Op.kt diff --git a/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/CommonBuiltins.kt b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/CommonBuiltins.kt index 7d7ef3377..cfcb97820 100644 --- a/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/CommonBuiltins.kt +++ b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/CommonBuiltins.kt @@ -40,6 +40,7 @@ object CommonBuiltins : AliasedLibrary by Library.of( NonVar, NotUnifiableWith, Number, + Op, Retract, Sleep, TermIdentical, diff --git a/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/primitive/Op.kt b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/primitive/Op.kt new file mode 100644 index 000000000..915c83764 --- /dev/null +++ b/solve/src/commonMain/kotlin/it/unibo/tuprolog/solve/stdlib/primitive/Op.kt @@ -0,0 +1,23 @@ +package it.unibo.tuprolog.solve.stdlib.primitive + +import it.unibo.tuprolog.core.Atom +import it.unibo.tuprolog.core.Integer +import it.unibo.tuprolog.core.Term +import it.unibo.tuprolog.core.operators.Operator +import it.unibo.tuprolog.solve.ExecutionContext +import it.unibo.tuprolog.solve.Solve +import it.unibo.tuprolog.solve.primitive.TernaryRelation + +object Op : TernaryRelation.NonBacktrackable("op") { + override fun Solve.Request.computeOne(first: Term, second: Term, third: Term): Solve.Response { + ensuringArgumentIsInteger(0) + ensuringArgumentIsNonNegativeInteger(0) + ensuringArgumentIsAtom(1) + ensuringArgumentIsSpecifier(1) + ensuringArgumentIsAtom(2) + val operator = Operator.fromTerms(first as Integer, second as Atom, third as Atom)!! + return replySuccess( + operators = context.operators + operator + ) + } +} \ No newline at end of file From 160cf509d8755a465bc9f978f4c609556acbcbef Mon Sep 17 00:00:00 2001 From: Giovanni Ciatto Date: Fri, 12 Jun 2020 23:17:07 +0200 Subject: [PATCH 14/14] let StateGoalSelection propagate operators, input channels, and output channels to parent context --- .../it/unibo/tuprolog/solve/ClassicExecutionContext.kt | 1 + .../it/unibo/tuprolog/solve/fsm/StateGoalSelection.kt | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/solve-classic/src/commonMain/kotlin/it/unibo/tuprolog/solve/ClassicExecutionContext.kt b/solve-classic/src/commonMain/kotlin/it/unibo/tuprolog/solve/ClassicExecutionContext.kt index a21e93f6b..11b74a72f 100644 --- a/solve-classic/src/commonMain/kotlin/it/unibo/tuprolog/solve/ClassicExecutionContext.kt +++ b/solve-classic/src/commonMain/kotlin/it/unibo/tuprolog/solve/ClassicExecutionContext.kt @@ -99,6 +99,7 @@ data class ClassicExecutionContext( "rules=$rules, " + "primitives=$primitives, " + "startTime=$startTime, " + + "operators=${operators.joinToString(",", "{", "}") { "${it.functor}:${it.specifier}" }}, " + "inputChannels=${inputChannels.keys}, " + "outputChannels=${outputChannels.keys}, " + "maxDuration=$maxDuration, " + diff --git a/solve-classic/src/commonMain/kotlin/it/unibo/tuprolog/solve/fsm/StateGoalSelection.kt b/solve-classic/src/commonMain/kotlin/it/unibo/tuprolog/solve/fsm/StateGoalSelection.kt index b9d0eeee2..b491e3fcc 100644 --- a/solve-classic/src/commonMain/kotlin/it/unibo/tuprolog/solve/fsm/StateGoalSelection.kt +++ b/solve-classic/src/commonMain/kotlin/it/unibo/tuprolog/solve/fsm/StateGoalSelection.kt @@ -24,7 +24,11 @@ internal data class StateGoalSelection(override val context: ClassicExecutionCon goals = goals.next, // go on with parent's goals procedure = procedure, step = nextStep(), - startTime = context.startTime + startTime = context.startTime, + operators = context.operators, + inputChannels = context.inputChannels, + outputChannels = context.outputChannels, + libraries = context.libraries ) } )