Functor
This commit is contained in:
parent
dff53b4e68
commit
65c4d925d3
15 changed files with 270 additions and 83 deletions
|
@ -67,51 +67,51 @@ open class Preprocessor {
|
||||||
when {
|
when {
|
||||||
// TODO Remove hardcoding by storing the functors as constants in operators?
|
// TODO Remove hardcoding by storing the functors as constants in operators?
|
||||||
|
|
||||||
term.functor == ":-/2" -> Rule( args[0] as Head, args[1] as Body )
|
term.functor == FunctorInfo.of(":-/2") -> Rule( args[0] as Head, args[1] as Body )
|
||||||
|
|
||||||
// Logic
|
// Logic
|
||||||
term.functor == "=/2" -> Unify(args[0], args[1])
|
term.functor == FunctorInfo.of("=/2") -> Unify(args[0], args[1])
|
||||||
term.functor == "\\=/2" -> NotUnify(args[0], args[1])
|
term.functor == FunctorInfo.of("\\=/2") -> NotUnify(args[0], args[1])
|
||||||
term.functor == ",/2" -> Conjunction(args[0] as LogicOperand, args[1] as LogicOperand)
|
term.functor == FunctorInfo.of(",/2") -> Conjunction(args[0] as LogicOperand, args[1] as LogicOperand)
|
||||||
term.functor == ";/2" -> Disjunction(args[0] as LogicOperand, args[1] as LogicOperand)
|
term.functor == FunctorInfo.of(";/2") -> Disjunction(args[0] as LogicOperand, args[1] as LogicOperand)
|
||||||
term.functor == "\\+/1" -> Not(args[0] as Goal)
|
term.functor == FunctorInfo.of("\\+/1") -> Not(args[0] as Goal)
|
||||||
term.functor == "\\==/2" -> NotEquivalent(args[0], args[1])
|
term.functor == FunctorInfo.of("\\==/2") -> NotEquivalent(args[0], args[1])
|
||||||
term.functor == "==/2" -> Equivalent(args[0], args[1])
|
term.functor == FunctorInfo.of("==/2") -> Equivalent(args[0], args[1])
|
||||||
|
|
||||||
term.functor == "=\\=/2" && args.all { it is Expression } -> EvaluatesToDifferent(args[0] as Expression, args[1] as Expression)
|
term.functor == FunctorInfo.of("=\\=/2") && args.all { it is Expression } -> EvaluatesToDifferent(args[0] as Expression, args[1] as Expression)
|
||||||
term.functor == "=:=/2" && args.all { it is Expression } -> EvaluatesTo(args[0] as Expression, args[1] as Expression)
|
term.functor == FunctorInfo.of("=:=/2") && args.all { it is Expression } -> EvaluatesTo(args[0] as Expression, args[1] as Expression)
|
||||||
term.functor == "is/2" && args.all { it is Expression } -> Is(args[0] as Expression, args[1] as Expression)
|
term.functor == FunctorInfo.of("is/2") && args.all { it is Expression } -> Is(args[0] as Expression, args[1] as Expression)
|
||||||
|
|
||||||
// Arithmetic
|
// Arithmetic
|
||||||
|
|
||||||
term.functor == "-/1" && args.all { it is Expression } -> Negate(args[0] as Expression)
|
term.functor == FunctorInfo.of("-/1") && args.all { it is Expression } -> Negate(args[0] as Expression)
|
||||||
term.functor == "-/2" && args.all { it is Expression } -> Subtract(args[0] as Expression, args[1] as Expression)
|
term.functor == FunctorInfo.of("-/2") && args.all { it is Expression } -> Subtract(args[0] as Expression, args[1] as Expression)
|
||||||
term.functor == "+/1" && args.all { it is Expression } -> Positive(args[0] as Expression)
|
term.functor == FunctorInfo.of("+/1") && args.all { it is Expression } -> Positive(args[0] as Expression)
|
||||||
term.functor == "+/2" && args.all { it is Expression } -> Add(args[0] as Expression, args[1] as Expression)
|
term.functor == FunctorInfo.of("+/2") && args.all { it is Expression } -> Add(args[0] as Expression, args[1] as Expression)
|
||||||
term.functor == "*/2" && args.all { it is Expression } -> Multiply(args[0] as Expression, args[1] as Expression)
|
term.functor == FunctorInfo.of("*/2") && args.all { it is Expression } -> Multiply(args[0] as Expression, args[1] as Expression)
|
||||||
term.functor == "//2" && args.all { it is Expression } -> Divide(args[0] as Expression, args[1] as Expression)
|
term.functor == FunctorInfo.of("//2") && args.all { it is Expression } -> Divide(args[0] as Expression, args[1] as Expression)
|
||||||
term.functor == "between/3" && args.all { it is Expression } -> Between(args[0] as Expression, args[1] as Expression, args[2] as Expression)
|
term.functor == FunctorInfo.of("between/3") && args.all { it is Expression } -> Between(args[0] as Expression, args[1] as Expression, args[2] as Expression)
|
||||||
term.functor == "succ/2" && args.all { it is Expression } -> Successor(args[0] as Expression, args[1] as Expression)
|
term.functor == FunctorInfo.of("succ/2") && args.all { it is Expression } -> Successor(args[0] as Expression, args[1] as Expression)
|
||||||
|
|
||||||
// Database
|
// Database
|
||||||
term.functor == "dynamic/1" -> Dynamic((args[0] as Atom).name)
|
term.functor == FunctorInfo.of("dynamic/1") -> Dynamic(FunctorInfo.of((args[0] as Atom).name))
|
||||||
term.functor == "retract/1" -> Retract(args[0])
|
term.functor == FunctorInfo.of("retract/1") -> Retract(args[0])
|
||||||
term.functor == "retractall/1" -> RetractAll(args[0])
|
term.functor == FunctorInfo.of("retractall/1") -> RetractAll(args[0])
|
||||||
term.functor == "assert/1" -> {
|
term.functor == FunctorInfo.of("assert/1") -> {
|
||||||
if (args[0] is Rule) {
|
if (args[0] is Rule) {
|
||||||
Assert(args[0] as Rule)
|
Assert(args[0] as Rule)
|
||||||
} else {
|
} else {
|
||||||
Assert(Fact(args[0] as Head))
|
Assert(Fact(args[0] as Head))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
term.functor == "asserta/1" -> {
|
term.functor == FunctorInfo.of("asserta/1") -> {
|
||||||
if (args[0] is Rule) {
|
if (args[0] is Rule) {
|
||||||
AssertA(args[0] as Rule)
|
AssertA(args[0] as Rule)
|
||||||
} else {
|
} else {
|
||||||
AssertA(Fact(args[0] as Head))
|
AssertA(Fact(args[0] as Head))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
term.functor == "assertz/1" -> {
|
term.functor == FunctorInfo.of("assertz/1") -> {
|
||||||
if (args[0] is Rule) {
|
if (args[0] is Rule) {
|
||||||
AssertZ(args[0] as Rule)
|
AssertZ(args[0] as Rule)
|
||||||
} else {
|
} else {
|
||||||
|
@ -120,10 +120,10 @@ open class Preprocessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Other
|
// Other
|
||||||
term.functor == "write/1" -> Write(args[0])
|
term.functor == FunctorInfo.of("write/1") -> Write(args[0])
|
||||||
term.functor == "read/1" -> Read(args[0])
|
term.functor == FunctorInfo.of("read/1") -> Read(args[0])
|
||||||
term.functor == "initialization/1" -> Initialization(args[0] as Goal)
|
term.functor == FunctorInfo.of("initialization/1") -> Initialization(args[0] as Goal)
|
||||||
term.functor == "forall/2" -> ForAll(args[0] as LogicOperand, args[1] as Goal)
|
term.functor == FunctorInfo.of("forall/2") -> ForAll(args[0] as LogicOperand, args[1] as Goal)
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
term.arguments = args
|
term.arguments = args
|
||||||
|
|
|
@ -6,14 +6,14 @@ import prolog.Substitutions
|
||||||
import prolog.ast.logic.Clause
|
import prolog.ast.logic.Clause
|
||||||
import prolog.ast.logic.Predicate
|
import prolog.ast.logic.Predicate
|
||||||
import prolog.ast.logic.Resolvent
|
import prolog.ast.logic.Resolvent
|
||||||
import prolog.ast.terms.Functor
|
import prolog.ast.terms.FunctorInfo
|
||||||
import prolog.ast.terms.Goal
|
import prolog.ast.terms.Goal
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prolog Program or Database
|
* Prolog Program or Database
|
||||||
*/
|
*/
|
||||||
open class Database(val sourceFile: String) {
|
open class Database(val sourceFile: String) {
|
||||||
var predicates: Map<Functor, Predicate> = emptyMap()
|
var predicates: Map<FunctorInfo, Predicate> = emptyMap()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the database by running the initialization clauses of that database.
|
* Initializes the database by running the initialization clauses of that database.
|
||||||
|
@ -23,14 +23,14 @@ open class Database(val sourceFile: String) {
|
||||||
|
|
||||||
if (sourceFile !== "") {
|
if (sourceFile !== "") {
|
||||||
Logger.debug("Moving clauses from $sourceFile to main database")
|
Logger.debug("Moving clauses from $sourceFile to main database")
|
||||||
predicates.filter { it.key != "/_" }
|
predicates.filter { it.key != FunctorInfo.of("/_") }
|
||||||
.forEach { (_, predicate) -> db.load(predicate, force = true) }
|
.forEach { (_, predicate) -> db.load(predicate, force = true) }
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.info("Initializing database from $sourceFile")
|
Logger.info("Initializing database from $sourceFile")
|
||||||
if (predicates.contains("/_")) {
|
if (predicates.contains(FunctorInfo.of("/_"))) {
|
||||||
Logger.debug("Loading clauses from /_ predicate")
|
Logger.debug("Loading clauses from /_ predicate")
|
||||||
predicates["/_"]?.clauses?.forEach {
|
predicates[FunctorInfo.of("/_")]?.clauses?.forEach {
|
||||||
Logger.debug("Loading clause $it")
|
Logger.debug("Loading clause $it")
|
||||||
val goal = it.body as Goal
|
val goal = it.body as Goal
|
||||||
goal.satisfy(emptyMap()).toList()
|
goal.satisfy(emptyMap()).toList()
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package prolog.ast.logic
|
package prolog.ast.logic
|
||||||
|
|
||||||
import prolog.Answers
|
import prolog.Answers
|
||||||
import prolog.ast.Database.Program
|
|
||||||
import prolog.Substitutions
|
import prolog.Substitutions
|
||||||
|
import prolog.ast.Database.Program
|
||||||
import prolog.ast.terms.*
|
import prolog.ast.terms.*
|
||||||
import prolog.builtins.True
|
import prolog.builtins.True
|
||||||
import prolog.flags.AppliedCut
|
import prolog.flags.AppliedCut
|
||||||
|
@ -20,7 +20,7 @@ import prolog.logic.unifyLazy
|
||||||
* @see [Predicate]
|
* @see [Predicate]
|
||||||
*/
|
*/
|
||||||
abstract class Clause(var head: Head, var body: Body) : Term, Resolvent {
|
abstract class Clause(var head: Head, var body: Body) : Term, Resolvent {
|
||||||
val functor: Functor = head.functor
|
val functor: FunctorInfo = head.functor
|
||||||
|
|
||||||
override fun solve(goal: Goal, subs: Substitutions): Answers = sequence {
|
override fun solve(goal: Goal, subs: Substitutions): Answers = sequence {
|
||||||
// If the clause is a rule, unify the goal with the head and then try to prove the body.
|
// If the clause is a rule, unify the goal with the head and then try to prove the body.
|
||||||
|
@ -45,7 +45,14 @@ abstract class Clause(var head: Head, var body: Body) : Term, Resolvent {
|
||||||
onSuccess = { bodySubs ->
|
onSuccess = { bodySubs ->
|
||||||
// If the body can be proven, yield the (combined) substitutions
|
// If the body can be proven, yield the (combined) substitutions
|
||||||
val goalToHeadResult = goalToHeadSubs.mapValues { applySubstitution(it.value, bodySubs) }
|
val goalToHeadResult = goalToHeadSubs.mapValues { applySubstitution(it.value, bodySubs) }
|
||||||
val headResult = headToGoalSubs.filterKeys { key -> goalToHeadSubs.any { occurs(key as Variable, it.value) } }
|
val headResult = headToGoalSubs.filterKeys { key ->
|
||||||
|
goalToHeadSubs.any {
|
||||||
|
occurs(
|
||||||
|
key as Variable,
|
||||||
|
it.value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
yield(Result.success(goalToHeadResult + headResult))
|
yield(Result.success(goalToHeadResult + headResult))
|
||||||
},
|
},
|
||||||
onFailure = { error ->
|
onFailure = { error ->
|
||||||
|
|
|
@ -2,25 +2,25 @@ package prolog.ast.logic
|
||||||
|
|
||||||
import prolog.Answers
|
import prolog.Answers
|
||||||
import prolog.Substitutions
|
import prolog.Substitutions
|
||||||
import prolog.ast.terms.Functor
|
import prolog.ast.terms.FunctorInfo
|
||||||
import prolog.ast.terms.Goal
|
import prolog.ast.terms.Goal
|
||||||
import prolog.flags.AppliedCut
|
import prolog.flags.AppliedCut
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collection of [Clause]s with the same [Functor].
|
* Collection of [Clause]s with the same [FunctorInfo].
|
||||||
*
|
*
|
||||||
* If a goal is proved, the system looks for a predicate with the same functor, then uses indexing
|
* If a goal is proved, the system looks for a predicate with the same functor, then uses indexing
|
||||||
* to select candidate clauses and then tries these clauses one-by-one.
|
* to select candidate clauses and then tries these clauses one-by-one.
|
||||||
*/
|
*/
|
||||||
class Predicate : Resolvent {
|
class Predicate : Resolvent {
|
||||||
val functor: Functor
|
val functor: FunctorInfo
|
||||||
val clauses: MutableList<Clause>
|
val clauses: MutableList<Clause>
|
||||||
var dynamic = false
|
var dynamic = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a predicate with the given functor and an empty list of clauses.
|
* Creates a predicate with the given functor and an empty list of clauses.
|
||||||
*/
|
*/
|
||||||
constructor(functor: Functor, dynamic: Boolean = false) {
|
constructor(functor: FunctorInfo, dynamic: Boolean = false) {
|
||||||
this.functor = functor
|
this.functor = functor
|
||||||
this.clauses = mutableListOf()
|
this.clauses = mutableListOf()
|
||||||
this.dynamic = dynamic
|
this.dynamic = dynamic
|
||||||
|
|
|
@ -4,9 +4,10 @@ import prolog.Answers
|
||||||
import prolog.Substitutions
|
import prolog.Substitutions
|
||||||
import prolog.ast.logic.Resolvent
|
import prolog.ast.logic.Resolvent
|
||||||
import prolog.logic.unifyLazy
|
import prolog.logic.unifyLazy
|
||||||
|
import prolog.ast.arithmetic.Integer
|
||||||
|
|
||||||
open class Atom(val name: String) : Goal(), Head, Body, Resolvent {
|
open class Atom(val name: String) : Goal(), Head, Body, Resolvent {
|
||||||
override val functor: Functor = "$name/_"
|
override val functor: FunctorInfo = FunctorInfo(this, Integer(0))
|
||||||
|
|
||||||
override fun solve(goal: Goal, subs: Substitutions): Answers = unifyLazy(goal, this, subs)
|
override fun solve(goal: Goal, subs: Substitutions): Answers = unifyLazy(goal, this, subs)
|
||||||
|
|
||||||
|
|
29
src/prolog/ast/terms/FunctorInfo.kt
Normal file
29
src/prolog/ast/terms/FunctorInfo.kt
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package prolog.ast.terms
|
||||||
|
|
||||||
|
import prolog.Substitutions
|
||||||
|
import prolog.ast.arithmetic.Integer
|
||||||
|
|
||||||
|
data class FunctorInfo(val name: Atom, val arity: Integer) : Term {
|
||||||
|
companion object {
|
||||||
|
fun of(functor: String): FunctorInfo {
|
||||||
|
// Split the functor string into name and arity, by splitting the last "/"
|
||||||
|
val lastSlash = functor.lastIndexOf('/')
|
||||||
|
val name = Atom(functor.substring(0, lastSlash))
|
||||||
|
val arity = Integer(functor.substring(lastSlash + 1).toIntOrNull() ?: 0)
|
||||||
|
return FunctorInfo(name, arity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String = "${name.name}/$arity"
|
||||||
|
override fun applySubstitution(subs: Substitutions) : FunctorInfo = this
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (other == null) return false
|
||||||
|
if (other !is FunctorInfo && other !is String) return false
|
||||||
|
if (other is FunctorInfo) {
|
||||||
|
return this.name.toString() == other.name.toString() && this.arity == other.arity
|
||||||
|
}
|
||||||
|
return this.toString() == other
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
package prolog.ast.terms
|
package prolog.ast.terms
|
||||||
|
|
||||||
import prolog.Answers
|
import prolog.Answers
|
||||||
import prolog.ast.Database.Program
|
|
||||||
import prolog.Substitutions
|
import prolog.Substitutions
|
||||||
|
import prolog.ast.Database.Program
|
||||||
import prolog.ast.logic.LogicOperand
|
import prolog.ast.logic.LogicOperand
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -13,7 +13,7 @@ import prolog.ast.logic.LogicOperand
|
||||||
* or it fails if Prolog fails to prove it.
|
* or it fails if Prolog fails to prove it.
|
||||||
*/
|
*/
|
||||||
abstract class Goal : LogicOperand(), Term {
|
abstract class Goal : LogicOperand(), Term {
|
||||||
abstract val functor: Functor
|
abstract val functor: FunctorInfo
|
||||||
|
|
||||||
override fun satisfy(subs: Substitutions): Answers = Program.solve(this, subs)
|
override fun satisfy(subs: Substitutions): Answers = Program.solve(this, subs)
|
||||||
}
|
}
|
|
@ -4,7 +4,5 @@ package prolog.ast.terms
|
||||||
* Part of a [Clause][prolog.ast.logic.Clause] before the neck operator.
|
* Part of a [Clause][prolog.ast.logic.Clause] before the neck operator.
|
||||||
*/
|
*/
|
||||||
interface Head : Term {
|
interface Head : Term {
|
||||||
val functor: Functor
|
val functor: FunctorInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
typealias Functor = String
|
|
||||||
|
|
|
@ -5,13 +5,14 @@ import prolog.Substitutions
|
||||||
import prolog.ast.logic.Resolvent
|
import prolog.ast.logic.Resolvent
|
||||||
import prolog.logic.applySubstitution
|
import prolog.logic.applySubstitution
|
||||||
import prolog.logic.unifyLazy
|
import prolog.logic.unifyLazy
|
||||||
|
import prolog.ast.arithmetic.Integer
|
||||||
|
|
||||||
typealias Argument = Term
|
typealias Argument = Term
|
||||||
|
|
||||||
typealias CompoundTerm = Structure
|
typealias CompoundTerm = Structure
|
||||||
|
|
||||||
open class Structure(val name: Atom, var arguments: List<Argument>) : Goal(), Head, Body, Resolvent {
|
open class Structure(val name: Atom, var arguments: List<Argument>) : Goal(), Head, Body, Resolvent {
|
||||||
override val functor: Functor = "${name.name}/${arguments.size}"
|
override val functor: FunctorInfo = FunctorInfo(name, Integer(arguments.size))
|
||||||
|
|
||||||
override fun solve(goal: Goal, subs: Substitutions): Answers {
|
override fun solve(goal: Goal, subs: Substitutions): Answers {
|
||||||
return unifyLazy(goal, this, subs)
|
return unifyLazy(goal, this, subs)
|
||||||
|
|
65
src/prolog/builtins/analysingAndConstructionOperators.kt
Normal file
65
src/prolog/builtins/analysingAndConstructionOperators.kt
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
package prolog.builtins
|
||||||
|
|
||||||
|
import prolog.Answers
|
||||||
|
import prolog.Substitutions
|
||||||
|
import prolog.ast.arithmetic.Integer
|
||||||
|
import prolog.ast.terms.AnonymousVariable
|
||||||
|
import prolog.ast.terms.Atom
|
||||||
|
import prolog.ast.terms.Head
|
||||||
|
import prolog.ast.terms.Structure
|
||||||
|
import prolog.ast.terms.Term
|
||||||
|
import prolog.ast.terms.Variable
|
||||||
|
import prolog.logic.applySubstitution
|
||||||
|
import prolog.logic.atomic
|
||||||
|
import prolog.logic.nonvariable
|
||||||
|
import prolog.logic.unifyLazy
|
||||||
|
import prolog.logic.variable
|
||||||
|
|
||||||
|
class Functor(private val term: Term, private val functorName: Term, private val functorArity: Term) :
|
||||||
|
Structure(Atom("functor"), listOf(term, functorName, functorArity)) {
|
||||||
|
override fun satisfy(subs: Substitutions): Answers {
|
||||||
|
if (nonvariable(term, subs)) {
|
||||||
|
val t = applySubstitution(term, subs) as Head
|
||||||
|
|
||||||
|
return Conjunction(
|
||||||
|
Unify(t.functor.arity, functorArity),
|
||||||
|
Unify(t.functor.name, functorName)
|
||||||
|
).satisfy(subs)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (variable(term, subs)) {
|
||||||
|
require(atomic(functorName, subs) && atomic(functorArity, subs)) {
|
||||||
|
"Arguments are not sufficiently instantiated"
|
||||||
|
}
|
||||||
|
|
||||||
|
val name = applySubstitution(functorName, subs) as Atom
|
||||||
|
val arity = applySubstitution(functorArity, subs) as Integer
|
||||||
|
|
||||||
|
val result = if (arity.value == 0) {
|
||||||
|
functorName
|
||||||
|
} else {
|
||||||
|
val argList = mutableListOf<Term>()
|
||||||
|
for (i in 1..arity.value) {
|
||||||
|
argList.add(AnonymousVariable.create())
|
||||||
|
}
|
||||||
|
Structure(name, argList)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nonvariable(term, subs)) {
|
||||||
|
return unifyLazy(term, result, subs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sequenceOf(Result.success(mapOf(term to result)))
|
||||||
|
}
|
||||||
|
|
||||||
|
throw IllegalStateException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun applySubstitution(subs: Substitutions): Functor = Functor(
|
||||||
|
term.applySubstitution(subs),
|
||||||
|
functorName.applySubstitution(subs),
|
||||||
|
functorArity.applySubstitution(subs)
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun toString(): String = "functor($term, $functorName, $functorArity)"
|
||||||
|
}
|
|
@ -3,26 +3,21 @@ package prolog.builtins
|
||||||
import io.Logger
|
import io.Logger
|
||||||
import prolog.Answers
|
import prolog.Answers
|
||||||
import prolog.Substitutions
|
import prolog.Substitutions
|
||||||
import prolog.ast.logic.Clause
|
|
||||||
import prolog.ast.terms.Atom
|
|
||||||
import prolog.ast.terms.Structure
|
|
||||||
import prolog.ast.logic.Predicate
|
|
||||||
import prolog.ast.Database.Program
|
|
||||||
import prolog.ast.terms.Functor
|
|
||||||
import prolog.ast.terms.Term
|
|
||||||
import prolog.ast.logic.Fact
|
|
||||||
import prolog.ast.Database
|
import prolog.ast.Database
|
||||||
import prolog.ast.terms.Body
|
import prolog.ast.Database.Program
|
||||||
import prolog.ast.terms.Goal
|
import prolog.ast.arithmetic.Integer
|
||||||
import prolog.ast.terms.Operator
|
import prolog.ast.logic.Clause
|
||||||
|
import prolog.ast.logic.Fact
|
||||||
|
import prolog.ast.logic.Predicate
|
||||||
|
import prolog.ast.terms.*
|
||||||
import prolog.logic.applySubstitution
|
import prolog.logic.applySubstitution
|
||||||
import prolog.logic.unifyLazy
|
import prolog.logic.unifyLazy
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* (Make) the [Predicate] with the corresponding [Functor] dynamic.
|
* (Make) the [Predicate] with the corresponding [FunctorInfo] dynamic.
|
||||||
*/
|
*/
|
||||||
class Dynamic(private val dynamicFunctor: Functor): Goal(), Body {
|
class Dynamic(private val dynamicFunctor: FunctorInfo) : Goal(), Body {
|
||||||
override val functor: Functor = "dynamic/1"
|
override val functor = FunctorInfo(Atom("dynamic"), Integer(1))
|
||||||
|
|
||||||
override fun satisfy(subs: Substitutions): Answers {
|
override fun satisfy(subs: Substitutions): Answers {
|
||||||
val predicate = Program.db.predicates[dynamicFunctor]
|
val predicate = Program.db.predicates[dynamicFunctor]
|
||||||
|
@ -48,7 +43,7 @@ class Dynamic(private val dynamicFunctor: Functor): Goal(), Body {
|
||||||
}
|
}
|
||||||
|
|
||||||
class Assert(clause: Clause) : AssertZ(clause) {
|
class Assert(clause: Clause) : AssertZ(clause) {
|
||||||
override val functor: Functor = "assert/1"
|
override val functor = FunctorInfo(Atom("assert"), Integer(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -612,7 +612,7 @@ class PreprocessorTests {
|
||||||
Atom("declaration/1")
|
Atom("declaration/1")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val expected = Dynamic("declaration/1")
|
val expected = Dynamic(FunctorInfo.of("declaration/1"))
|
||||||
|
|
||||||
val result = preprocessor.preprocess(input)
|
val result = preprocessor.preprocess(input)
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ import prolog.ast.terms.CompoundTerm
|
||||||
import prolog.ast.terms.Structure
|
import prolog.ast.terms.Structure
|
||||||
import prolog.ast.terms.Variable
|
import prolog.ast.terms.Variable
|
||||||
import prolog.builtins.Conjunction
|
import prolog.builtins.Conjunction
|
||||||
|
import prolog.ast.terms.FunctorInfo
|
||||||
|
|
||||||
class LogicGrammarTests {
|
class LogicGrammarTests {
|
||||||
private lateinit var parser: Grammar<List<Clause>>
|
private lateinit var parser: Grammar<List<Clause>>
|
||||||
|
@ -94,13 +95,13 @@ class LogicGrammarTests {
|
||||||
|
|
||||||
assertTrue(rule.head is Structure, "Expected head to be a structure")
|
assertTrue(rule.head is Structure, "Expected head to be a structure")
|
||||||
val head = rule.head as Structure
|
val head = rule.head as Structure
|
||||||
assertEquals("parent/2", head.functor, "Expected functor 'parent/2'")
|
assertEquals(FunctorInfo.of("parent/2"), head.functor, "Expected functor 'parent/2'")
|
||||||
assertEquals(Variable("X"), head.arguments[0], "Expected first argument 'X'")
|
assertEquals(Variable("X"), head.arguments[0], "Expected first argument 'X'")
|
||||||
assertEquals(Variable("Y"), head.arguments[1], "Expected second argument 'Y'")
|
assertEquals(Variable("Y"), head.arguments[1], "Expected second argument 'Y'")
|
||||||
|
|
||||||
assertTrue(rule.body is Structure, "Expected body to be a structure")
|
assertTrue(rule.body is Structure, "Expected body to be a structure")
|
||||||
val body = rule.body as Structure
|
val body = rule.body as Structure
|
||||||
assertEquals("father/2", body.functor, "Expected functor 'father/2'")
|
assertEquals(FunctorInfo.of("father/2"), body.functor, "Expected functor 'father/2'")
|
||||||
assertEquals(Variable("X"), body.arguments[0], "Expected first argument 'X'")
|
assertEquals(Variable("X"), body.arguments[0], "Expected first argument 'X'")
|
||||||
assertEquals(Variable("Y"), body.arguments[1], "Expected second argument 'Y'")
|
assertEquals(Variable("Y"), body.arguments[1], "Expected second argument 'Y'")
|
||||||
}
|
}
|
||||||
|
@ -125,12 +126,12 @@ class LogicGrammarTests {
|
||||||
|
|
||||||
assertEquals(1, result.size, "Expected 1 rule")
|
assertEquals(1, result.size, "Expected 1 rule")
|
||||||
val rule = result[0] as Rule
|
val rule = result[0] as Rule
|
||||||
assertEquals("guest/2", rule.head.functor, "Expected functor 'guest/2'")
|
assertEquals(FunctorInfo.of("guest/2"), rule.head.functor, "Expected functor 'guest/2'")
|
||||||
assertEquals(",/2", (rule.body as CompoundTerm).functor, "Expected functor ',/2'")
|
assertEquals(FunctorInfo.of(",/2"), (rule.body as CompoundTerm).functor, "Expected functor ',/2'")
|
||||||
val l1 = (rule.body as CompoundTerm).arguments[0] as CompoundTerm
|
val l1 = (rule.body as CompoundTerm).arguments[0] as CompoundTerm
|
||||||
assertEquals(",/2", l1.functor, "Expected functor ',/2'")
|
assertEquals(FunctorInfo.of(",/2"), l1.functor, "Expected functor ',/2'")
|
||||||
val l2 = l1.arguments[0] as CompoundTerm
|
val l2 = l1.arguments[0] as CompoundTerm
|
||||||
assertEquals("invited/2", l2.functor, "Expected functor 'invited/2'")
|
assertEquals(FunctorInfo.of("invited/2"), l2.functor, "Expected functor 'invited/2'")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -157,6 +158,6 @@ class LogicGrammarTests {
|
||||||
assertEquals(1, result.size, "Expected 1 rule")
|
assertEquals(1, result.size, "Expected 1 rule")
|
||||||
assertTrue(result[0] is Rule, "Expected a rule")
|
assertTrue(result[0] is Rule, "Expected a rule")
|
||||||
val rule = result[0] as Rule
|
val rule = result[0] as Rule
|
||||||
assertEquals("/_", rule.head.functor, "Expected a constraint")
|
assertEquals(FunctorInfo.of("/0"), rule.head.functor, "Expected a constraint")
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
package prolog.builtins
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.Assertions.assertInstanceOf
|
||||||
|
import org.junit.jupiter.api.Assertions.assertTrue
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import prolog.ast.arithmetic.Integer
|
||||||
|
import prolog.ast.terms.AnonymousVariable
|
||||||
|
import prolog.ast.terms.Atom
|
||||||
|
import prolog.ast.terms.Structure
|
||||||
|
import prolog.ast.terms.Variable
|
||||||
|
|
||||||
|
class AnalysingAndConstructionOperatorsTests {
|
||||||
|
@Test
|
||||||
|
fun `functor(foo, foo, 0)`() {
|
||||||
|
val functor = Functor(Atom("foo"), Atom("foo"), Integer(0))
|
||||||
|
|
||||||
|
val result = functor.satisfy(emptyMap()).toList()
|
||||||
|
|
||||||
|
assertEquals(1, result.size, "Expected 1 result")
|
||||||
|
assertTrue(result[0].isSuccess, "Expected success")
|
||||||
|
assertTrue(result[0].getOrNull()!!.isEmpty(), "Expected empty substitutions")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `functor(foo(X), foo, Y)`() {
|
||||||
|
val functor = Functor(
|
||||||
|
Structure(Atom("foo"), listOf(Variable("X"))),
|
||||||
|
Atom("foo"),
|
||||||
|
Variable("Y")
|
||||||
|
)
|
||||||
|
|
||||||
|
val result = functor.satisfy(emptyMap()).toList()
|
||||||
|
|
||||||
|
assertEquals(1, result.size, "Expected 1 result")
|
||||||
|
assertTrue(result[0].isSuccess, "Expected success")
|
||||||
|
val subs = result[0].getOrNull()!!
|
||||||
|
assertEquals(1, subs.size, "Expected 1 substitution")
|
||||||
|
assertEquals(Integer(1), subs[Variable("Y")])
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `functor(foo, X, Y)`() {
|
||||||
|
val atom = Atom("foo")
|
||||||
|
val functor = Functor(atom, Variable("X"), Variable("Y"))
|
||||||
|
|
||||||
|
val result = functor.satisfy(emptyMap()).toList()
|
||||||
|
|
||||||
|
assertEquals(1, result.size, "Expected 1 result")
|
||||||
|
assertTrue(result[0].isSuccess, "Expected success")
|
||||||
|
val subs = result[0].getOrNull()!!
|
||||||
|
assertEquals(2, subs.size, "Expected 2 substitutions")
|
||||||
|
assertEquals(atom.functor.name, subs[Variable("X")])
|
||||||
|
assertEquals(atom.functor.arity, subs[Variable("Y")])
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `functor(X, foo, 1)`() {
|
||||||
|
val functor = Functor(Variable("X"), Atom("foo"), Integer(1))
|
||||||
|
|
||||||
|
val result = functor.satisfy(emptyMap()).toList()
|
||||||
|
|
||||||
|
assertEquals(1, result.size, "Expected 1 result")
|
||||||
|
assertTrue(result[0].isSuccess, "Expected success")
|
||||||
|
val subs = result[0].getOrNull()!!
|
||||||
|
assertEquals(1, subs.size, "Expected 1 substitution")
|
||||||
|
assertInstanceOf(Structure::class.java, subs[Variable("X")])
|
||||||
|
val structure = subs[Variable("X")] as Structure
|
||||||
|
assertEquals(Atom("foo"), structure.name)
|
||||||
|
assertEquals(1, structure.arguments.size)
|
||||||
|
assertInstanceOf(AnonymousVariable::class.java, structure.arguments[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `functor(foo(a), foo, 0)`() {
|
||||||
|
val functor = Functor(Structure(Atom("foo"), listOf(Atom("a"))), Atom("foo"), Integer(0))
|
||||||
|
|
||||||
|
val result = functor.satisfy(emptyMap()).toList()
|
||||||
|
|
||||||
|
assertTrue(result.isEmpty(), "Expected no results")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `functor(foo(X), foo, 0)`() {
|
||||||
|
val functor = Functor(Structure(Atom("foo"), listOf(Variable("X"))), Atom("foo"), Integer(0))
|
||||||
|
|
||||||
|
val result = functor.satisfy(emptyMap()).toList()
|
||||||
|
|
||||||
|
assertTrue(result.isEmpty(), "Expected no results")
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,14 +8,13 @@ import org.junit.jupiter.api.Nested
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.junit.jupiter.params.ParameterizedTest
|
import org.junit.jupiter.params.ParameterizedTest
|
||||||
import org.junit.jupiter.params.provider.ValueSource
|
import org.junit.jupiter.params.provider.ValueSource
|
||||||
import prolog.ast.Database
|
|
||||||
import prolog.ast.Database.Program
|
import prolog.ast.Database.Program
|
||||||
import prolog.ast.logic.Clause
|
import prolog.ast.logic.Clause
|
||||||
import prolog.ast.logic.Fact
|
import prolog.ast.logic.Fact
|
||||||
import prolog.ast.logic.Predicate
|
import prolog.ast.logic.Predicate
|
||||||
import prolog.ast.logic.Rule
|
import prolog.ast.logic.Rule
|
||||||
import prolog.ast.terms.Atom
|
import prolog.ast.terms.Atom
|
||||||
import prolog.ast.terms.Functor
|
import prolog.ast.terms.FunctorInfo
|
||||||
import prolog.ast.terms.Structure
|
import prolog.ast.terms.Structure
|
||||||
import prolog.ast.terms.Variable
|
import prolog.ast.terms.Variable
|
||||||
|
|
||||||
|
@ -40,7 +39,7 @@ class DatabaseOperatorsTests {
|
||||||
createAssert(fact).satisfy(emptyMap())
|
createAssert(fact).satisfy(emptyMap())
|
||||||
|
|
||||||
assertEquals(1, Program.db.predicates.size, "Expected 1 predicate")
|
assertEquals(1, Program.db.predicates.size, "Expected 1 predicate")
|
||||||
assertEquals(fact, Program.db.predicates["a/_"]!!.clauses[0])
|
assertEquals(fact, Program.db.predicates[FunctorInfo.of("a/_")]!!.clauses[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -49,7 +48,7 @@ class DatabaseOperatorsTests {
|
||||||
createAssert(fact).satisfy(emptyMap())
|
createAssert(fact).satisfy(emptyMap())
|
||||||
|
|
||||||
assertEquals(1, Program.db.predicates.size, "Expected 1 predicate")
|
assertEquals(1, Program.db.predicates.size, "Expected 1 predicate")
|
||||||
assertEquals(fact, Program.db.predicates["a/1"]!!.clauses[0])
|
assertEquals(fact, Program.db.predicates[FunctorInfo.of("a/1")]!!.clauses[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -61,7 +60,7 @@ class DatabaseOperatorsTests {
|
||||||
createAssert(rule).satisfy(emptyMap())
|
createAssert(rule).satisfy(emptyMap())
|
||||||
|
|
||||||
assertEquals(1, Program.db.predicates.size, "Expected 1 predicate")
|
assertEquals(1, Program.db.predicates.size, "Expected 1 predicate")
|
||||||
assertEquals(rule, Program.db.predicates["a/1"]!!.clauses[0])
|
assertEquals(rule, Program.db.predicates[FunctorInfo.of("a/1")]!!.clauses[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,8 +91,8 @@ class DatabaseOperatorsTests {
|
||||||
AssertA(rule2).satisfy(emptyMap())
|
AssertA(rule2).satisfy(emptyMap())
|
||||||
|
|
||||||
assertEquals(1, Program.db.predicates.size, "Expected 1 predicate")
|
assertEquals(1, Program.db.predicates.size, "Expected 1 predicate")
|
||||||
assertEquals(rule2, Program.db.predicates["a/1"]!!.clauses[0])
|
assertEquals(rule2, Program.db.predicates[FunctorInfo.of("a/1")]!!.clauses[0])
|
||||||
assertEquals(rule1, Program.db.predicates["a/1"]!!.clauses[1])
|
assertEquals(rule1, Program.db.predicates[FunctorInfo.of("a/1")]!!.clauses[1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,8 +116,8 @@ class DatabaseOperatorsTests {
|
||||||
AssertZ(rule2).satisfy(emptyMap())
|
AssertZ(rule2).satisfy(emptyMap())
|
||||||
|
|
||||||
assertEquals(1, Program.db.predicates.size, "Expected 1 predicate")
|
assertEquals(1, Program.db.predicates.size, "Expected 1 predicate")
|
||||||
assertEquals(rule1, Program.db.predicates["a/1"]!!.clauses[0])
|
assertEquals(rule1, Program.db.predicates[FunctorInfo.of("a/1")]!!.clauses[0])
|
||||||
assertEquals(rule2, Program.db.predicates["a/1"]!!.clauses[1])
|
assertEquals(rule2, Program.db.predicates[FunctorInfo.of("a/1")]!!.clauses[1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,20 +308,20 @@ class DatabaseOperatorsTests {
|
||||||
|
|
||||||
val control = Program.query(Structure(Atom("a"), listOf(Variable("X")))).toList()
|
val control = Program.query(Structure(Atom("a"), listOf(Variable("X")))).toList()
|
||||||
assertEquals(3, control.size, "Expected 3 results")
|
assertEquals(3, control.size, "Expected 3 results")
|
||||||
assertEquals(3, Program.db.predicates["a/1"]!!.clauses.size, "Expected 3 clauses")
|
assertEquals(3, Program.db.predicates[FunctorInfo.of("a/1")]!!.clauses.size, "Expected 3 clauses")
|
||||||
|
|
||||||
val retract = RetractAll(Structure(Atom("a"), listOf(Variable("X"))))
|
val retract = RetractAll(Structure(Atom("a"), listOf(Variable("X"))))
|
||||||
val result = retract.satisfy(emptyMap()).toList()
|
val result = retract.satisfy(emptyMap()).toList()
|
||||||
|
|
||||||
assertEquals(1, result.size, "Expected 1 result")
|
assertEquals(1, result.size, "Expected 1 result")
|
||||||
assertTrue(result[0].isSuccess, "Expected success")
|
assertTrue(result[0].isSuccess, "Expected success")
|
||||||
assertEquals(0, Program.db.predicates["a/1"]!!.clauses.size, "Expected 0 clauses")
|
assertEquals(0, Program.db.predicates[FunctorInfo.of("a/1")]!!.clauses.size, "Expected 0 clauses")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `If Head refers to a predicate that is not defined, it is implicitly created as a dynamic predicate`() {
|
fun `If Head refers to a predicate that is not defined, it is implicitly created as a dynamic predicate`() {
|
||||||
val predicateName = "idonotyetexist"
|
val predicateName = "idonotyetexist"
|
||||||
val predicateFunctor = "$predicateName/1"
|
val predicateFunctor = FunctorInfo.of("$predicateName/1")
|
||||||
|
|
||||||
assertFalse(predicateFunctor in Program.db.predicates, "Expected predicate to not exist before")
|
assertFalse(predicateFunctor in Program.db.predicates, "Expected predicate to not exist before")
|
||||||
|
|
||||||
|
|
Reference in a new issue