refactor: Herstructurering

This commit is contained in:
Tibo De Peuter 2025-04-06 19:16:50 +02:00
parent 1acd1cfb67
commit e3c84e1761
Signed by: tdpeuter
GPG key ID: 38297DE43F75FFE2
33 changed files with 290 additions and 178 deletions

View file

@ -1,12 +1,15 @@
package prolog.components package prolog
import prolog.Substituted import prolog.logic.Substituted
import prolog.ast.logic.Clause
import prolog.ast.logic.Fact
import prolog.ast.logic.Predicate
import prolog.ast.logic.Resolvent
import prolog.ast.terms.Functor
import prolog.ast.terms.Goal
import prolog.builtins.True import prolog.builtins.True
import prolog.components.expressions.Clause
import prolog.components.expressions.Fact typealias Database = Program
import prolog.components.expressions.Predicate
import prolog.components.terms.Functor
import prolog.components.terms.Goal
/** /**
* Prolog Program or database. * Prolog Program or database.
@ -29,7 +32,7 @@ object Program: Resolvent {
* Queries the program with a goal. * Queries the program with a goal.
* @return true if the goal can be proven, false otherwise. * @return true if the goal can be proven, false otherwise.
*/ */
fun query(goal: Goal): Boolean = solve(goal, emptyMap()).toList().isNotEmpty() fun query(goal: Goal): Sequence<Substituted> = solve(goal, emptyMap())
override fun solve(goal: Goal, subs: Substituted): Sequence<Substituted> { override fun solve(goal: Goal, subs: Substituted): Sequence<Substituted> {
val functor = goal.functor val functor = goal.functor
@ -75,5 +78,3 @@ object Program: Resolvent {
setup() setup()
} }
} }
typealias Database = Program

View file

