Skip to content

Commit e0a2d86

Browse files
committed
test: some prolog programs resolutions
1 parent 2348b2a commit e0a2d86

File tree

8 files changed

+75
-36
lines changed

8 files changed

+75
-36
lines changed

src/main/scala/io/github/kelvindev15/prolog/core/RecursiveStruct.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import io.github.kelvindev15.prolog.utils.BinaryToFlatVisitor
44

55
trait RecursiveStruct extends Struct:
66
def linearizedArguments: Seq[Term]
7+
override def foreach(action: Term => Unit): Unit = linearizedArguments.foreach(action)
78

89
object RecursiveStruct:
910
trait BinaryRecursiveStruct extends RecursiveStruct:

src/main/scala/io/github/kelvindev15/prolog/core/Struct.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ trait Struct extends Term:
1515
override def isGround: Boolean = arguments.forall(_.isGround)
1616

1717
override def accept[T](visitor: TermVisitor[T]): T = visitor.visit(this)
18+
def foreach(action: Term => Unit): Unit = arguments.foreach(action)
1819

1920
object Struct:
2021
private def isFunctorWellFormed(functor: String): Boolean = functor.matches(Syntax.AtomRegex.regex)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ trait DeclarativeDSL:
4040
def directive(using theory: MutableTheoryWrapper)(directive: Directive): Unit = theory add directive
4141
def clause(using theory: MutableTheoryWrapper)(c: Clause): Unit = theory add c
4242

43-
def solve(using program: PrologProgram)(goal: Term): Unit =
44-
prologProgram = prologProgram withGoal goal
43+
def goal(using program: PrologProgram)(g: Term): Unit =
44+
prologProgram = prologProgram withGoal g
4545

4646
object DeclarativeDSL:
4747
trait MutableTheoryWrapper:

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

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
package io.github.kelvindev15.prolog.engine
22

33
import io.github.kelvindev15.prolog.PrologProgram
4-
import io.github.kelvindev15.prolog.core.{Struct, Term, Variable}
54
import io.github.kelvindev15.prolog.core.Theory.Theory
5+
import io.github.kelvindev15.prolog.core.{Struct, Term, Variable}
66
import io.github.kelvindev15.prolog.engine.Solver.Solution
77
import io.github.kelvindev15.prolog.engine.Solver.Solution.{Halt, Yes}
88
import io.github.kelvindev15.prolog.engine.visitors.{BackVisitor, TuPKtTermVisitor}
99
import it.unibo.tuprolog.core.{Clause as KClause, Struct as KStruct, Substitution as KSubstitution, Term as KTerm}
10-
import it.unibo.tuprolog.solve.Solver as KSolver
11-
import it.unibo.tuprolog.solve.channel.{InputStore, OutputStore}
10+
import it.unibo.tuprolog.solve.{Solution as KSolution, Solver as KSolver}
11+
import it.unibo.tuprolog.solve.channel.{InputChannel, OutputChannel}
1212
import it.unibo.tuprolog.solve.classic.ClassicSolverFactory
1313
import it.unibo.tuprolog.solve.flags.FlagStore
1414
import it.unibo.tuprolog.theory.Theory as KTheory
1515
import it.unibo.tuprolog.unify.Unificator
16-
import it.unibo.tuprolog.solve.Solution as KSolution
1716

1817
trait Solver:
1918
def solve(program: PrologProgram): Iterator[Solution]
@@ -48,16 +47,21 @@ object Solver:
4847
given Conversion[java.util.Iterator[KSolution], Iterator[Solution]] = _.asScala.map(identity)
4948

