Skip to content

Commit ea10f17

Browse files
committed
test: clause/2
1 parent b94db9b commit ea10f17

File tree

10 files changed

+105
-24
lines changed

10 files changed

+105
-24
lines changed

src/main/scala/io/github/kelvindev15/prolog/dsl/DSLConversions.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@ package io.github.kelvindev15.prolog.dsl
22

33
import io.github.kelvindev15.prolog.core.Constant.Atom
44
import io.github.kelvindev15.prolog.core.Struct.Fact
5-
import io.github.kelvindev15.prolog.core.{Constant, PrologList, Struct, Term}
5+
import io.github.kelvindev15.prolog.core.{Constant, Prolog, PrologList, Struct, Term}
66

77
trait DSLConversions:
88
dsl: PrologDSL =>
99
given Conversion[String, Atom] = Atom(_)
10-
given Conversion[AnyVal, Constant.Numeric] = Constant.Numeric(_)
10+
given Conversion[AnyVal, Constant] = {
11+
case boolean: Boolean => Atom(if (boolean) "true" else "false")
12+
case other => Constant.Numeric(other)
13+
}
1114
given Conversion[Struct, Fact] = Fact(_)
1215
given Conversion[Seq[Term], PrologList] = PrologList(_*)

src/main/scala/io/github/kelvindev15/prolog/dsl/DSLPrologBuiltins.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package io.github.kelvindev15.prolog.dsl
33
import io.github.kelvindev15.prolog.core.Constant.Atom
44
import io.github.kelvindev15.prolog.core.Goals.Conjunction
55
import io.github.kelvindev15.prolog.core.Struct.{Directive, Indicator}
6-
import io.github.kelvindev15.prolog.core.{Constant, Struct, Term}
6+
import io.github.kelvindev15.prolog.core.{Constant, Prolog, Struct, Term}
77

88
import scala.annotation.targetName
99

@@ -63,4 +63,5 @@ trait DSLPrologBuiltins:
6363
Struct(Atom("op"), precedence, associativity, name)
6464

6565
def length(term: Term, len: Term): Struct = Struct(Atom("length"), term, len)
66+
val `[]`: Atom = Prolog.Functors.EMPTY_LIST
6667

src/main/scala/io/github/kelvindev15/prolog/dsl/DSLVariables.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ trait DSLVariables:
88
def A: Variable = Variable("A")
99
def B: Variable = Variable("B")
1010
def C: Variable = Variable("C")
11+
def D: Variable = Variable("D")
1112
def H: Variable = Variable("H")
1213
def S: Variable = Variable("S")
1314
def T: Variable = Variable("T")

src/main/scala/io/github/kelvindev15/prolog/engine/Solver.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,6 @@ object Solver:
2828

2929
def lazySolve(using solver: Solver = tuPrologSolver())(prologProgram: PrologProgram): LazyList[Solution] =
3030
solver lazySolve prologProgram
31+
32+
def solutionsOf(using solver: Solver = tuPrologSolver())(prologProgram: PrologProgram): Seq[Solution] =
33+
solver solutionsOf prologProgram

src/main/scala/io/github/kelvindev15/prolog/engine/solvers/TuPrologClassicSolver.scala

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,32 @@
11
package io.github.kelvindev15.prolog.engine.solvers
22

33
import io.github.kelvindev15.prolog.PrologProgram
4-
import io.github.kelvindev15.prolog.core.{Struct, Term}
54
import io.github.kelvindev15.prolog.core.theory.Theory
5+
import io.github.kelvindev15.prolog.core.{Struct, Term}
66
import io.github.kelvindev15.prolog.engine.Solver
77
import io.github.kelvindev15.prolog.engine.Solver.Solution.{Halt, Yes}
88
import io.github.kelvindev15.prolog.engine.Solver.{Solution, Substitution}
99
import io.github.kelvindev15.prolog.engine.visitors.{From2PKtTermVisitor, To2PKtTermVisitor}
10+
import it.unibo.tuprolog.core.{Clause as KClause, Struct as KStruct, Substitution as KSubstitution, Term as KTerm}
1011
import it.unibo.tuprolog.solve.channel.{InputChannel, OutputChannel}
1112
import it.unibo.tuprolog.solve.classic.ClassicSolverFactory
1213
import it.unibo.tuprolog.solve.flags.FlagStore
13-
import it.unibo.tuprolog.unify.Unificator
14-
import it.unibo.tuprolog.core.{Clause as KClause, Struct as KStruct, Substitution as KSubstitution, Term as KTerm}
1514
import it.unibo.tuprolog.solve.{Solution as KSolution, Solver as KSolver}
1615
import it.unibo.tuprolog.theory.Theory as KTheory
16+
import it.unibo.tuprolog.unify.Unificator
17+
1718
import scala.jdk.CollectionConverters.*
1819

1920
class TuPrologClassicSolver extends Solver:
20-
private val to2pktVisitor = To2PKtTermVisitor()
21+
private val to2pktVisitor = To2PKtTermVisitor.withNewScope
2122
private val from2pktVisitor = From2PKtTermVisitor()
2223