@ -1,18 +1,19 @@
package prolog.components.expressions package prolog.ast.logic
import prolog.Substituted import prolog.logic.Substituted
import prolog.ast.terms.Body
import prolog.ast.terms.Functor
import prolog.ast.terms.Goal
import prolog.ast.terms.Head
import prolog.builtins.True import prolog.builtins.True
import prolog.builtins.equivalent import prolog.logic.unifyLazy
import prolog.components.Resolvent
import prolog.components.terms.*
import prolog.unifyLazy
/** /**
* Sentence of a Prolog program. * Sentence of a Prolog program.
* *
* A clause consists of a [Head] and body separated by the [neck][prolog.terms.Neck] operator, or it is a [Fact]. * A clause consists of a [Head] and body separated by the neck operator, or it is a [Fact].
* *
* @see [prolog.components.terms.Variable] * @see [prolog.ast.terms.Variable]
* @see [Predicate] * @see [Predicate]
*/ */
abstract class Clause(private val head: Head, private val body: Body) : Resolvent { abstract class Clause(private val head: Head, private val body: Body) : Resolvent {

View file

@ -0,0 +1,6 @@
package prolog.ast.logic
import prolog.ast.terms.Head
import prolog.builtins.True
class Fact(head: Head) : Clause(head, True())

View file

@ -1,9 +1,8 @@
package prolog.components.expressions package prolog.ast.logic
import prolog.Substituted import prolog.logic.Substituted
import prolog.components.Resolvent import prolog.ast.terms.Functor
import prolog.components.terms.Functor import prolog.ast.terms.Goal
import prolog.components.terms.Goal
/** /**
* Collection of [Clause]s with the same [Functor]. * Collection of [Clause]s with the same [Functor].
@ -37,7 +36,7 @@ class Predicate : Resolvent {
* Adds a clause to the predicate. * Adds a clause to the predicate.
*/ */
fun add(clause: Clause) { fun add(clause: Clause) {
require (clause.functor == functor) { "Clause functor does not match predicate functor" } require(clause.functor == functor) { "Clause functor does not match predicate functor" }
clauses.add(clause) clauses.add(clause)
} }

View file

@ -1,6 +1,6 @@
package prolog.components package prolog.ast.logic
import prolog.Substituted import prolog.logic.Substituted
interface Provable { interface Provable {
/** /**

View file

@ -1,7 +1,7 @@
package prolog.components package prolog.ast.logic
import prolog.Substituted import prolog.logic.Substituted
import prolog.components.terms.Goal import prolog.ast.terms.Goal
/** /**
* Can be instructed to solve a goal. * Can be instructed to solve a goal.

View file

@ -0,0 +1,6 @@
package prolog.ast.logic
import prolog.ast.terms.Body
import prolog.ast.terms.Head
class Rule(head: Head, body: Body) : Clause(head, body)

View file

@ -1,8 +1,8 @@
package prolog.components.terms package prolog.ast.terms
import prolog.Substituted import prolog.ast.logic.Resolvent
import prolog.components.Resolvent import prolog.logic.Substituted
import prolog.unifyLazy import prolog.logic.unifyLazy
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: Functor = "$name/_"
@ -11,10 +11,10 @@ open class Atom(val name: String) : Goal(), Head, Body, Resolvent {
override fun compareTo(other: Term): Int { override fun compareTo(other: Term): Int {
return when (other) { return when (other) {
is Variable -> 1 is Variable -> 1
is Atom -> name.compareTo(other.name) is Atom -> name.compareTo(other.name)
is Structure -> -1 is Structure -> -1
else -> throw IllegalArgumentException("Cannot compare $this with $other") else -> throw IllegalArgumentException("Cannot compare $this with $other")
} }
} }

View file

@ -0,0 +1,5 @@
package prolog.ast.terms
import prolog.ast.logic.Provable
interface Body : Provable

View file

@ -1,8 +1,8 @@
package prolog.components.terms package prolog.ast.terms
import prolog.Substituted import prolog.Program
import prolog.components.Program import prolog.ast.logic.Provable
import prolog.components.Provable import prolog.logic.Substituted
/** /**
* Ask the Prolog engine. * Ask the Prolog engine.

View file

@ -0,0 +1,10 @@
package prolog.ast.terms
/**
* Part of a [Clause][prolog.ast.logic.Clause] before the neck operator.
*/
interface Head : Term {
val functor: Functor
}
typealias Functor = String

View file

@ -1,12 +1,7 @@
package prolog.components.expressions package prolog.ast.terms
import prolog.Substituted import prolog.ast.logic.Provable
import prolog.components.Provable import prolog.logic.Substituted
import prolog.components.terms.Atom
import prolog.components.terms.CompoundTerm
import prolog.components.terms.Goal
typealias Operand = Goal
abstract class Operator( abstract class Operator(
private val symbol: Atom, private val symbol: Atom,
@ -22,3 +17,5 @@ abstract class Operator(
} }
} }
} }
typealias Operand = Goal

View file

@ -1,13 +1,15 @@
package prolog.components.terms package prolog.ast.terms
import prolog.Substituted import prolog.ast.logic.Resolvent
import prolog.builtins.equivalent import prolog.builtins.equivalent
import prolog.components.Resolvent import prolog.logic.Substituted
import prolog.unifyLazy import prolog.logic.unifyLazy
typealias Argument = Term typealias Argument = Term
open class Structure(val name: Atom, val arguments: List<Argument>): Goal(), Head, Body, Resolvent { typealias CompoundTerm = Structure
open class Structure(val name: Atom, val arguments: List<Argument>) : Goal(), Head, Body, Resolvent {
override val functor: Functor = "${name.name}/${arguments.size}" override val functor: Functor = "${name.name}/${arguments.size}"
override fun solve(goal: Goal, subs: Substituted): Sequence<Substituted> { override fun solve(goal: Goal, subs: Substituted): Sequence<Substituted> {
@ -39,5 +41,3 @@ open class Structure(val name: Atom, val arguments: List<Argument>): Goal(), Hea
} }
} }
} }
typealias CompoundTerm = Structure

View file

@ -0,0 +1,9 @@
package prolog.ast.terms
/**
* Value in Prolog.
*
* A [Term] is either a [Variable], [Atom], integer, float or [CompoundTerm].
* In addition, SWI-Prolog also defines the type string.
*/
interface Term : Comparable<Term>

View file