5049
private def tuPrologClassicSolverOf(staticTheory: Theory, dynamicTheory: Theory): KSolver =
51-
ClassicSolverFactory.INSTANCE.solverOf(
50+
ClassicSolverFactory.INSTANCE.solverWithDefaultBuiltins(
5251
Unificator.getDefault,
5352
ClassicSolverFactory.INSTANCE.getDefaultRuntime,
5453
FlagStore.DEFAULT,
5554
staticTheory,
5655
dynamicTheory,
57-
InputStore.fromStandard(),
58-
OutputStore.fromStandard()
56+
InputChannel.stdIn(),
57+
OutputChannel.stdOut(),
58+
OutputChannel.stdErr(),
59+
OutputChannel.warn()
5960
)
6061

6162
def tuPrologSolver(): Solver = (program: PrologProgram) =>
6263
val solver = tuPrologClassicSolverOf(program.dynamicTheory, program.staticTheory)
6364
program.goal.map { goal => solver.solve(goal.asStruct()).iterator() }.get
65+
66+
def solve(using solver: Solver = tuPrologSolver())(prologProgram: PrologProgram): Iterator[Solution] =
67+
solver.solve(prologProgram)
Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,34 @@
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.{Struct, Term, Variable}
4+
import io.github.kelvindev15.prolog.core.PrologList.Cons
5+
import io.github.kelvindev15.prolog.core.*
56
import it.unibo.tuprolog.core
6-
import it.unibo.tuprolog.core.{TermVisitor, Atom as KAtom, Struct as KStruct, Term as KTerm, Var as KVariable}
7+
import it.unibo.tuprolog.core.{TermVisitor, Atom as KAtom, Cons as KCons, Constant as KConstant, EmptyList as KEmptyList, Integer as KInteger, Real as KReal, Struct as KStruct, Term as KTerm, Var as KVariable}
78

89
import scala.jdk.CollectionConverters.*
910

1011
class BackVisitor extends TermVisitor[Term]:
1112
override def defaultValue(term: KTerm): Term = term match
13+
case emptyList: KEmptyList => visitEmptyList(emptyList)
14+
case list: KCons => visitCons(list)
1215
case atom: KAtom => visitAtom(atom)
1316
case variable: KVariable => visitVar(variable)
17+
case constant: KConstant => visitConstant(constant)
18+
1419

1520
override def visitAtom(term: KAtom): Atom = Atom(term.getValue)
1621
override def visitStruct(term: KStruct): Struct =
1722
Struct(Atom(term.getFunctor), term.getArgs.asScala.map(visitTerm).toSeq*)
1823
override def visitVar(term: KVariable): Variable = Variable(term.getName)
1924

25+
override def visitEmptyList(term: KEmptyList): PrologList = io.github.kelvindev15.prolog.core.PrologList.Nil
26+
override def visitCons(term: KCons): PrologList = term.getTail match
27+
case t if t.isCons => Cons(visitTerm(term.getHead), visitCons(term.getTail.asCons()))
28+
case t if t.isEmptyList => Cons(visitTerm(term.getHead), visitEmptyList(t.asEmptyList()))
29+
case t if t.isVar => Cons(visitTerm(term.getHead), visitVar(term.getTail.asVar()))
30+
override def visitConstant(term: KConstant): Term = term match
31+
case integer: KInteger => Constant(integer.getValue.toInt)
32+
case double: KReal => Constant(double.getValue.toDouble)
33+
2034

src/main/scala/io/github/kelvindev15/prolog/engine/visitors/TuPKtTermVisitor.scala

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,13 @@ import io.github.kelvindev15.prolog.core.Constant.Atom
44
import io.github.kelvindev15.prolog.core.Struct.{Clause, Directive, Fact, Rule}
55
import io.github.kelvindev15.prolog.core.{Constant, Struct, Variable}
66
import io.github.kelvindev15.prolog.utils.TermVisitor
7-
import it.unibo.tuprolog.core.{
8-
Term as KTerm,
9-
Atom as KAtom,
10-
Numeric as KNumeric,
11-
Var as KVar,
12-
Struct as KStruct,
13-
Fact as KFact,
14-
Rule as KRule,
15-
Directive as KDirective,
16-
}
7+
import it.unibo.tuprolog.core.{Atom as KAtom, Directive as KDirective, Fact as KFact, Numeric as KNumeric, Rule as KRule, Struct as KStruct, Term as KTerm, Var as KVar}
178

189
class TuPKtTermVisitor extends TermVisitor[KTerm]:
1910
override def visit(atom: Atom): KTerm = KAtom.of(atom.unquotedValue)
20-
override def visit(numeric: Constant.Numeric): KTerm = KNumeric.of(numeric.value.asInstanceOf[Double])
11+
override def visit(numeric: Constant.Numeric): KTerm = numeric.value match
12+
case value: Double => KNumeric.of(value)
13+
case value: Int => KNumeric.of(value)
2114
override def visit(struct: Struct): KTerm = struct match
2215
case clause: Clause => super.visit(clause)
2316
case _ => KStruct.of(struct.functor.unquotedValue, struct.arguments.map(this.visit)*)

src/test/scala/io/github/kelvindev15/dsl/TestDeclarativeDSL.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ class TestDeclarativeDSL extends AnyFunSuite with Matchers with PrologDSL with D
6666

6767
test("Setting the goal of a prolog program"):
6868
prolog {
69-
solve {
69+
goal {
7070
&&(2 is X + Y, (head(H) | T) `=` S)
7171
}
7272
}.goal shouldBe Some(Conjunction(

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

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,50 @@
11
package io.github.kelvindev15.engine
22

3+
import io.github.kelvindev15.prolog.core.{PrologList, Term}
34
import io.github.kelvindev15.prolog.dsl.{DeclarativeDSL, PrologDSL}
4-
import io.github.kelvindev15.prolog.engine.Solver.Solution
55
import io.github.kelvindev15.prolog.engine.Solver
6+
import io.github.kelvindev15.prolog.engine.Solver.Solution
67
import org.scalatest.flatspec.AnyFlatSpec
78
import org.scalatest.matchers.should.Matchers
89

10+
import scala.reflect
11+
import scala.reflect.ClassTag
12+
913
class TestPrologEngine extends AnyFlatSpec with Matchers with PrologDSL with DeclarativeDSL:
1014

11-
"A prolog with no solution" should "return a single NO answer" in:
12-
val solution = Solver.tuPrologSolver().solve {
13-
prolog {
14-
staticTheory {
15-
clause { "father"("mala", "paga") }
16-
clause { "father"("mala", "kel") }
15+
private val rainbowColors = PrologList("red", "orange", "yellow", "green", "blue", "indigo", "violet")
16+
private val rainbowProgram = prolog:
17+
staticTheory {
18+
for color <- rainbowColors do clause { "rainbow"(color) }
19+
}
20+
21+
private def expect[T <: Solution](using tag: ClassTag[T])(solutions: Iterator[Solution]): Unit =
22+
assert(solutions.hasNext)
23+
solutions.next() shouldBe a [T]
24+
private def expectOne[T <: Solution](using tag: ClassTag[T])(solution: Iterator[Solution]): Unit =
25+
expect[T](solution)
26+
solution.hasNext shouldBe false
27+
28+
"'rainbow(orange)'" should "be a Yes solution of the rainbow program" in:
29+
expectOne[Solution.Yes]:
30+
Solver solve (rainbowProgram withGoal "rainbow"("orange"))
31+
32+
"A prolog program with no solution" should "return a single NO answer" in:
33+
expectOne[Solution.No]:
34+
Solver solve (rainbowProgram withGoal "rainbow"("purple"))
35+
36+
"Member predicate" should "give a Yes solution if the term is in the list" in:
37+
expect[Solution.Yes]:
38+
Solver solve {
39+
prolog {
40+
goal { member("yellow", rainbowColors) }
41+
}
42+
}
43+
44+
"Member predicate" should "give a No solution if the term is not in the list" in:
45+
expect[Solution.No]:
46+
Solver solve {
47+
prolog {
48+
goal { member("purple", rainbowColors) }
1749
}
18-
solve { "father"("mala", X) }
1950
}
20-
}
21-
solution shouldBe a [Iterator[Solution]]
22-
for
23-
s <- solution
24-
do println(s)

0 commit comments

Comments
 (0)