Functor
This commit is contained in:
parent
dff53b4e68
commit
65c4d925d3
15 changed files with 270 additions and 83 deletions
|
@ -6,14 +6,14 @@ import prolog.Substitutions
|
|||
import prolog.ast.logic.Clause
|
||||
import prolog.ast.logic.Predicate
|
||||
import prolog.ast.logic.Resolvent
|
||||
import prolog.ast.terms.Functor
|
||||
import prolog.ast.terms.FunctorInfo
|
||||
import prolog.ast.terms.Goal
|
||||
|
||||
/**
|
||||
* Prolog Program or Database
|
||||
*/
|
||||
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.
|
||||
|
@ -23,14 +23,14 @@ open class Database(val sourceFile: String) {
|
|||
|
||||
if (sourceFile !== "") {
|
||||
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) }
|
||||
}
|
||||
|
||||
Logger.info("Initializing database from $sourceFile")
|
||||
if (predicates.contains("/_")) {
|
||||
if (predicates.contains(FunctorInfo.of("/_"))) {
|
||||
Logger.debug("Loading clauses from /_ predicate")
|
||||
predicates["/_"]?.clauses?.forEach {
|
||||
predicates[FunctorInfo.of("/_")]?.clauses?.forEach {
|
||||
Logger.debug("Loading clause $it")
|
||||
val goal = it.body as Goal
|
||||
goal.satisfy(emptyMap()).toList()
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package prolog.ast.logic
|
||||
|
||||
import prolog.Answers
|
||||
import prolog.ast.Database.Program
|
||||
import prolog.Substitutions
|
||||
import prolog.ast.Database.Program
|
||||
import prolog.ast.terms.*
|
||||
import prolog.builtins.True
|
||||
import prolog.flags.AppliedCut
|
||||
|
@ -20,7 +20,7 @@ import prolog.logic.unifyLazy
|
|||
* @see [Predicate]
|
||||
*/
|
||||
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 {
|
||||
// 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 ->
|
||||
// If the body can be proven, yield the (combined) substitutions
|
||||
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))
|
||||
},
|
||||
onFailure = { error ->
|
||||
|
|
|
@ -2,25 +2,25 @@ package prolog.ast.logic
|
|||
|
||||
import prolog.Answers
|
||||
import prolog.Substitutions
|
||||
import prolog.ast.terms.Functor
|
||||
import prolog.ast.terms.FunctorInfo
|
||||
import prolog.ast.terms.Goal
|
||||
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
|
||||
* to select candidate clauses and then tries these clauses one-by-one.
|
||||
*/
|
||||
class Predicate : Resolvent {
|
||||
val functor: Functor
|
||||
val functor: FunctorInfo
|
||||
val clauses: MutableList<Clause>
|
||||
var dynamic = false
|
||||
|
||||
/**
|
||||
* 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.clauses = mutableListOf()
|
||||
this.dynamic = dynamic
|
||||
|
|
|
@ -4,9 +4,10 @@ import prolog.Answers
|
|||
import prolog.Substitutions
|
||||
import prolog.ast.logic.Resolvent
|
||||
import prolog.logic.unifyLazy
|
||||
import prolog.ast.arithmetic.Integer
|
||||
|
||||
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)
|
||||
|
||||
|
|
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
|
||||
|
||||
import prolog.Answers
|
||||
import prolog.ast.Database.Program
|
||||
import prolog.Substitutions
|
||||
import prolog.ast.Database.Program
|
||||
import prolog.ast.logic.LogicOperand
|
||||
|
||||
/**
|
||||
|
@ -13,7 +13,7 @@ import prolog.ast.logic.LogicOperand
|
|||
* or it fails if Prolog fails to prove it.
|
||||
*/
|
||||
abstract class Goal : LogicOperand(), Term {
|
||||
abstract val functor: Functor
|
||||
abstract val functor: FunctorInfo
|
||||
|
||||
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.
|
||||
*/
|
||||
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.logic.applySubstitution
|
||||
import prolog.logic.unifyLazy
|
||||
import prolog.ast.arithmetic.Integer
|
||||
|
||||
typealias Argument = Term
|
||||
|
||||
typealias CompoundTerm = Structure
|
||||
|
||||
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 {
|
||||
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 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.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.terms.Body
|
||||
import prolog.ast.terms.Goal
|
||||
import prolog.ast.terms.Operator
|
||||
import prolog.ast.Database.Program
|
||||
import prolog.ast.arithmetic.Integer
|
||||
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.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 {
|
||||
override val functor: Functor = "dynamic/1"
|
||||
class Dynamic(private val dynamicFunctor: FunctorInfo) : Goal(), Body {
|
||||
override val functor = FunctorInfo(Atom("dynamic"), Integer(1))
|
||||
|
||||
override fun satisfy(subs: Substitutions): Answers {
|
||||
val predicate = Program.db.predicates[dynamicFunctor]
|
||||
|
@ -48,7 +43,7 @@ class Dynamic(private val dynamicFunctor: Functor): Goal(), Body {
|
|||
}
|
||||
|
||||
class Assert(clause: Clause) : AssertZ(clause) {
|
||||
override val functor: Functor = "assert/1"
|
||||
override val functor = FunctorInfo(Atom("assert"), Integer(1))
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Reference in a new issue