refactor: Herstructurering
This commit is contained in:
parent
1acd1cfb67
commit
e3c84e1761
33 changed files with 290 additions and 178 deletions
|
@ -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.components.expressions.Clause
|
||||
import prolog.components.expressions.Fact
|
||||
import prolog.components.expressions.Predicate
|
||||
import prolog.components.terms.Functor
|
||||
import prolog.components.terms.Goal
|
||||
|
||||
typealias Database = Program
|
||||
|
||||
/**
|
||||
* Prolog Program or database.
|
||||
|
@ -29,7 +32,7 @@ object Program: Resolvent {
|
|||
* Queries the program with a goal.
|
||||
* @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> {
|
||||
val functor = goal.functor
|
||||
|
@ -75,5 +78,3 @@ object Program: Resolvent {
|
|||
setup()
|
||||
}
|
||||
}
|
||||
|
||||
typealias Database = Program
|
|
@ -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.equivalent
|
||||
import prolog.components.Resolvent
|
||||
import prolog.components.terms.*
|
||||
import prolog.unifyLazy
|
||||
import prolog.logic.unifyLazy
|
||||
|
||||
/**
|
||||
* ‘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]
|
||||
*/
|
||||
abstract class Clause(private val head: Head, private val body: Body) : Resolvent {
|
6
src/prolog/ast/logic/Fact.kt
Normal file
6
src/prolog/ast/logic/Fact.kt
Normal 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())
|
|
@ -1,9 +1,8 @@
|
|||
package prolog.components.expressions
|
||||
package prolog.ast.logic
|
||||
|
||||
import prolog.Substituted
|
||||
import prolog.components.Resolvent
|
||||
import prolog.components.terms.Functor
|
||||
import prolog.components.terms.Goal
|
||||
import prolog.logic.Substituted
|
||||
import prolog.ast.terms.Functor
|
||||
import prolog.ast.terms.Goal
|
||||
|
||||
/**
|
||||
* Collection of [Clause]s with the same [Functor].
|
||||
|
@ -37,7 +36,7 @@ class Predicate : Resolvent {
|
|||
* Adds a clause to the predicate.
|
||||
*/
|
||||
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)
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
package prolog.components
|
||||
package prolog.ast.logic
|
||||
|
||||
import prolog.Substituted
|
||||
import prolog.logic.Substituted
|
||||
|
||||
interface Provable {
|
||||
/**
|
|
@ -1,7 +1,7 @@
|
|||
package prolog.components
|
||||
package prolog.ast.logic
|
||||
|
||||
import prolog.Substituted
|
||||
import prolog.components.terms.Goal
|
||||
import prolog.logic.Substituted
|
||||
import prolog.ast.terms.Goal
|
||||
|
||||
/**
|
||||
* Can be instructed to solve a goal.
|
6
src/prolog/ast/logic/Rule.kt
Normal file
6
src/prolog/ast/logic/Rule.kt
Normal 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)
|
|
@ -1,8 +1,8 @@
|
|||
package prolog.components.terms
|
||||
package prolog.ast.terms
|
||||
|
||||
import prolog.Substituted
|
||||
import prolog.components.Resolvent
|
||||
import prolog.unifyLazy
|
||||
import prolog.ast.logic.Resolvent
|
||||
import prolog.logic.Substituted
|
||||
import prolog.logic.unifyLazy
|
||||
|
||||
open class Atom(val name: String) : Goal(), Head, Body, Resolvent {
|
||||
override val functor: Functor = "$name/_"
|
5
src/prolog/ast/terms/Body.kt
Normal file
5
src/prolog/ast/terms/Body.kt
Normal file
|
@ -0,0 +1,5 @@
|
|||
package prolog.ast.terms
|
||||
|
||||
import prolog.ast.logic.Provable
|
||||
|
||||
interface Body : Provable
|
|
@ -1,8 +1,8 @@
|
|||
package prolog.components.terms
|
||||
package prolog.ast.terms
|
||||
|
||||
import prolog.Substituted
|
||||
import prolog.components.Program
|
||||
import prolog.components.Provable
|
||||
import prolog.Program
|
||||
import prolog.ast.logic.Provable
|
||||
import prolog.logic.Substituted
|
||||
|
||||
/**
|
||||
* Ask the Prolog engine.
|
10
src/prolog/ast/terms/Head.kt
Normal file
10
src/prolog/ast/terms/Head.kt
Normal 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
|
|
@ -1,12 +1,7 @@
|
|||
package prolog.components.expressions
|
||||
package prolog.ast.terms
|
||||
|
||||
import prolog.Substituted
|
||||
import prolog.components.Provable
|
||||
import prolog.components.terms.Atom
|
||||
import prolog.components.terms.CompoundTerm
|
||||
import prolog.components.terms.Goal
|
||||
|
||||
typealias Operand = Goal
|
||||
import prolog.ast.logic.Provable
|
||||
import prolog.logic.Substituted
|
||||
|
||||
abstract class Operator(
|
||||
private val symbol: Atom,
|
||||
|
@ -22,3 +17,5 @@ abstract class Operator(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
typealias Operand = Goal
|
|
@ -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.components.Resolvent
|
||||
import prolog.unifyLazy
|
||||
import prolog.logic.Substituted
|
||||
import prolog.logic.unifyLazy
|
||||
|
||||
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 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
|
9
src/prolog/ast/terms/Term.kt
Normal file
9
src/prolog/ast/terms/Term.kt
Normal 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>
|
|
@ -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 {
|
||||
private var alias: Optional<Term> = Optional.empty()
|
1
src/prolog/builtins/arithmetic.kt
Normal file
1
src/prolog/builtins/arithmetic.kt
Normal file
|
@ -0,0 +1 @@
|
|||
package prolog.builtins
|
|
@ -1,15 +1,12 @@
|
|||
package prolog.builtins
|
||||
|
||||
import prolog.Substituted
|
||||
import prolog.components.expressions.Operand
|
||||
import prolog.components.expressions.Operator
|
||||
import prolog.components.terms.Atom
|
||||
import prolog.components.terms.Body
|
||||
import prolog.ast.terms.*
|
||||
import prolog.logic.Substituted
|
||||
|
||||
/**
|
||||
* Always fail.
|
||||
*/
|
||||
class Fail: Atom("fail"), Body {
|
||||
class Fail : Atom("fail"), Body {
|
||||
override fun prove(subs: Substituted): Sequence<Substituted> = emptySequence()
|
||||
}
|
||||
|
||||
|
@ -21,7 +18,7 @@ typealias False = Fail
|
|||
/**
|
||||
* Always succeed.
|
||||
*/
|
||||
class True: Atom("true"), Body {
|
||||
class True : Atom("true"), Body {
|
||||
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.
|
||||
*/
|
||||
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 {
|
||||
if (leftOperand != null) {
|
||||
yieldAll(leftOperand.prove(subs))
|
||||
|
@ -57,3 +54,24 @@ class Disjunction(leftOperand: Operand, rightOperand: Operand) : Operator(Atom("
|
|||
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())
|
||||
}
|
||||
}
|
||||
|
|
1
src/prolog/builtins/meta.kt
Normal file
1
src/prolog/builtins/meta.kt
Normal file
|
@ -0,0 +1 @@
|
|||
package prolog.builtins
|
|
@ -1,9 +1,9 @@
|
|||
package prolog.builtins
|
||||
|
||||
import prolog.Substituted
|
||||
import prolog.components.expressions.Operand
|
||||
import prolog.components.expressions.Operator
|
||||
import prolog.components.terms.Atom
|
||||
import prolog.ast.terms.Atom
|
||||
import prolog.ast.terms.Operand
|
||||
import prolog.ast.terms.Operator
|
||||
import prolog.logic.Substituted
|
||||
|
||||
class Query(rightOperand: Operand) : Operator(Atom("?-"), null, rightOperand) {
|
||||
override fun prove(subs: Substituted): Sequence<Substituted> = rightOperand.prove(subs)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package prolog.builtins
|
||||
|
||||
import prolog.components.terms.Atom
|
||||
import prolog.components.terms.Term
|
||||
import prolog.ast.terms.Atom
|
||||
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
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package prolog.builtins
|
||||
|
||||
import prolog.components.terms.Atom
|
||||
import prolog.components.terms.Structure
|
||||
import prolog.components.terms.Term
|
||||
import prolog.components.terms.Variable
|
||||
import prolog.ast.terms.Atom
|
||||
import prolog.ast.terms.Structure
|
||||
import prolog.ast.terms.Term
|
||||
import prolog.ast.terms.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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package prolog.builtins
|
||||
|
||||
import prolog.components.terms.CompoundTerm
|
||||
import prolog.components.terms.Term
|
||||
import prolog.components.terms.Variable
|
||||
import prolog.ast.terms.CompoundTerm
|
||||
import prolog.ast.terms.Term
|
||||
import prolog.ast.terms.Variable
|
||||
|
||||
/**
|
||||
* True if [Term] is bound (i.e., not a variable) and is not compound.
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
package prolog.components.expressions
|
||||
|
||||
import prolog.builtins.True
|
||||
import prolog.components.terms.Head
|
||||
|
||||
class Fact(head: Head) : Clause(head, True())
|
|
@ -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)
|
|
@ -1,5 +0,0 @@
|
|||
package prolog.components.terms
|
||||
|
||||
import prolog.components.Provable
|
||||
|
||||
interface Body : Provable
|
|
@ -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
|
|
@ -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>
|
||||
*/
|
|
@ -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.compound
|
||||
import prolog.builtins.equivalent
|
||||
import prolog.builtins.variable
|
||||
import prolog.components.terms.Term
|
||||
import prolog.components.terms.Variable
|
||||
import prolog.components.terms.Structure
|
||||
import java.util.*
|
||||
|
||||
typealias Substituted = Map<Variable, Term>
|
|
@ -3,14 +3,15 @@ package prolog
|
|||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import prolog.ast.logic.Fact
|
||||
import prolog.ast.logic.Rule
|
||||
import prolog.builtins.Conjunction
|
||||
import prolog.builtins.Disjunction
|
||||
import prolog.components.Program
|
||||
import prolog.components.expressions.Fact
|
||||
import prolog.components.expressions.Rule
|
||||
import prolog.components.terms.Atom
|
||||
import prolog.components.terms.Structure
|
||||
import prolog.components.terms.Variable
|
||||
import prolog.builtins.Query
|
||||
import prolog.builtins.equivalent
|
||||
import prolog.ast.terms.Atom
|
||||
import prolog.ast.terms.Structure
|
||||
import prolog.ast.terms.Variable
|
||||
|
||||
class EvaluationTest {
|
||||
@BeforeEach
|
||||
|
@ -25,7 +26,7 @@ class EvaluationTest {
|
|||
|
||||
val result = Program.query(Atom("a"))
|
||||
|
||||
assertTrue(result)
|
||||
assertTrue(result.any())
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -35,7 +36,7 @@ class EvaluationTest {
|
|||
|
||||
val result = Program.query(Atom("b"))
|
||||
|
||||
assertFalse(result)
|
||||
assertFalse(result.any())
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -44,9 +45,9 @@ class EvaluationTest {
|
|||
val fact2 = Fact(Atom("b"))
|
||||
Program.load(listOf(fact1, fact2))
|
||||
|
||||
assertTrue(Program.query(Atom("a")))
|
||||
assertTrue(Program.query(Atom("b")))
|
||||
assertFalse(Program.query(Atom("c")))
|
||||
assertTrue(Program.query(Atom("a")).any())
|
||||
assertTrue(Program.query(Atom("b")).any())
|
||||
assertFalse(Program.query(Atom("c")).any())
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -54,7 +55,7 @@ class EvaluationTest {
|
|||
val structure = Structure(Atom("f"), listOf(Atom("a"), Atom("b")))
|
||||
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
|
||||
|
@ -63,9 +64,9 @@ class EvaluationTest {
|
|||
val structure2 = Structure(Atom("g"), listOf(Atom("c"), Atom("d")))
|
||||
Program.load(listOf(Fact(structure1), Fact(structure2)))
|
||||
|
||||
assertTrue(Program.query(Structure(Atom("f"), listOf(Atom("a"), Atom("b")))))
|
||||
assertTrue(Program.query(Structure(Atom("g"), listOf(Atom("c"), Atom("d")))))
|
||||
assertFalse(Program.query(Structure(Atom("h"), listOf(Atom("e")))))
|
||||
assertTrue(Program.query(Structure(Atom("f"), listOf(Atom("a"), Atom("b")))).any())
|
||||
assertTrue(Program.query(Structure(Atom("g"), listOf(Atom("c"), Atom("d")))).any())
|
||||
assertFalse(Program.query(Structure(Atom("h"), listOf(Atom("e")))).any())
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -89,8 +90,8 @@ class EvaluationTest {
|
|||
|
||||
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("jane"), 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")))).any())
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -110,19 +111,20 @@ class EvaluationTest {
|
|||
Structure(Atom("father"), listOf(variable1, variable2)),
|
||||
/* ; */
|
||||
Structure(Atom("mother"), listOf(variable1, variable2))
|
||||
))
|
||||
)
|
||||
)
|
||||
|
||||
Program.load(listOf(father, mother, parent))
|
||||
|
||||
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"))))
|
||||
assertTrue(result2)
|
||||
assertTrue(result2.toList().isNotEmpty())
|
||||
|
||||
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"))))
|
||||
assertFalse(result4)
|
||||
assertFalse(result4.any())
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -162,18 +164,55 @@ class EvaluationTest {
|
|||
Program.load(listOf(male, female, parent1, parent2, isFather, isMother))
|
||||
|
||||
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"))))
|
||||
assertTrue(result2)
|
||||
assertTrue(result2.any())
|
||||
|
||||
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"))))
|
||||
assertFalse(result4)
|
||||
assertFalse(result4.any())
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
66
tests/prolog/builtins/ControlBuiltinsTest.kt
Normal file
66
tests/prolog/builtins/ControlBuiltinsTest.kt
Normal 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")
|
||||
}
|
||||
}
|
|
@ -3,9 +3,8 @@ package prolog.builtins
|
|||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import prolog.components.terms.Atom
|
||||
import prolog.components.terms.Structure
|
||||
import prolog.ast.terms.Atom
|
||||
import prolog.ast.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)
|
||||
|
|
|
@ -3,12 +3,12 @@ package prolog.builtins
|
|||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Test
|
||||
import prolog.components.terms.Atom
|
||||
import prolog.components.terms.Structure
|
||||
import prolog.components.terms.Variable
|
||||
import prolog.ast.terms.Atom
|
||||
import prolog.ast.terms.Structure
|
||||
import prolog.ast.terms.Variable
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class BuiltinsTest {
|
||||
class VerificationBuiltinsTest {
|
||||
@Test
|
||||
fun unbound_variable_is_var() {
|
||||
val variable = Variable("X")
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package prolog
|
||||
package prolog.logic
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.Disabled
|
||||
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.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
|
Reference in a new issue