Checkpoint

This commit is contained in:
Tibo De Peuter 2025-04-11 19:27:01 +02:00
parent e3c84e1761
commit e73e5cbfc8
32 changed files with 1354 additions and 92 deletions

View file

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

View file

@ -0,0 +1,152 @@
package prolog.builtins
import prolog.ast.arithmetic.ArithmeticOperator
import prolog.ast.arithmetic.Expression
import prolog.ast.logic.LogicOperand
import prolog.ast.logic.LogicOperator
import prolog.ast.logic.Provable
import prolog.ast.terms.*
import prolog.logic.*
// TODO >
// TODO <
// TODO =<
// TODO >=
/**
* True if expression Expr1 evaluates to a number non-equal to Expr2.
*/
class EvaluatesToDifferent(private val left: Expression, private val right: Expression) :
ArithmeticOperator(Atom("=\\="), left, right) {
override fun evaluate(subs: Substituted): Pair<Term, Substituted> {
val t1 = left.evaluate(subs)
val t2 = right.evaluate(subs)
// Should both be instantiated
if (!atomic(t1.first) || !atomic(t2.first)) {
throw IllegalArgumentException("Both operands must be instantiated")
}
return if (equivalent(t1.first, t2.first)) {
Pair(False, emptyMap())
} else {
Pair(True, t1.second + t2.second)
}
}
}
/**
* True if Expr1 evaluates to a number equal to Expr2.
*/
class EvaluatesTo(private val left: Expression, private val right: Expression) :
ArithmeticOperator(Atom("=:="), left, right) {
override fun evaluate(subs: Substituted): Pair<Term, Substituted> {
val t1 = left.evaluate(subs)
val t2 = right.evaluate(subs)
// Should both be instantiated
if (!atomic(t1.first) || !atomic(t2.first)) {
throw IllegalArgumentException("Both operands must be instantiated")
}
return if (equivalent(t1.first, t2.first)) {
Pair(True, t1.second + t2.second)
} else {
Pair(False, emptyMap())
}
}
}
/**
* True when Number is the value to which Expr evaluates.
*/
class Is(private val left: Expression, private val right: Expression) :
Operator(Atom("is"), left, right), Provable {
override fun prove(subs: Substituted): Sequence<Substituted> {
val t1 = left.evaluate(subs)
val t2 = right.evaluate(subs)
if (!atomic(t2.first)) {
throw IllegalArgumentException("Arguments are not sufficiently instantiated")
}
return unifyLazy(t1.first, t2.first, subs).map{ it + t1.second + t2.second }
}
}
/**
* Result = -Expr
*/
class Negate(operand: Expression) : Subtract(Integer(0), operand)
/**
* Result = Expr
*/
class Positive(operand: Expression) : Add(Integer(0), operand)
/**
* Result = Expr1 + Expr2
*/
open class Add(private val expr1: Expression, private val expr2: Expression) :
ArithmeticOperator(Atom("+"), expr1, expr2) {
override fun evaluate(subs: Substituted): Pair<Term, Substituted> {
val result = Variable("Result")
val map = plus(expr1, expr2, result, subs)
return result.evaluate(map.first())
}
}
/**
* Result = Expr1 - Expr2
*/
open class Subtract(private val expr1: Expression, private val expr2: Expression) :
ArithmeticOperator(Atom("-"), expr1, expr2) {
override fun evaluate(subs: Substituted): Pair<Term, Substituted> {
val result = Variable("Result")
val map = plus(expr2, result, expr1, subs)
return result.evaluate(map.first())
}
}
// TODO Expr * Expr
/**
* Result = Expr1 * Expr2
*/
class Multiply(private val expr1: Expression, private val expr2: Expression) :
ArithmeticOperator(Atom("*"), expr1, expr2) {
override fun evaluate(subs: Substituted): Pair<Term, Substituted> {
val result = Variable("Result")
val map = mul(expr1, expr2, result, subs)
return result.evaluate(map.first())
}
}
// TODO Expr / Expr
// TODO Expr mod Expr
// TODO Expr rem Expr
class Between(private val expr1: Expression, private val expr2: Expression, private val expr3: Expression) :
Operator(Atom("between"), expr1, expr2) {
override fun prove(subs: Substituted): Sequence<Substituted> {
val e1 = expr1.evaluate(subs)
val e2 = expr2.evaluate(subs)
val e3 = expr3.evaluate(subs)
require(e1.first is Integer && e2.first is Integer) { "Arguments must be integers" }
val v1 = e1.first as Integer
val v2 = e2.first as Integer
return if (variable(e3.first)) {
between(v1, v2, e3.first as Variable).map { it + e1.second + e2.second }
} else {
between(v1, v2, e3.first as Integer).map { it + e1.second + e2.second }
}
}
}

View file