@ -1,6 +1,6 @@
package prolog.components.terms package prolog.ast.terms
import java.util.Optional import java.util.*
data class Variable(val name: String) : Term { data class Variable(val name: String) : Term {
private var alias: Optional<Term> = Optional.empty() private var alias: Optional<Term> = Optional.empty()
@ -23,9 +23,9 @@ data class Variable(val name: String) : Term {
override fun compareTo(other: Term): Int { override fun compareTo(other: Term): Int {
return when (other) { return when (other) {
is Variable -> name.compareTo(other.name) is Variable -> name.compareTo(other.name)
// Variables are always less than atoms // Variables are always less than atoms
else -> -1 else -> -1
} }
} }

View file

@ -0,0 +1 @@
package prolog.builtins

View file

@ -1,15 +1,12 @@
package prolog.builtins package prolog.builtins
import prolog.Substituted import prolog.ast.terms.*
import prolog.components.expressions.Operand import prolog.logic.Substituted
import prolog.components.expressions.Operator
import prolog.components.terms.Atom
import prolog.components.terms.Body
/** /**
* Always fail. * Always fail.
*/ */
class Fail: Atom("fail"), Body { class Fail : Atom("fail"), Body {
override fun prove(subs: Substituted): Sequence<Substituted> = emptySequence() override fun prove(subs: Substituted): Sequence<Substituted> = emptySequence()
} }
@ -21,7 +18,7 @@ typealias False = Fail
/** /**
* Always succeed. * Always succeed.
*/ */
class True: Atom("true"), Body { class True : Atom("true"), Body {
override fun prove(subs: Substituted): Sequence<Substituted> = sequenceOf(emptyMap()) override fun prove(subs: Substituted): Sequence<Substituted> = sequenceOf(emptyMap())
} }
@ -49,7 +46,7 @@ class Conjunction(leftOperand: Operand, rightOperand: Operand) : Operator(Atom("
/** /**
* Disjunction (or). True if either Goal1 or Goal2 succeeds. * Disjunction (or). True if either Goal1 or Goal2 succeeds.
*/ */
class Disjunction(leftOperand: Operand, rightOperand: Operand) : Operator(Atom(";"), leftOperand, rightOperand) { open class Disjunction(leftOperand: Operand, rightOperand: Operand) : Operator(Atom(";"), leftOperand, rightOperand) {
override fun prove(subs: Substituted): Sequence<Substituted> = sequence { override fun prove(subs: Substituted): Sequence<Substituted> = sequence {
if (leftOperand != null) { if (leftOperand != null) {
yieldAll(leftOperand.prove(subs)) yieldAll(leftOperand.prove(subs))
@ -57,3 +54,24 @@ class Disjunction(leftOperand: Operand, rightOperand: Operand) : Operator(Atom("
yieldAll(rightOperand.prove(subs)) yieldAll(rightOperand.prove(subs))
} }
} }
@Deprecated("Use Disjunction instead")
class Bar(leftOperand: Operand, rightOperand: Operand) : Disjunction(leftOperand, rightOperand)
// TODO ->
// TODO *->
/**
* True if 'Goal' cannot be proven.
*/
class Not(goal: Goal) : Operator(Atom("\\+"), rightOperand = goal) {
override fun prove(subs: Substituted): Sequence<Substituted> {
// If the goal can be proven, return an empty sequence
if (rightOperand.prove(subs).toList().isNotEmpty()) {
return emptySequence()
}
// If the goal cannot be proven, return a sequence with an empty map
return sequenceOf(emptyMap())
}
}

View file

@ -0,0 +1 @@
package prolog.builtins

View file

@ -1,9 +1,9 @@
package prolog.builtins package prolog.builtins
import prolog.Substituted import prolog.ast.terms.Atom
import prolog.components.expressions.Operand import prolog.ast.terms.Operand
import prolog.components.expressions.Operator import prolog.ast.terms.Operator
import prolog.components.terms.Atom import prolog.logic.Substituted
class Query(rightOperand: Operand) : Operator(Atom("?-"), null, rightOperand) { class Query(rightOperand: Operand) : Operator(Atom("?-"), null, rightOperand) {
override fun prove(subs: Substituted): Sequence<Substituted> = rightOperand.prove(subs) override fun prove(subs: Substituted): Sequence<Substituted> = rightOperand.prove(subs)

View file

@ -1,7 +1,7 @@
package prolog.builtins package prolog.builtins
import prolog.components.terms.Atom import prolog.ast.terms.Atom
import prolog.components.terms.Term import prolog.ast.terms.Term
/** /**
* True when Term is a term with functor Name/Arity. If Term is a variable it is unified with a new term whose * True when Term is a term with functor Name/Arity. If Term is a variable it is unified with a new term whose

View file

@ -1,9 +1,9 @@
package prolog.builtins package prolog.builtins
import prolog.components.terms.Atom import prolog.ast.terms.Atom
import prolog.components.terms.Structure import prolog.ast.terms.Structure
import prolog.components.terms.Term import prolog.ast.terms.Term
import prolog.components.terms.Variable import prolog.ast.terms.Variable
/** /**
* True if Term1 is equivalent to Term2. A variable is only identical to a sharing variable. * True if Term1 is equivalent to Term2. A variable is only identical to a sharing variable.
@ -18,3 +18,7 @@ fun equivalent(term1: Term, term2: Term): Boolean {
else -> false else -> false
} }
} }
/**
*
*/

View file

@ -1,8 +1,8 @@
package prolog.builtins package prolog.builtins
import prolog.components.terms.CompoundTerm import prolog.ast.terms.CompoundTerm
import prolog.components.terms.Term import prolog.ast.terms.Term
import prolog.components.terms.Variable import prolog.ast.terms.Variable
/** /**
* True if [Term] is bound (i.e., not a variable) and is not compound. * True if [Term] is bound (i.e., not a variable) and is not compound.

View file

@ -1,6 +0,0 @@
package prolog.components.expressions
import prolog.builtins.True
import prolog.components.terms.Head
class Fact(head: Head) : Clause(head, True())

View file

@ -1,6 +0,0 @@
package prolog.components.expressions
import prolog.components.terms.Body
import prolog.components.terms.Head
class Rule(head: Head, body: Body): Clause(head, body)

View file

@ -1,5 +0,0 @@
package prolog.components.terms
import prolog.components.Provable
interface Body : Provable

View file

@ -1,10 +0,0 @@
package prolog.components.terms
/**
* Part of a [Clause][prolog.components.expressions.Clause] before the [neck][prolog.terms.Neck] operator.
*/
interface Head : Term {
val functor: Functor
}
typealias Functor = String

View file

@ -1,23 +0,0 @@
package prolog.components.terms
/**
* Value in Prolog.
*
* A [Term] is either a [Variable], [Atom], integer, float or [CompoundTerm].
* In addition, SWI-Prolog also defines the type string.
*/
interface Term : Comparable<Term>
/*
<program> ::= <clause list> <query> | <query>
<clause list> ::= <clause> | <clause list> <clause>
<clause> ::= <predicate> . | <predicate> :- <predicate list>.
<predicate list> ::= <predicate> | <predicate list> , <predicate>
<predicate> ::= <atom> | <atom> ( <term list> )
<term list> ::= <term> | <term list> , <term>
<term> ::= <numeral> | <atom> | <variable> | <structure>
<structure> ::= <atom> ( <term list> )
<query> ::= ?- <predicate list>.
<small atom> ::= <lowercase letter> | <small atom> <character>
<variable> ::= <uppercase letter> | <variable> <character>
*/

View file

@ -1,12 +1,12 @@
package prolog package prolog.logic
import prolog.ast.terms.Structure
import prolog.ast.terms.Term
import prolog.ast.terms.Variable
import prolog.builtins.atomic import prolog.builtins.atomic
import prolog.builtins.compound import prolog.builtins.compound
import prolog.builtins.equivalent import prolog.builtins.equivalent
import prolog.builtins.variable import prolog.builtins.variable
import prolog.components.terms.Term
import prolog.components.terms.Variable
import prolog.components.terms.Structure
import java.util.* import java.util.*
typealias Substituted = Map<Variable, Term> typealias Substituted = Map<Variable, Term>

View file

@ -3,14 +3,15 @@ package prolog
import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import prolog.ast.logic.Fact
import prolog.ast.logic.Rule
import prolog.builtins.Conjunction import prolog.builtins.Conjunction
import prolog.builtins.Disjunction import prolog.builtins.Disjunction
import prolog.components.Program import prolog.builtins.Query
import prolog.components.expressions.Fact import prolog.builtins.equivalent
import prolog.components.expressions.Rule import prolog.ast.terms.Atom
import prolog.components.terms.Atom import prolog.ast.terms.Structure
import prolog.components.terms.Structure import prolog.ast.terms.Variable
import prolog.components.terms.Variable
class EvaluationTest { class EvaluationTest {
@BeforeEach @BeforeEach
@ -25,7 +26,7 @@ class EvaluationTest {
val result = Program.query(Atom("a")) val result = Program.query(Atom("a"))
assertTrue(result) assertTrue(result.any())
} }
@Test @Test
@ -35,7 +36,7 @@ class EvaluationTest {
val result = Program.query(Atom("b")) val result = Program.query(Atom("b"))
assertFalse(result) assertFalse(result.any())
} }
@Test @Test
@ -44,9 +45,9 @@ class EvaluationTest {
val fact2 = Fact(Atom("b")) val fact2 = Fact(Atom("b"))
Program.load(listOf(fact1, fact2)) Program.load(listOf(fact1, fact2))
assertTrue(Program.query(Atom("a"))) assertTrue(Program.query(Atom("a")).any())
assertTrue(Program.query(Atom("b"))) assertTrue(Program.query(Atom("b")).any())
assertFalse(Program.query(Atom("c"))) assertFalse(Program.query(Atom("c")).any())
} }
@Test @Test
@ -54,7 +55,7 @@ class EvaluationTest {
val structure = Structure(Atom("f"), listOf(Atom("a"), Atom("b"))) val structure = Structure(Atom("f"), listOf(Atom("a"), Atom("b")))
Program.load(listOf(Fact(structure))) Program.load(listOf(Fact(structure)))
assertTrue(Program.query(Structure(Atom("f"), listOf(Atom("a"), Atom("b"))))) assertTrue(Program.query(Structure(Atom("f"), listOf(Atom("a"), Atom("b")))).any())
} }
@Test @Test
@ -63,9 +64,9 @@ class EvaluationTest {
val structure2 = Structure(Atom("g"), listOf(Atom("c"), Atom("d"))) val structure2 = Structure(Atom("g"), listOf(Atom("c"), Atom("d")))
Program.load(listOf(Fact(structure1), Fact(structure2))) Program.load(listOf(Fact(structure1), Fact(structure2)))
assertTrue(Program.query(Structure(Atom("f"), listOf(Atom("a"), Atom("b"))))) assertTrue(Program.query(Structure(Atom("f"), listOf(Atom("a"), Atom("b")))).any())
assertTrue(Program.query(Structure(Atom("g"), listOf(Atom("c"), Atom("d"))))) assertTrue(Program.query(Structure(Atom("g"), listOf(Atom("c"), Atom("d")))).any())
assertFalse(Program.query(Structure(Atom("h"), listOf(Atom("e"))))) assertFalse(Program.query(Structure(Atom("h"), listOf(Atom("e")))).any())
} }
/** /**
@ -89,8 +90,8 @@ class EvaluationTest {
Program.load(listOf(father, mother, parent1, parent2)) Program.load(listOf(father, mother, parent1, parent2))
assertTrue(Program.query(Structure(Atom("parent"), listOf(Atom("john"), Atom("jimmy"))))) assertTrue(Program.query(Structure(Atom("parent"), listOf(Atom("john"), Atom("jimmy")))).any())
assertTrue(Program.query(Structure(Atom("parent"), listOf(Atom("jane"), Atom("jimmy"))))) assertTrue(Program.query(Structure(Atom("parent"), listOf(Atom("jane"), Atom("jimmy")))).any())
} }
/** /**
@ -110,19 +111,20 @@ class EvaluationTest {
Structure(Atom("father"), listOf(variable1, variable2)), Structure(Atom("father"), listOf(variable1, variable2)),
/* ; */ /* ; */
Structure(Atom("mother"), listOf(variable1, variable2)) Structure(Atom("mother"), listOf(variable1, variable2))
)) )
)
Program.load(listOf(father, mother, parent)) Program.load(listOf(father, mother, parent))
val result1 = Program.query(Structure(Atom("parent"), listOf(Atom("john"), Atom("jimmy")))) val result1 = Program.query(Structure(Atom("parent"), listOf(Atom("john"), Atom("jimmy"))))
assertTrue(result1) assertTrue(result1.toList().isNotEmpty())
val result2 = Program.query(Structure(Atom("parent"), listOf(Atom("jane"), Atom("jimmy")))) val result2 = Program.query(Structure(Atom("parent"), listOf(Atom("jane"), Atom("jimmy"))))
assertTrue(result2) assertTrue(result2.toList().isNotEmpty())
val result3 = Program.query(Structure(Atom("parent"), listOf(Atom("john"), Atom("jane")))) val result3 = Program.query(Structure(Atom("parent"), listOf(Atom("john"), Atom("jane"))))
assertFalse(result3) assertFalse(result3.any())
val result4 = Program.query(Structure(Atom("father"), listOf(Atom("john"), Atom("jane")))) val result4 = Program.query(Structure(Atom("father"), listOf(Atom("john"), Atom("jane"))))
assertFalse(result4) assertFalse(result4.any())
} }
/** /**
@ -162,18 +164,55 @@ class EvaluationTest {
Program.load(listOf(male, female, parent1, parent2, isFather, isMother)) Program.load(listOf(male, female, parent1, parent2, isFather, isMother))
val result1 = Program.query(Structure(Atom("isFather"), listOf(Atom("john"), Atom("jimmy")))) val result1 = Program.query(Structure(Atom("isFather"), listOf(Atom("john"), Atom("jimmy"))))
assertTrue(result1) assertTrue(result1.any())
val result2 = Program.query(Structure(Atom("isMother"), listOf(Atom("jane"), Atom("jimmy")))) val result2 = Program.query(Structure(Atom("isMother"), listOf(Atom("jane"), Atom("jimmy"))))
assertTrue(result2) assertTrue(result2.any())
val result3 = Program.query(Structure(Atom("isFather"), listOf(Atom("jane"), Atom("jimmy")))) val result3 = Program.query(Structure(Atom("isFather"), listOf(Atom("jane"), Atom("jimmy"))))
assertFalse(result3) assertFalse(result3.any())
val result4 = Program.query(Structure(Atom("isMother"), listOf(Atom("john"), Atom("jimmy")))) val result4 = Program.query(Structure(Atom("isMother"), listOf(Atom("john"), Atom("jimmy"))))
assertFalse(result4) assertFalse(result4.any())
val result5 = Program.query(Structure(Atom("parent"), listOf(Atom("trudy"), Atom("jimmy")))) val result5 = Program.query(Structure(Atom("parent"), listOf(Atom("trudy"), Atom("jimmy"))))
assertFalse(result5) assertFalse(result5.any())
}
@Test
fun requires_backtracking() {
val fact1 = Fact(Structure(Atom("a"), listOf(Atom("b"))))
val fact2 = Fact(Structure(Atom("a"), listOf(Atom("c"))))
val fact3 = Fact(Structure(Atom("a"), listOf(Atom("d"))))
Program.load(listOf(fact1, fact2, fact3))
assertTrue(Program.query(Structure(Atom("a"), listOf(Atom("b")))).any())
assertTrue(Program.query(Structure(Atom("a"), listOf(Atom("c")))).any())
assertTrue(Program.query(Structure(Atom("a"), listOf(Atom("d")))).any())
}
@Test
fun multiple_choices() {
val fact1 = Fact(Structure(Atom("a"), listOf(Atom("b"))))
val fact2 = Fact(Structure(Atom("a"), listOf(Atom("c"))))
val fact3 = Fact(Structure(Atom("a"), listOf(Atom("d"))))
Program.load(listOf(fact1, fact2, fact3))
val results = Query(Structure(Atom("a"), listOf(Variable("X")))).prove(emptyMap())
val expectedResults = listOf(
mapOf(Variable("X") to Atom("b")),
mapOf(Variable("X") to Atom("c")),
mapOf(Variable("X") to Atom("d"))
)
val actualResults = results.toList()
assertEquals(expectedResults.size, actualResults.size, "Number of results should match")
for (i in expectedResults.indices) {
assertEquals(expectedResults[i].size, actualResults[i].size, "Substitution size should match")
assertTrue(expectedResults[i].all { actualResults[i][it.key]?.let { it1 -> equivalent(it.value, it1) } ?: false }, "Substitution values should match")
}
} }
} }

View file

@ -0,0 +1,66 @@
package prolog.builtins
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import prolog.Program
import prolog.ast.logic.Fact
import prolog.ast.terms.Atom
class ControlBuiltinsTest {
@BeforeEach
fun setUp() {
Program.clear()
}
@Test
fun not_atom() {
val success = Fact(Atom("a"))
Program.load(listOf(success))
val goal = Atom("a")
val notGoal = Not(goal)
val result1 = Program.query(goal)
val result2 = Program.query(notGoal)
assertTrue(result1.any(), "Expected query to succeed")
assertFalse(result2.any(), "Expected query to fail")
}
@Test
fun not_compound() {
val success = Fact(Atom("f"))
val failure = Fact(Atom("g"))
Program.load(listOf(success, failure))
val goal = Atom("f")
val notGoal = Not(Atom("g"))
val result1 = Program.query(goal)
val result2 = Program.query(notGoal)
assertTrue(result1.any(), "Expected query to succeed")
assertFalse(result2.any(), "Expected query to fail")
}
@Test
fun fail_should_cause_fails() {
val success = Fact(Atom("a"))
val failure = Fact(Atom("b"))
Program.load(listOf(success, failure))
val goal = Atom("a")
val failGoal = Fail()
val result1 = Program.query(goal)
val result2 = Program.query(failGoal)
assertTrue(result1.any(), "Expected query to succeed")
assertFalse(result2.any(), "Expected query to fail")
}
}

View file

@ -3,9 +3,8 @@ package prolog.builtins
import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows import prolog.ast.terms.Atom
import prolog.components.terms.Atom import prolog.ast.terms.Structure
import prolog.components.terms.Structure
/** /**
* Based on [Predicates for analyzing/constructing terms](https://github.com/dtonhofer/prolog_notes/blob/master/swipl_notes/about_term_analysis_and_construction/term_analysis_construction.png) * Based on [Predicates for analyzing/constructing terms](https://github.com/dtonhofer/prolog_notes/blob/master/swipl_notes/about_term_analysis_and_construction/term_analysis_construction.png)

View file

@ -3,12 +3,12 @@ package prolog.builtins
import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import prolog.components.terms.Atom import prolog.ast.terms.Atom
import prolog.components.terms.Structure import prolog.ast.terms.Structure
import prolog.components.terms.Variable import prolog.ast.terms.Variable
import kotlin.test.assertEquals import kotlin.test.assertEquals
class BuiltinsTest { class VerificationBuiltinsTest {
@Test @Test
fun unbound_variable_is_var() { fun unbound_variable_is_var() {
val variable = Variable("X") val variable = Variable("X")

View file

@ -1,12 +1,12 @@
package prolog package prolog.logic
import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import prolog.ast.terms.Atom
import prolog.ast.terms.Structure
import prolog.ast.terms.Variable
import prolog.builtins.equivalent import prolog.builtins.equivalent
import prolog.components.terms.Atom
import prolog.components.terms.Structure
import prolog.components.terms.Variable
/* /*
* Based on: https://en.wikipedia.org/wiki/Unification_%28computer_science%29#Examples_of_syntactic_unification_of_first-order_terms * Based on: https://en.wikipedia.org/wiki/Unification_%28computer_science%29#Examples_of_syntactic_unification_of_first-order_terms