2324
given Conversion[Term, KTerm] = _.accept(to2pktVisitor)
2425
given Conversion[KTerm, KClause] = _.asClause()
2526
given Conversion[KTerm, KStruct] = _.asStruct()
2627
given Conversion[Theory, KTheory] with
27-
override def apply(theory: Theory): KTheory = KTheory.of(theory.map(_.asClause()) *)
28+
override def apply(theory: Theory): KTheory =
29+
KTheory.of(theory.map(_.accept(To2PKtTermVisitor.withNewScope).asClause())*)
2830
given Conversion[KStruct, Struct] = from2pktVisitor.visitStruct(_)
2931
given Conversion[KSubstitution, Substitution] = {
3032
case substitution: KSubstitution.Unifier => Map(
@@ -54,5 +56,5 @@ class TuPrologClassicSolver extends Solver:
5456
)
5557

5658
override def solve(program: PrologProgram): Iterator[Solution] =
57-
val solver = classicSolverOf(program.dynamicTheory, program.staticTheory)
59+
val solver = classicSolverOf(program.staticTheory, program.dynamicTheory)
5860
program.goal.map { goal => solver.solve(goal.asStruct()).iterator() }.get
Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,42 @@
11
package io.github.kelvindev15.prolog.engine.visitors
22

33
import io.github.kelvindev15.prolog.core.Constant.Atom
4+
import io.github.kelvindev15.prolog.core.PrologList.Cons
45
import io.github.kelvindev15.prolog.core.RecursiveStruct.BinaryRecursiveStruct
56
import io.github.kelvindev15.prolog.core.Struct.{Clause, Directive, Fact, Rule}
6-
import io.github.kelvindev15.prolog.core.{Constant, RecursiveStruct, Struct, Variable}
7+
import io.github.kelvindev15.prolog.core.{Constant, PrologList, RecursiveStruct, Struct, Variable}
78
import io.github.kelvindev15.prolog.utils.TermVisitor
8-
import it.unibo.tuprolog.core.{Scope as KScope, Term as KTerm}
9+
import it.unibo.tuprolog.core.{Scope as KScope, Term as KTerm,
10+
Atom as KAtom,
11+
Numeric as KNum,
12+
Struct as KStruct,
13+
Fact as KFact,
14+
Rule as KRule,
15+
Var as KVar,
16+
Directive as KDirective,
17+
Cons as KCons,
18+
EmptyList as KEmptyList,
19+
}
920

10-
class To2PKtTermVisitor() extends TermVisitor[KTerm]:
11-
private val scope: KScope = KScope.empty()
12-
override def visit(atom: Atom): KTerm = scope.atomOf(atom.unquotedValue)
21+
class To2PKtTermVisitor(scope: KScope = KScope.empty()) extends TermVisitor[KTerm]:
22+
override def visit(atom: Atom): KTerm = KAtom.of(atom.unquotedValue)
1323
override def visit(numeric: Constant.Numeric): KTerm = numeric.value match
14-
case value: Double => scope.numOf(value)
15-
case value: Int => scope.numOf(value)
24+
case value: Double => KNum.of(value)
25+
case value: Int => KNum.of(value)
1626
override def visit(struct: Struct): KTerm = struct match
1727
case clause: Clause => super.visit(clause)
18-
case _ => scope.structOf(struct.functor.unquotedValue, struct.arguments.map(this.visit)*)
19-
override def visit(fact: Fact): KTerm = fact.head.map(h => scope.factOf(h.accept(this).asStruct())).get
28+
case list: PrologList => visit(list)
29+
case _ => KStruct.of(struct.functor.unquotedValue, struct.arguments.map(this.visit)*)
30+
override def visit(fact: Fact): KTerm = fact.head.map(h => KFact.of(h.accept(this).asStruct())).get
2031
override def visit(rule: Rule): KTerm =
21-
rule.head.map(h => scope.ruleOf(h.accept(this).asStruct(), rule.body.accept(this))).get
22-
override def visit(directive: Directive): KTerm = scope.directiveOf(directive.body.accept(this))
32+
rule.head.map(h => KRule.of(h.accept(this).asStruct(), rule.body.accept(this))).get
33+
override def visit(directive: Directive): KTerm = KDirective.of(directive.body.accept(this))
2334
override def visit(variable: Variable): KTerm = scope.varOf(variable.name)
2435
override def visit(binaryRecursiveStruct: BinaryRecursiveStruct): KTerm =
2536
visit(binaryRecursiveStruct.asInstanceOf[Struct])
37+
override def visit(list: PrologList): KTerm = list match
38+
case head: Cons => KCons.of(visit(head.head), visit(head.tail))
39+
case PrologList.Nil => KEmptyList.getInstance()
2640

41+
object To2PKtTermVisitor:
42+
def withNewScope: To2PKtTermVisitor = To2PKtTermVisitor(KScope.empty())

src/main/scala/io/github/kelvindev15/prolog/utils/TermVisitor.scala

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package io.github.kelvindev15.prolog.utils
22

3-
import io.github.kelvindev15.prolog.core.{Constant, Struct, Term, Variable}
3+
import io.github.kelvindev15.prolog.core.{Constant, PrologList, RecursiveStruct, Struct, Term, Variable}
44
import io.github.kelvindev15.prolog.core.Constant.{Atom, Numeric}
55
import io.github.kelvindev15.prolog.core.RecursiveStruct.BinaryRecursiveStruct
66
import io.github.kelvindev15.prolog.core.Struct.{Clause, Directive, Fact, Rule}
@@ -24,6 +24,9 @@ trait TermVisitor[T]:
2424
def visit(rule: Rule): T = throw NotImplementedError()
2525
def visit(fact: Fact): T = throw NotImplementedError()
2626
def visit(directive: Directive): T = throw NotImplementedError()
27-
28-
def visit(binaryRecursiveStruct: BinaryRecursiveStruct): T = throw NotImplementedError()
27+
def visit(recursiveStruct: RecursiveStruct): T = recursiveStruct match
28+
case list: PrologList => visit(list)
29+
case binary: BinaryRecursiveStruct => visit(binary)
30+
def visit(binaryRecursiveStruct: BinaryRecursiveStruct): T = visit(binaryRecursiveStruct.asInstanceOf[Struct])
31+
def visit(list: PrologList): T = throw NotImplementedError()
2932

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package io.github.kelvindev15.engine
2+
3+
import io.github.kelvindev15.engine.utils.TestUtils
4+
import io.github.kelvindev15.prolog.PrologProgram
5+
import io.github.kelvindev15.prolog.dsl.{DeclarativeDSL, PrologDSL}
6+
import io.github.kelvindev15.prolog.engine.Solver
7+
import io.github.kelvindev15.prolog.engine.Solver.Solution
8+
import org.scalatest.funsuite.AnyFunSuite
9+
import org.scalatest.matchers.should.Matchers
10+
11+
class TestBuiltins extends AnyFunSuite with Matchers with PrologDSL with DeclarativeDSL with TestUtils:
12+
test("Test var(X)"):
13+
expectOne[Solution.Yes]:
14+
Solver solve (PrologProgram.emptyTheory withGoal `var`(X))
15+
expectOne[Solution.No]:
16+
Solver solve (PrologProgram.emptyTheory withGoal `var`(23))
17+
expectOne[Solution.No]:
18+
Solver solve (PrologProgram.emptyTheory withGoal &&(X `=` Y, Y `=` 23 , `var`(X)))
19+
20+
test("Test atom(X)"):
21+
expectOne[Solution.No]:
22+
Solver solve (PrologProgram.emptyTheory withGoal atom(23))
23+
expectOne[Solution.Yes]:
24+
Solver solve (PrologProgram.emptyTheory withGoal atom("apples"))
25+
expectOne[Solution.Yes]:
26+
Solver solve (PrologProgram.emptyTheory withGoal atom("'/us/chris/pl.123'"))
27+
expectOne[Solution.No]:
28+
Solver solve (PrologProgram.emptyTheory withGoal atom(X))
29+
expectOne[Solution.No]:
30+
Solver solve (PrologProgram.emptyTheory withGoal atom("book"("bronte", "w_h", X)))
31+
32+
test("Test atomic(X)"):
33+
expectOne[Solution.Yes]:
34+
Solver solve (PrologProgram.emptyTheory withGoal atomic(23.4))
35+
expectOne[Solution.No]:
36+
Solver solve (PrologProgram.emptyTheory withGoal atomic(X))
37+

src/test/scala/io/github/kelvindev15/engine/TestPrologEngine.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,18 @@ class TestPrologEngine extends AnyFlatSpec with Matchers with TestUtils with Pro
5555
goal:
5656
query
5757
} expectSolutionsIn fruits.map(e => query.yes(X -> e))
58+
59+
"clause/2" should "give two solution with an append theory" in:
60+
val append = "app"
61+
val query = clause(append(A, B, C), Y)
62+
Solver lazySolve {
63+
prolog:
64+
staticTheory:
65+
fact { append(Nil, X, X) }
66+
rule { append(cons(A)(B), C, cons(A)(D)) :- append(B, C, D) }
67+
goal:
68+
query
69+
} expectSolutionsIn Seq(
70+
query.yes(A -> Nil, B -> C, Y -> true),
71+
query.yes(A -> cons(A, B), C -> cons(A, D), Y -> append(B, B, D))
72+
)

src/test/scala/io/github/kelvindev15/engine/utils/TestUtils.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ trait TestUtils:
2020
extension(solutions: Iterable[Solution])
2121
def expectSolutionsIn(expectedSolutions: Iterable[Solution]): Unit =
2222
solutions should contain allElementsOf expectedSolutions.to(LazyList)
23-
23+
2424
extension(query: Struct)
2525
def yes(substitutions: (Variable, Term)*): Yes = Yes(query, Substitution(substitutions*))
2626
def no: Solution.No = Solution.No(query)

0 commit comments

Comments
 (0)