Assert{a,z,}

This commit is contained in:
Tibo De Peuter 2025-05-02 21:51:34 +02:00
parent f9017da734
commit 80fb3d1e60
Signed by: tdpeuter
GPG key ID: 38297DE43F75FFE2
11 changed files with 373 additions and 79 deletions

View file

@ -66,72 +66,40 @@ open class Preprocessor {
when {
// TODO Remove hardcoding by storing the functors as constants in operators?
term.functor == ":-/2" -> Rule( args[0] as Head, args[1] as Body )
// Logic
term.functor == "=/2" -> {
Unify(args[0], args[1])
}
term.functor == "=/2" -> Unify(args[0], args[1])
term.functor == "\\=/2" -> NotUnify(args[0], args[1])
term.functor == ",/2" -> Conjunction(args[0] as LogicOperand, args[1] as LogicOperand)
term.functor == ";/2" -> Disjunction(args[0] as LogicOperand, args[1] as LogicOperand)
term.functor == "\\+/1" -> Not(args[0] as Goal)
term.functor == "==/2" -> Equivalent(args[0], args[1])
term.functor == "\\=/2" -> {
NotUnify(args[0], args[1])
}
term.functor == ",/2" -> {
Conjunction(args[0] as LogicOperand, args[1] as LogicOperand)
}
term.functor == ";/2" -> {
Disjunction(args[0] as LogicOperand, args[1] as LogicOperand)
}
term.functor == "\\+/1" -> {
Not(args[0] as Goal)
}
term.functor == "==/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 == "=:=/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 == "=\\=/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 == "is/2" && args.all { it is Expression } -> Is(args[0] as Expression, args[1] as Expression)
// Arithmetic
term.functor == "-/1" && args.all { it is Expression } -> {
Negate(args[0] as Expression)
}
term.functor == "-/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 == "+/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 == "*/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 == "between/3" && args.all { it is Expression } -> Between(args[0] as Expression, args[1] as Expression, args[2] as Expression)
term.functor == "-/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 == "+/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 == "//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)
// Database
term.functor == "assert/1" -> {
if (args[0] is Rule) {
Assert(args[0] as Rule)
} else {
Assert(Fact(args[0] as Head))
}
}
term.functor == "asserta/1" -> AssertA(args[0] as Clause)
// Other
term.functor == "write/1" -> Write(args[0])

View file

@ -31,10 +31,12 @@ import prolog.ast.terms.*
* | 100 | yfx | . |
* | 1 | fx | $ |
*
* It is very easy to extend this grammar to support more operators. Just add them at the appropriate rule or create a
* new rule and chain it to the existing ones.
*
* @see [SWI-Prolog Predicate op/3](https://www.swi-prolog.org/pldoc/man?predicate=op/3)
*/
open class TermsGrammar : Tokens() {
// Basic named terms
protected val variable: Parser<Variable> by (variableToken or anonymousVariableToken) use { Variable(text) }
protected val simpleAtom: Parser<Atom> by (nameToken or exclamation) use { Atom(text) }
@ -66,42 +68,43 @@ open class TermsGrammar : Tokens() {
or int
)
// Level 200 - prefix operators (+, -, \)
protected val op200: Parser<CompoundTerm> by ((plus or minus) * parser(::term200)) use {
CompoundTerm(Atom(t1.text), listOf(t2))
}
protected val term200: Parser<Term> by (op200 or baseTerm)
// Level 400 - multiplication, division
protected val op400: Parser<String> by (multiply or divide) use { text }
protected val term400: Parser<Term> by (term200 * zeroOrMore(op400 * term200)) use {
t2.fold(t1) { acc, (op, term) -> CompoundTerm(Atom(op), listOf(acc, term)) }
}
// Level 500 - addition, subtraction
protected val op500: Parser<String> by (plus or minus) use { text }
protected val term500: Parser<Term> by (term400 * zeroOrMore(op500 * term400)) use {
t2.fold(t1) { acc, (op, term) -> CompoundTerm(Atom(op), listOf(acc, term)) }
}
// Level 700 - comparison operators
protected val op700: Parser<String> by (equivalent or equals or notEquals or isOp) use { text }
protected val term700: Parser<Term> by (term500 * optional(op700 * term500)) use {
if (t2 == null) t1 else CompoundTerm(Atom(t2!!.t1), listOf(t1, t2!!.t2))
}
// Level 1000 - conjunction (,)
protected val term1000: Parser<Term> by (term700 * zeroOrMore(comma * term700)) use {
t2.fold(t1) { acc, (_, term) -> CompoundTerm(Atom(","), listOf(acc, term)) }
protected val op1000: Parser<String> by (comma) use { text }
protected val term1000: Parser<Term> by (term700 * zeroOrMore(op1000 * term700)) use {
t2.fold(t1) { acc, (op, term) -> CompoundTerm(Atom(op), listOf(acc, term)) }
}
// Level 1100 - disjunction (;)
protected val term1100: Parser<Term> by (term1000 * zeroOrMore(semicolon * term1000)) use {
t2.fold(t1) { acc, (_, term) -> CompoundTerm(Atom(";"), listOf(acc, term)) }
protected val op1100: Parser<String> by (semicolon) use { text }
protected val term1100: Parser<Term> by (term1000 * zeroOrMore(op1100 * term1000)) use {
t2.fold(t1) { acc, (op, term) -> CompoundTerm(Atom(op), listOf(acc, term)) }
}
protected val op1200: Parser<String> by (neck) use { text }
protected val term1200: Parser<Term> by (term1100 * zeroOrMore(op1200 * term1100)) use {
t2.fold(t1) { acc, (op, term) -> CompoundTerm(Atom(op), listOf(acc, term)) }
}
// Term - highest level expression
protected val term: Parser<Term> by term1100
protected val term: Parser<Term> by term1200
protected val termNoConjunction: Parser<Term> by term700
// Parts for clauses

View file

@ -12,7 +12,7 @@ import prolog.ast.terms.Goal
* This object is a singleton that manages a list of databases.
*/
object Program : Resolvent {
private val internalDb = Database("")
val internalDb = Database("")
private val databases: MutableList<Database> = mutableListOf(internalDb)
var storeNewLine: Boolean = false
@ -35,7 +35,7 @@ object Program : Resolvent {
}
}
fun load(clauses: List<Clause>) = internalDb.load(clauses)
fun load(clauses: List<Clause>, index: Int? = null) = internalDb.load(clauses, index)
fun clear() {
databases.forEach { it.clear() }

View file

@ -14,7 +14,7 @@ import prolog.ast.terms.Goal
* Prolog Program or Database
*/
class Database(val sourceFile: String): Resolvent {
private var predicates: Map<Functor, Predicate> = emptyMap()
var predicates: Map<Functor, Predicate> = emptyMap()
fun initialize() {
Logger.info("Initializing database from $sourceFile")
@ -39,14 +39,14 @@ class Database(val sourceFile: String): Resolvent {
/**
* Loads a list of clauses into the program.
*/
fun load(clauses: List<Clause>) {
fun load(clauses: List<Clause>, index: Int? = null) {
for (clause in clauses) {
val functor = clause.functor
val predicate = predicates[functor]
if (predicate != null) {
// If the predicate already exists, add the clause to it
predicate.add(clause)
predicate.add(clause, index)
} else {
// If the predicate does not exist, create a new one
predicates += Pair(functor, Predicate(listOf(clause)))

View file

@ -19,7 +19,7 @@ import prolog.logic.unifyLazy
* @see [prolog.ast.terms.Variable]
* @see [Predicate]
*/
abstract class Clause(val head: Head, val body: Body) : Resolvent {
abstract class Clause(val head: Head, val body: Body) : Term, Resolvent {
val functor: Functor = head.functor
override fun solve(goal: Goal, subs: Substitutions): Answers = sequence {
@ -70,4 +70,18 @@ abstract class Clause(val head: Head, val body: Body) : Resolvent {
}
override fun toString(): String = if (body is True) head.toString() else "$head :- $body"
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Clause) return false
if (head != other.head) return false
if (body != other.body) return false
return true
}
override fun hashCode(): Int {
return super.hashCode()
}
}

View file

@ -36,9 +36,9 @@ class Predicate : Resolvent {
/**
* Adds a clause to the predicate.
*/
fun add(clause: Clause) {
fun add(clause: Clause, index: Int? = null) {
require(clause.functor == functor) { "Clause functor does not match predicate functor" }
clauses.add(clause)
if (index != null) clauses.add(index, clause) else clauses.add(clause)
}
/**

View file

@ -0,0 +1,38 @@
package prolog.builtins
import prolog.Answers
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.Program
import prolog.ast.terms.Functor
class Assert(clause: Clause) : AssertZ(clause) {
override val functor: Functor = "assert/1"
}
/**
* Assert a [Clause] as a first clause of the [Predicate] into the [Program].
*/
class AssertA(val clause: Clause) : Structure(Atom("asserta"), listOf(clause)) {
override fun satisfy(subs: Substitutions): Answers {
// Add clause to the program
Program.load(listOf(clause), 0)
return sequenceOf(Result.success(emptyMap()))
}
}
/**
* Assert a [Clause] as a last clause of the [Predicate] into the [Program].
*/
open class AssertZ(val clause: Clause) : Structure(Atom("assertz"), listOf(clause)) {
override fun satisfy(subs: Substitutions): Answers {
// Add clause to the program
Program.load(listOf(clause))
return sequenceOf(Result.success(emptyMap()))
}
}