diff --git a/examples/scratchpad.pl b/examples/scratchpad.pl index b122c4f..9367251 100644 --- a/examples/scratchpad.pl +++ b/examples/scratchpad.pl @@ -21,3 +21,7 @@ main :- write("but "), did_not_get_an_a(Y), write(Y), write(" did not get an A, "), fail; write("unfortunately."), nl. + +:- initialization(main). +main :- write('gpl zegt: '), groet(wereld), nl. +groet(X) :- write(dag(X)). diff --git a/src/interpreter/Preprocessor.kt b/src/interpreter/Preprocessor.kt index 031cab9..686139f 100644 --- a/src/interpreter/Preprocessor.kt +++ b/src/interpreter/Preprocessor.kt @@ -66,6 +66,14 @@ open class Preprocessor { when { // TODO Remove hardcoding by storing the functors as constants in operators? // Logic + 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) } @@ -92,14 +100,6 @@ open class Preprocessor { // Arithmetic - term.functor == "=/2" && args.all { it is Expression } -> { - Unify(args[0] as Expression, args[1] as Expression) - } - - term.functor == "\\=/2" && args.all { it is Expression } -> { - NotUnify(args[0] as Expression, args[1] as Expression) - } - term.functor == "-/1" && args.all { it is Expression } -> { Negate(args[0] as Expression) } diff --git a/src/parser/grammars/TermsGrammar.kt b/src/parser/grammars/TermsGrammar.kt index 52d4ef0..1d20fc1 100644 --- a/src/parser/grammars/TermsGrammar.kt +++ b/src/parser/grammars/TermsGrammar.kt @@ -7,6 +7,32 @@ import prolog.ast.arithmetic.Float import prolog.ast.arithmetic.Integer import prolog.ast.terms.* +/** + * Precedence is based on the following table: + * + * | Precedence | Type | Operators | + * |------------|------|-----------------------------------------------------------------------------------------------| + * | 1200 | xfx | --\>, :-, =\>, ==\> | + * | 1200 | fx | :-, ?- | + * | 1105 | xfy | \| | + * | 1100 | xfy | ; | + * | 1050 | xfy | -\>, \*-\> | + * | 1000 | xfy | , | + * | 990 | xfx | := | + * | 900 | fy | \\+ | + * | 700 | xfx | \<, =, =.., =:=, =\<, ==, =\\=, \>, \>=, \\=, \\==, as, is, \>:\<, :\< | + * | 600 | xfy | : | + * | 500 | yfx | +, -, /\\, \\/, xor | + * | 500 | fx | ? | + * | 400 | yfx | \*, /, //, div, rdiv, \<\<, \>\>, mod, rem | + * | 200 | xfx | \*\* | + * | 200 | xfy | ^ | + * | 200 | fy | +, -, \\ | + * | 100 | yfx | . | + * | 1 | fx | $ | + * + * @see [SWI-Prolog Predicate op/3](https://www.swi-prolog.org/pldoc/man?predicate=op/3) + */ open class TermsGrammar : Tokens() { // Basic named terms @@ -19,7 +45,7 @@ open class TermsGrammar : Tokens() { ) use { Atom(text.substring(1, text.length - 1)) } protected val atom: Parser by (quotedAtom or simpleAtom) protected val compound: Parser by (atom * -leftParenthesis * separated( - parser(::term), + parser(::termNoConjunction), comma, acceptZero = true ) * -rightParenthesis) use { @@ -30,51 +56,57 @@ open class TermsGrammar : Tokens() { protected val int: Parser by integerToken use { Integer(text.toInt()) } protected val float: Parser by floatToken use { Float(text.toFloat()) } - // Operators - protected val ops: Parser by (dummy - // Logic - or comma - or semicolon - // Arithmetic - or plus - or equals - or notEquals - ) use { this.text } - protected val simpleOperand: Parser by (dummy - // Logic + // Base terms (atoms, compounds, variables, numbers) + protected val baseTerm: Parser by (dummy + or (-leftParenthesis * parser(::term) * -rightParenthesis) or compound or atom or variable - // Arithmetic - or int or float + or int ) - protected val operand: Parser by (dummy - or parser(::operator) - or simpleOperand - ) - protected val operator: Parser by (simpleOperand * ops * operand) use { - CompoundTerm(Atom(t2), listOf(t1, t3)) + + // Level 200 - prefix operators (+, -, \) + protected val op200: Parser by ((plus or minus) * parser(::term200)) use { + CompoundTerm(Atom(t1.text), listOf(t2)) + } + protected val term200: Parser by (op200 or baseTerm) + + // Level 400 - multiplication, division + protected val op400: Parser by (multiply or divide) use { text } + protected val term400: Parser by (term200 * zeroOrMore(op400 * term200)) use { + t2.fold(t1) { acc, (op, term) -> CompoundTerm(Atom(op), listOf(acc, term)) } } - // Parts - protected val head: Parser by (dummy - or compound - or atom - ) - protected val body: Parser by (dummy - or operator - or head - or variable - ) use { this as Body } + // Level 500 - addition, subtraction + protected val op500: Parser by (plus or minus) use { text } + protected val term500: Parser by (term400 * zeroOrMore(op500 * term400)) use { + t2.fold(t1) { acc, (op, term) -> CompoundTerm(Atom(op), listOf(acc, term)) } + } - protected val term: Parser by (dummy - or float - or int - or variable - or compound - or atom - ) + // Level 700 - comparison operators + protected val op700: Parser by (equals or notEquals) use { text } + protected val term700: Parser 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 by (term700 * zeroOrMore(comma * term700)) use { + t2.fold(t1) { acc, (_, term) -> CompoundTerm(Atom(","), listOf(acc, term)) } + } + + // Level 1100 - disjunction (;) + protected val term1100: Parser by (term1000 * zeroOrMore(semicolon * term1000)) use { + t2.fold(t1) { acc, (_, term) -> CompoundTerm(Atom(";"), listOf(acc, term)) } + } + + // Term - highest level expression + protected val term: Parser by term1100 + protected val termNoConjunction: Parser by term700 + + // Parts for clauses + protected val head: Parser by (compound or atom) + protected val body: Parser by term use { this as Body } override val rootParser: Parser by term -} \ No newline at end of file +} diff --git a/src/parser/grammars/Tokens.kt b/src/parser/grammars/Tokens.kt index cf55e09..83e7e33 100644 --- a/src/parser/grammars/Tokens.kt +++ b/src/parser/grammars/Tokens.kt @@ -25,6 +25,9 @@ abstract class Tokens : Grammar() { protected val equals: Token by literalToken("=") protected val notEquals: Token by literalToken("\\=") protected val plus: Token by literalToken("+") + protected val minus: Token by literalToken("-") + protected val multiply: Token by literalToken("*") + protected val divide: Token by literalToken("/") protected val dot by literalToken(".") // Ignored tokens diff --git a/src/prolog/Program.kt b/src/prolog/Program.kt index 87e787e..d129f0d 100644 --- a/src/prolog/Program.kt +++ b/src/prolog/Program.kt @@ -48,4 +48,10 @@ object Program : Resolvent { correspondingDBs.forEach { it.clear() } } + + fun reset() { + clear() + variableRenamingStart = 0 + storeNewLine = false + } } \ No newline at end of file diff --git a/src/prolog/ast/arithmetic/Integer.kt b/src/prolog/ast/arithmetic/Integer.kt index 50028a9..dbf9c39 100644 --- a/src/prolog/ast/arithmetic/Integer.kt +++ b/src/prolog/ast/arithmetic/Integer.kt @@ -1,11 +1,15 @@ package prolog.ast.arithmetic +import prolog.Answers import prolog.Substitutions +import prolog.ast.logic.LogicOperand -data class Integer(override val value: Int) : Number { +data class Integer(override val value: Int) : Number, LogicOperand() { // Integers are already evaluated override fun simplify(subs: Substitutions): Simplification = Simplification(this, this) + override fun satisfy(subs: Substitutions): Answers = sequenceOf(Result.success(emptyMap())) + override fun toString(): String = value.toString() override operator fun plus(other: Number): Number = when (other) { diff --git a/src/prolog/ast/logic/Clause.kt b/src/prolog/ast/logic/Clause.kt index 3bf4138..f402951 100644 --- a/src/prolog/ast/logic/Clause.kt +++ b/src/prolog/ast/logic/Clause.kt @@ -8,6 +8,7 @@ import prolog.builtins.True import prolog.flags.AppliedCut import prolog.logic.applySubstitution import prolog.logic.numbervars +import prolog.logic.occurs import prolog.logic.unifyLazy /** @@ -46,7 +47,7 @@ abstract class Clause(val head: Head, val body: Body) : Resolvent { .mapValues { reverse[it.value] ?: it.value } result = result.map { it.key to applySubstitution(it.value, result) } .toMap() - .filterNot { it.key in renamed.keys } + .filterNot { it.key in renamed.keys && !occurs(it.key as Variable, goal, emptyMap())} yield(Result.success(result)) }, onFailure = { error -> diff --git a/src/prolog/ast/terms/Variable.kt b/src/prolog/ast/terms/Variable.kt index 0ef7ee6..f19ea86 100644 --- a/src/prolog/ast/terms/Variable.kt +++ b/src/prolog/ast/terms/Variable.kt @@ -4,8 +4,9 @@ import prolog.Answers import prolog.Substitutions import prolog.ast.arithmetic.Expression import prolog.ast.arithmetic.Simplification +import prolog.ast.logic.LogicOperand -data class Variable(val name: String) : Term, Body, Expression { +data class Variable(val name: String) : Term, Body, Expression, LogicOperand() { override fun simplify(subs: Substitutions): Simplification { // If the variable is bound, return the value of the binding // If the variable is not bound, return the variable itself diff --git a/src/prolog/builtins/io.kt b/src/prolog/builtins/io.kt index 3f9aaae..5806511 100644 --- a/src/prolog/builtins/io.kt +++ b/src/prolog/builtins/io.kt @@ -18,7 +18,12 @@ import prolog.logic.unifyLazy */ class Write(private val term: Term) : Operator(Atom("write"), null, term), Satisfiable { override fun satisfy(subs: Substitutions): Answers { - val t = applySubstitution(term, subs) + var t = term + var temp = applySubstitution(t, subs) + while (t != temp) { + t = temp + temp = applySubstitution(t, subs) + } Terminal().say(t.toString()) diff --git a/tests/compare.sh b/tests/compare.sh index 9bdadab..d876e13 100644 --- a/tests/compare.sh +++ b/tests/compare.sh @@ -2,11 +2,13 @@ # This script is expected to be run from the root of the project. +WATCH_ALONG=0 + # Paths to the two implementations GPL="src/gpl" SPL="swipl" -GPL_FLAGS=("--debug") +GPL_FLAGS=("--error") SPL_FLAGS=("--quiet" "-t" "'true'") # Directory containing test files @@ -14,7 +16,11 @@ TEST_DIR="examples" # Temporary files for storing outputs GPL_OUT=$(mktemp) +GPL_ERR=$(mktemp) SPL_OUT=$(mktemp) +SPL_ERR=$(mktemp) + +touch "$GPL_OUT" "$GPL_ERR" "$SPL_OUT" "$SPL_ERR" # Flag to track if all tests pass PASSED=0 @@ -22,27 +28,43 @@ FAILED=0 # Iterate over all test files in the test directory #for TESTFILE in $(find ${TEST_DIR} -type f); do -files=("examples/program.pl" "examples/basics/disjunction.pl" "examples/basics/fraternity.pl") +files=( +"examples/program.pl" +"examples/basics/disjunction.pl" +"examples/basics/fraternity.pl" +"examples/basics/unification.pl" +"examples/basics/write.pl" +) for TESTFILE in "${files[@]}"; do # Run both programs with the test file - "${SPL}" "${SPL_FLAGS[@]}" "$TESTFILE" > "${SPL_OUT}" 2>&1 - "${GPL}" "${GPL_FLAGS[@]}" -s "$TESTFILE" > "${GPL_OUT}" 2>&1 + "${SPL}" "${SPL_FLAGS[@]}" "$TESTFILE" > "${SPL_OUT}" 2> "${SPL_ERR}" + "${GPL}" "${GPL_FLAGS[@]}" -s "$TESTFILE" > "${GPL_OUT}" 2> "${GPL_ERR}" # Compare the outputs - if diff -q "$SPL_OUT" "$GPL_OUT" > /dev/null; then - PASSED=$((PASSED + 1)) - else - echo "Test failed! Outputs differ for $TESTFILE" - printf "\nTest:\n%s\n" "$(cat "$TESTFILE")" - printf "\nExpected:\n%s\n" "$(cat "$SPL_OUT")" - printf "\nGot:\n%s\n" "$(cat "$GPL_OUT")" + was_different="$( + diff -q "$SPL_OUT" "$GPL_OUT" > /dev/null + echo $? + )" + if [[ "${was_different}" -ne 0 || "${WATCH_ALONG}" -eq 1 ]]; then + if [ "${was_different}" -ne 0 ]; then + message="TEST FAILED" + fi + printf "%s for %s\n" "${message:="Result"}" "${TESTFILE}" + printf "\nTest:\n%s\n" "$(cat "${TESTFILE}")" + printf "\nExpected:\n%s\n" "$(cat "${SPL_OUT}" && cat "${SPL_ERR}")" + printf "\nGot:\n%s\n" "$(cat "${GPL_OUT}" && cat "${GPL_ERR}")" echo "-----------------------------------------" + fi + + if [ "${was_different}" -ne 0 ]; then FAILED=$((FAILED + 1)) + else + PASSED=$((PASSED + 1)) fi done # Clean up temporary files -rm "$SPL_OUT" "$GPL_OUT" +rm "$SPL_OUT" "$GPL_OUT" "$SPL_ERR" "$GPL_ERR" # Final result, summary if [ $FAILED -eq 0 ]; then diff --git a/tests/e2e/Examples.kt b/tests/e2e/Examples.kt new file mode 100644 index 0000000..9febe53 --- /dev/null +++ b/tests/e2e/Examples.kt @@ -0,0 +1,46 @@ +package e2e + +import interpreter.FileLoader +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import prolog.Program +import java.io.ByteArrayOutputStream +import java.io.PrintStream + +class Examples { + private val loader = FileLoader() + + @BeforeEach + fun setup() { + Program.reset() + } + + @Test + fun fraternity() { + val inputFile = "examples/basics/fraternity.pl" + + loader.load(inputFile) + } + + @Test + fun unification() { + val inputFile = "examples/basics/unification.pl" + + loader.load(inputFile) + } + + @Test + fun write() { + val expected = "gpl zegt: dag(wereld)\n" + + val outStream = ByteArrayOutputStream() + System.setOut(PrintStream(outStream)) + + val inputFile = "examples/basics/write.pl" + + // Compare the stdio output with the expected output + loader.load(inputFile) + assertEquals(expected, outStream.toString()) + } +} \ No newline at end of file diff --git a/tests/e2e/myClass.kt b/tests/e2e/myClass.kt deleted file mode 100644 index f5ff7c3..0000000 --- a/tests/e2e/myClass.kt +++ /dev/null @@ -1,4 +0,0 @@ -package e2e - -class myClass { -} \ No newline at end of file diff --git a/tests/parser/OperatorParserTests.kt b/tests/parser/OperatorParserTests.kt index a8bedb0..9c19db2 100644 --- a/tests/parser/OperatorParserTests.kt +++ b/tests/parser/OperatorParserTests.kt @@ -1,5 +1,6 @@ package parser +import com.github.h0tk3y.betterParse.combinators.use import com.github.h0tk3y.betterParse.grammar.Grammar import com.github.h0tk3y.betterParse.grammar.parseToEnd import com.github.h0tk3y.betterParse.parser.Parser @@ -13,7 +14,7 @@ import prolog.ast.terms.Structure class OperatorParserTests { class OperatorParser: TermsGrammar() { - override val rootParser: Parser by operator + override val rootParser: Parser by body use { this as CompoundTerm } } private var parser = OperatorParser() as Grammar diff --git a/tests/parser/grammars/LogicGrammarTests.kt b/tests/parser/grammars/LogicGrammarTests.kt index d7e054e..ba0143d 100644 --- a/tests/parser/grammars/LogicGrammarTests.kt +++ b/tests/parser/grammars/LogicGrammarTests.kt @@ -40,7 +40,7 @@ class LogicGrammarTests { assertEquals(1, result.size, "Expected 1 fact") assertTrue(result[0] is Fact, "Expected a fact") - assertEquals(input, "${result[0].toString()}.", "Expected fact to be '$input'") + assertEquals(input, "${result[0]}.", "Expected fact to be '$input'") } @ParameterizedTest @@ -125,9 +125,12 @@ class LogicGrammarTests { assertEquals(1, result.size, "Expected 1 rule") val rule = result[0] as Rule - assertInstanceOf(CompoundTerm::class.java, rule.body, "Expected body to be a conjunction") - val conjunction = rule.body as CompoundTerm - assertEquals("invited/2", (conjunction.arguments[0] as CompoundTerm).functor, "Expected functor 'invited/2'") + assertEquals("guest/2", rule.head.functor, "Expected functor 'guest/2'") + assertEquals(",/2", (rule.body as CompoundTerm).functor, "Expected functor ',/2'") + val l1 = (rule.body as CompoundTerm).arguments[0] as CompoundTerm + assertEquals(",/2", l1.functor, "Expected functor ',/2'") + val l2 = l1.arguments[0] as CompoundTerm + assertEquals("invited/2", l2.functor, "Expected functor 'invited/2'") } @Test diff --git a/tests/parser/grammars/TermsGrammarTests.kt b/tests/parser/grammars/TermsGrammarTests.kt index 9acbdfd..5166856 100644 --- a/tests/parser/grammars/TermsGrammarTests.kt +++ b/tests/parser/grammars/TermsGrammarTests.kt @@ -2,10 +2,11 @@ package parser.grammars import com.github.h0tk3y.betterParse.grammar.Grammar import com.github.h0tk3y.betterParse.grammar.parseToEnd -import com.github.h0tk3y.betterParse.parser.Parser import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertDoesNotThrow import org.junit.jupiter.params.ParameterizedTest @@ -17,7 +18,6 @@ import prolog.ast.terms.Structure import prolog.ast.terms.Term import prolog.ast.terms.Variable import prolog.logic.equivalent -import kotlin.test.assertEquals class TermsGrammarTests { private lateinit var parser: Grammar @@ -32,7 +32,7 @@ class TermsGrammarTests { fun `parse atom`(name: String) { val result = parser.parseToEnd(name) - Assertions.assertEquals(Atom(name), result, "Expected atom '$name'") + assertEquals(Atom(name), result, "Expected atom '$name'") } @ParameterizedTest @@ -52,7 +52,7 @@ class TermsGrammarTests { val expected = input.substring(1, input.length - 1) - Assertions.assertEquals(Atom(expected), result, "Expected atom") + assertEquals(Atom(expected), result, "Expected atom") } @ParameterizedTest @@ -60,7 +60,7 @@ class TermsGrammarTests { fun `parse variable`(name: String) { val result = parser.parseToEnd(name) - Assertions.assertEquals(Variable(name), result, "Expected atom '$name'") + assertEquals(Variable(name), result, "Expected atom '$name'") } @Test @@ -69,10 +69,7 @@ class TermsGrammarTests { val result = parser.parseToEnd(input) - Assertions.assertTrue( - equivalent(Structure(Atom("f"), emptyList()), result, emptyMap()), - "Expected atom 'f'" - ) + assertEquals(Structure(Atom("f"), emptyList()), result, "Expected atom 'f'") } @Test @@ -81,10 +78,7 @@ class TermsGrammarTests { val result = parser.parseToEnd(input) - Assertions.assertTrue( - equivalent(Structure(Atom("f"), listOf(Atom("a"))), result, emptyMap()), - "Expected atom 'f(a)'" - ) + assertEquals(Structure(Atom("f"), listOf(Atom("a"))), result, "Expected atom 'f(a)'") } @Test @@ -93,8 +87,9 @@ class TermsGrammarTests { val result = parser.parseToEnd(input) - Assertions.assertTrue( - equivalent(Structure(Atom("f"), listOf(Atom("a"), Atom("b"))), result, emptyMap()), + assertEquals( + Structure(Atom("f"), listOf(Atom("a"), Atom("b"))), + result, "Expected atom 'f(a, b)'" ) } @@ -105,7 +100,7 @@ class TermsGrammarTests { val result = parser.parseToEnd(input) - Assertions.assertTrue( + assertTrue( equivalent(Structure(Atom("f"), listOf(Atom("a"), Variable("X"))), result, emptyMap()), "Expected atom 'f(a, X)'" ) @@ -179,4 +174,59 @@ class TermsGrammarTests { fun `parse unification`(input: String) { assertDoesNotThrow { parser.parseToEnd(input) } } + + @Nested + class `Operator precedence` { + private lateinit var parser: Grammar + + @BeforeEach + fun setup() { + parser = TermsGrammar() as Grammar + } + + @Test + fun `parse addition and multiplication`() { + val input = "1 + 2 * 3" + + val result = parser.parseToEnd(input) + + assertEquals( + Structure(Atom("+"), listOf(Integer(1), Structure(Atom("*"), listOf(Integer(2), Integer(3))))), + result, + "Expected addition and multiplication" + ) + } + + @Test + fun `parse multiplication and addition`() { + val input = "1 * 2 + 3" + + val result = parser.parseToEnd(input) + + assertEquals( + Structure(Atom("+"), listOf(Structure(Atom("*"), listOf(Integer(1), Integer(2))), Integer(3))), + result, + "Expected multiplication and addition" + ) + } + + @Test + fun `complex expression`() { + val input = "1 + 2 * 3 - 4 / 5" + + val result = parser.parseToEnd(input) + + assertEquals( + Structure( + Atom("-"), + listOf( + Structure(Atom("+"), listOf(Integer(1), Structure(Atom("*"), listOf(Integer(2), Integer(3))))), + Structure(Atom("/"), listOf(Integer(4), Integer(5))) + ) + ), + result, + "Expected complex expression" + ) + } + } } \ No newline at end of file diff --git a/tests/prolog/EvaluationTests.kt b/tests/prolog/EvaluationTests.kt index cab3d0a..3d185bb 100644 --- a/tests/prolog/EvaluationTests.kt +++ b/tests/prolog/EvaluationTests.kt @@ -390,5 +390,27 @@ class EvaluationTests { assertEquals(1, subs5.size, "Expected 1 substitution, especially without 'Person'") assertEquals(Atom("bob"), subs5[Variable("X")], "Expected bob") } + + @Test + fun `likes_italian_food(Person)`() { + val result = Program.query(Structure(Atom("likes_italian_food"), listOf(Variable("Person")))).toList() + + assertEquals(3, result.size, "Expected 3 results") + + assertTrue(result[0].isSuccess, "Expected success") + val subs3 = result[0].getOrNull()!! + assertEquals(1, subs3.size, "Expected 1 substitution") + assertEquals(Atom("alice"), subs3[Variable("Person")], "Expected alice") + + assertTrue(result[1].isSuccess, "Expected success") + val subs4 = result[1].getOrNull()!! + assertEquals(1, subs4.size, "Expected 1 substitution") + assertEquals(Atom("alice"), subs4[Variable("Person")], "Expected alice") + + assertTrue(result[2].isSuccess, "Expected success") + val subs5 = result[2].getOrNull()!! + assertEquals(1, subs5.size, "Expected 1 substitution") + assertEquals(Atom("bob"), subs5[Variable("Person")], "Expected bob") + } } } diff --git a/tests/prolog/builtins/IoOperatorsTests.kt b/tests/prolog/builtins/IoOperatorsTests.kt index 8f7d5fe..f36a41a 100644 --- a/tests/prolog/builtins/IoOperatorsTests.kt +++ b/tests/prolog/builtins/IoOperatorsTests.kt @@ -1,21 +1,18 @@ package prolog.builtins -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertInstanceOf -import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource import prolog.ast.arithmetic.Float +import prolog.ast.arithmetic.Integer import prolog.ast.terms.Atom import prolog.ast.terms.CompoundTerm -import java.io.ByteArrayOutputStream -import java.io.PrintStream -import prolog.ast.arithmetic.Integer import prolog.ast.terms.Variable import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.io.PrintStream class IoOperatorsTests { private var outStream = ByteArrayOutputStream()