@ -1,12 +1,16 @@
package prolog.builtins
import prolog.ast.terms.*
import prolog.ast.logic.LogicOperand
import prolog.ast.terms.Atom
import prolog.ast.terms.Body
import prolog.ast.terms.Goal
import prolog.ast.logic.LogicOperator
import prolog.logic.Substituted
/**
* Always fail.
*/
class Fail : Atom("fail"), Body {
object Fail : Atom("fail"), Body {
override fun prove(subs: Substituted): Sequence<Substituted> = emptySequence()
}
@ -18,7 +22,7 @@ typealias False = Fail
/**
* Always succeed.
*/
class True : Atom("true"), Body {
object True : Atom("true"), Body {
override fun prove(subs: Substituted): Sequence<Substituted> = sequenceOf(emptyMap())
}
@ -29,16 +33,12 @@ class True : Atom("true"), Body {
/**
* Conjunction (and). True if both Goal1 and Goal2 are true.
*/
class Conjunction(leftOperand: Operand, rightOperand: Operand) : Operator(Atom(","), leftOperand, rightOperand) {
class Conjunction(private val left: LogicOperand, private val right: LogicOperand) : LogicOperator(Atom(","), left, right) {
override fun prove(subs: Substituted): Sequence<Substituted> = sequence {
if (leftOperand != null) {
leftOperand.prove(subs).forEach { left ->
rightOperand.prove(subs + left).forEach { right ->
yield(left + right)
}
left.prove(subs).forEach { left ->
right.prove(subs + left).forEach { right ->
yield(left + right)
}
} else {
yieldAll(rightOperand.prove(subs))
}
}
}
@ -46,17 +46,16 @@ class Conjunction(leftOperand: Operand, rightOperand: Operand) : Operator(Atom("
/**
* Disjunction (or). True if either Goal1 or Goal2 succeeds.
*/
open class Disjunction(leftOperand: Operand, rightOperand: Operand) : Operator(Atom(";"), leftOperand, rightOperand) {
open class Disjunction(private val left: LogicOperand, private val right: LogicOperand) :
LogicOperator(Atom(";"), left, right) {
override fun prove(subs: Substituted): Sequence<Substituted> = sequence {
if (leftOperand != null) {
yieldAll(leftOperand.prove(subs))
}
yieldAll(rightOperand.prove(subs))
yieldAll(left.prove(subs))
yieldAll(right.prove(subs))
}
}
@Deprecated("Use Disjunction instead")
class Bar(leftOperand: Operand, rightOperand: Operand) : Disjunction(leftOperand, rightOperand)
class Bar(leftOperand: LogicOperand, rightOperand: LogicOperand) : Disjunction(leftOperand, rightOperand)
// TODO ->
@ -65,10 +64,10 @@ class Bar(leftOperand: Operand, rightOperand: Operand) : Disjunction(leftOperand
/**
* True if 'Goal' cannot be proven.
*/
class Not(goal: Goal) : Operator(Atom("\\+"), rightOperand = goal) {
class Not(private val goal: Goal) : LogicOperator(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()) {
if (goal.prove(subs).toList().isNotEmpty()) {
return emptySequence()
}
// If the goal cannot be proven, return a sequence with an empty map

View file

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

View file

@ -1,10 +1,10 @@
package prolog.builtins
import prolog.ast.logic.LogicOperand
import prolog.ast.terms.Atom
import prolog.ast.terms.Operand
import prolog.ast.terms.Operator
import prolog.ast.logic.LogicOperator
import prolog.logic.Substituted
class Query(rightOperand: Operand) : Operator(Atom("?-"), null, rightOperand) {
override fun prove(subs: Substituted): Sequence<Substituted> = rightOperand.prove(subs)
class Query(private val query: LogicOperand) : LogicOperator(Atom("?-"), null, query) {
override fun prove(subs: Substituted): Sequence<Substituted> = query.prove(subs)
}

View file

@ -1,24 +1,24 @@
/**
* Comparison and Unification of Terms
*
* [SWI Prolog Documentation](https://www.swi-prolog.org/pldoc/man?section=compare)
*/
package prolog.builtins
import prolog.ast.terms.Atom
import prolog.ast.terms.Structure
import prolog.ast.terms.Operator
import prolog.ast.terms.Term
import prolog.ast.terms.Variable
import prolog.logic.Substituted
import prolog.logic.applySubstitution
import prolog.logic.equivalent
/**
* True if Term1 is equivalent to Term2. A variable is only identical to a sharing variable.
*/
fun equivalent(term1: Term, term2: Term): Boolean {
return when {
term1 is Variable && term2 is Variable -> term1 == term2
term1 is Variable -> term1.alias().isPresent && equivalent(term1.alias().get(), term2)
term2 is Variable -> term2.alias().isPresent && equivalent(term2.alias().get(), term1)
term1 is Atom && term2 is Atom -> term1.compareTo(term2) == 0
term1 is Structure && term2 is Structure -> term1.compareTo(term2) == 0
else -> false
class Equivalent(private val term1: Term, private val term2: Term) : Operator(Atom("=="), term1, term2) {
override fun prove(subs: Substituted): Sequence<Substituted> = sequence {
val t1 = applySubstitution(term1, subs)
val t2 = applySubstitution(term2, subs)
if (equivalent(t1, t2)) {
yield(emptyMap())
}
}
}
/**
*
*/

View file

@ -1,41 +0,0 @@
package prolog.builtins
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.
* Thus, atomic acts as if defined by:
*
* atomic(Term) :-
* nonvar(Term),
* \+ compound(Term).
*/
fun atomic(term: Term): Boolean = nonvariable(term) && !compound(term)
/**
* True if [Term] is bound to a compound term.
* See also functor/3 =../2, compound_name_arity/3 and compound_name_arguments/3.
*/
fun compound(term: Term): Boolean {
val isCompound = term is CompoundTerm
val isVariableCompound = term is Variable && term.alias().isPresent && compound(term.alias().get())
return isCompound || isVariableCompound
}
/**
* True if [Term] currently is not a free variable.
*/
fun nonvariable(term: Term): Boolean = !variable(term)
/**
* True if [Term] currently is a free variable.
*/
fun variable(term: Term): Boolean {
if (term is Variable) {
return term.alias().isEmpty || term.alias().get() === term || variable(term.alias().get())
}
return false;
}