diff --git a/examples/basics/password.pl b/examples/basics/password.pl new file mode 100644 index 0000000..7322247 --- /dev/null +++ b/examples/basics/password.pl @@ -0,0 +1,19 @@ +login :- + getdata(_, _), + writeln('Welcome!'). +login :- + repeat, + writeln('Sorry, you are not permitted.'), + getdata(_, _), + writeln('Welcome'). + +getdata(Name, Password) :- + write('Username: '), + read(Name), + write('Password: '), + read(Password), + user(Name, Password). + +user(john, john1). +user(jimmy, jimmy1). +user(mary, mary1). diff --git a/examples/basics/repeat.pl b/examples/basics/repeat.pl new file mode 100644 index 0000000..35efa9b --- /dev/null +++ b/examples/basics/repeat.pl @@ -0,0 +1,2 @@ +repeat. +repeat :- repeat. \ No newline at end of file diff --git a/examples/basics/summer.pl b/examples/basics/summer.pl index ba759d9..f0ad7a3 100644 --- a/examples/basics/summer.pl +++ b/examples/basics/summer.pl @@ -14,9 +14,9 @@ my_sum :- write('The sum is: '), write(Sum), nl. -main :- +summer_main :- Start = 1, End = 9, summer(Start, End, Sum), write('The sum of '), write(Start), write(' to '), write(End), write(' is: '), write(Sum), nl. -:- initialization(main). +:- initialization(summer_main). diff --git a/examples/demo.pl b/examples/demo.pl new file mode 100644 index 0000000..ed94765 --- /dev/null +++ b/examples/demo.pl @@ -0,0 +1,164 @@ +:- initialization(demo). + +% % % % % % % % % % +% DEMO MENU LOGIC % +% % % % % % % % % % + +demo :- + clear, + header, + menu. + +menu :- + nl, + options, + write('DEMO: Enter your choice: '), + read(Choice), + choice(Choice). + +header :- + writeln('* * * * * * * * * * * * * * * * * * * * * * * * *'), + writeln('* GHENT PROLOG DEMO *'), + writeln('* * * * * * * * * * * * * * * * * * * * * * * * *'). + +options :- + writeln('-------------------------------------------------'), + writeln('INPUT DESCRIPTION CATEGORY SOURCE'), + writeln('-------------------------------------------------'), + writeln('(1) A password prompt basics password.pl'), + writeln('(2) Add i in [a..b] maths summer.pl'), + writeln('(3) A translator meta mib_voorbeelden.pl'), + writeln('(4) A tale of a ceremony cont. ceremony.pl'), + writeln('(5) Maths with Shift/Reset cont. continuations.pl'), +% writeln('(6) Alternative lists meta my_list.pl'), + nl, + writeln('(interpreter) to enter the interpreter'), + writeln('(exit) to exit the demo'), + nl. + +choice(1) :- !, run_login_example, menu. +choice(2) :- !, run_summer_example, menu. +choice(3) :- !, run_mib_voorbeelden_example, continue, menu. +choice(4) :- !, run_ceremony_example, continue, menu. +choice(5) :- !, run_continuations_example, continue, menu. +%choice(6) :- !, run_my_list_example, continue, menu. +choice(interpreter) :- !, run_interpreter, continue, menu. +choice(exit) :- !, writeln('Exiting...'). +choice(_) :- write('DEMO: Invalid choice, please try again: '), read(Choice), choice(Choice). + +% % % % % % % % % % +% DEMO REPOSITORY % +% % % % % % % % % % + +run_login_example :- + say('Loading login example...'), + consult('examples/basics/repeat.pl'), + consult('examples/basics/password.pl'), + say('Showing login/0'), + say('You might try logging in with tibo/noaccess and john/john1.'), + login, login_example_loop, !, + say('Login example finished.'). + +login_example_loop :- + yes_or_no('Do you want to try again?', (login, login_example_loop), true). + +run_summer_example :- + say('Loading summer example...'), + consult('examples/basics/summer.pl'), + say('Showing my_sum/1'), + say('You might try summing 1 to 9.'), + my_sum, summer_example_loop, !, + say('Summer example finished.'). + +summer_example_loop :- + yes_or_no('Do you want to try again?', (my_sum, summer_example_loop), true). + +run_mib_voorbeelden_example :- + say('Loading mib_voorbeelden example...'), + consult('examples/meta/mib_voorbeelden.pl'), clear, + say('Showing example1/0'), example1, next(( + say('Showing example2/0'), example2, next(( + say('Showing example3/0'), example3)))), + say('mib_voorbeelden example finished.'). + +run_ceremony_example :- + say('Loading ceremony example...'), + consult('examples/meta/ceremony.pl'), clear, + say('Showing ceremony_main/0'), + ceremony_main, + say('Ceremony example finished.'). + +run_continuations_example :- + say('Loading continuations example...'), + consult('examples/meta/continuations.pl'), clear, + say('Showing continuations_main/0'), + continuations_main, + say('Continuations example finished.'). + +run_my_list_example :- + say('Loading my_list example...'), + consult('examples/meta/my_list.pl'), + say('Showing my_member/2'), + showcase(my_member(X, a(b(c))), 'X', X), + continue, + say('Showing my_length/2'), + showcase(my_length(a(b(c)), Length), 'Length', Length), + say('my_list example finished.'). + +run_interpreter :- + say('Loading interpreter...'), + consult('examples/meta/ground.pl'), + consult('examples/meta/interpreter.pl'), + say('Showing interpreter/0'), + say('Have fun!'), + interpreter, + say('Interpreter finished.'). + +% % % % % % % % % % % +% HELPER PREDICATES % +% % % % % % % % % % % + +yes_or_no(Question, IfYes, IfNo) :- + write(Question), write(' (y/n): '), + read(Answer), + ( ( was_yes(Answer), !, call(IfYes)) ; ( was_no(Answer), !, call(IfNo))). + +was_yes(y). +was_yes('Y'). +was_yes(yes). +was_yes('Yes'). +was_yes('YES'). + +was_no(n). +was_no('N'). +was_no(no). +was_no('No'). +was_no('NO'). + +say(Message) :- + write('DEMO: '), + writeln(Message). + +showcase(Goal, VarName, Var) :- + say(Goal), + call(Goal), + say(solution(VarName, Var)). + +showcase(Goal) :- + Goal =.. List, + say(List), + call(Goal). + +next(Next) :- yes_or_no('See next?', call(Next), true). + +continue :- + write('Write anything to continue... '), + read(_). + +clear :- clear(30). + +clear(0) :- !. +clear(N) :- + nl, + M is N - 1, + clear(M). diff --git a/examples/meta/calculator.pl b/examples/meta/calculator.pl deleted file mode 100644 index 3ac4216..0000000 --- a/examples/meta/calculator.pl +++ /dev/null @@ -1,15 +0,0 @@ -accumulator :- - Sum = 0, - shift(Number), - NewSum is Sum + Number, - write("Result is: "), writeln(NewSum). - -main :- - reset(accumulator, Number, Cont), - between(1, 5, Number), - forall() - call(Cont), - writeln("End of calculation"). - -:- initialization(main). - diff --git a/examples/meta/ceremony.pl b/examples/meta/ceremony.pl index e378367..2e5138d 100644 --- a/examples/meta/ceremony.pl +++ b/examples/meta/ceremony.pl @@ -4,7 +4,7 @@ printer :- shift(Name), write(Name), write(", "). -main :- +ceremony_main :- writeln("/Ceremony starts/"), reset(printer, Name, Cont), \+ \+ ( Name = "John", call(Cont) ), @@ -12,4 +12,4 @@ main :- writeln("and my parents!"), writeln("/Ceremony ends/"). -:- initialization(main). +:- initialization(ceremony_main). diff --git a/examples/meta/continuations.pl b/examples/meta/continuations.pl index 8661361..8742fb5 100644 --- a/examples/meta/continuations.pl +++ b/examples/meta/continuations.pl @@ -12,11 +12,11 @@ test_ :- X is 1 + (2 * Y), write("In test X = "), write(X), writeln("; done"). -main :- +continuations_main :- test(Cont, Term), \+ \+ ( writeln("Calling Cont(2)"), Term = 2, call(Cont)), \+ \+ ( writeln("Calling Cont(4)"), Term = 4, call(Cont)). -:- initialization(main). +:- initialization(continuations_main). diff --git a/examples/meta/ground.pl b/examples/meta/ground.pl new file mode 100644 index 0000000..d7af4d7 --- /dev/null +++ b/examples/meta/ground.pl @@ -0,0 +1,15 @@ +ground(T) :- + nonvar(T), atomic(T), + !. +ground(T) :- + nonvar(T), compound(T), + functor(T, _, N), + ground(T, N). + +ground(T, N) :- + N \== 0, + arg(N, T, A), + ground(A), + M is N - 1, + ground(T, M). +ground(T, 0). diff --git a/examples/meta/interpreter.pl b/examples/meta/interpreter.pl new file mode 100644 index 0000000..30f74d3 --- /dev/null +++ b/examples/meta/interpreter.pl @@ -0,0 +1,27 @@ +interpreter :- + interpreter_prompt, + read(Goal), + interpreter(Goal). + +interpreter_prompt :- write('Next Command ?-- '). + +interpreter(exit) :- !. +interpreter(Goal) :- + ground(Goal), !, + solve_ground(Goal), + interpreter. +interpreter(Goal) :- + solve(Goal), + interpreter. + +solve_ground(Goal) :- + call(Goal), + !, + writeln('True :)'). +solve_ground(_) :- writeln('False :('). + +solve(Goal) :- + call(Goal), + writeln(Goal), + fail. +solve(_) :- writeln('No more solutions :/'). diff --git a/examples/meta/mib_voorbeelden.pl b/examples/meta/mib_voorbeelden.pl index 47003d5..e3cd52b 100644 --- a/examples/meta/mib_voorbeelden.pl +++ b/examples/meta/mib_voorbeelden.pl @@ -40,9 +40,9 @@ example3 :- mib(f, e, Result2, Result3), write(Result3), nl. -main :- +mib_main :- example1, example2, example3. -:- initialization(main). +:- initialization(mib_main). diff --git a/examples/meta/my_list.pl b/examples/meta/my_list.pl new file mode 100644 index 0000000..0f6b1de --- /dev/null +++ b/examples/meta/my_list.pl @@ -0,0 +1,14 @@ +% my_member(Element, List) - checks if Element is in List +my_member(Element, Element) :- atomic(Element). +my_member(Element, Compound) :- + compound(Compound), + Compound =.. [Head, Tail], + (Element = Head ; my_member(Element, Tail)). + +% my_length(List, Length) - finds the my_length of a list +my_length(Atom, 1) :- atomic(Atom). +my_length(Compound, N) :- + compound(Compound), + Compound =.. [_, Tail], + my_length(Tail, M), + N is M + 1. diff --git a/src/prolog/builtins/analysisOperators.kt b/src/prolog/builtins/analysisOperators.kt index 7f83ab0..75b7711 100644 --- a/src/prolog/builtins/analysisOperators.kt +++ b/src/prolog/builtins/analysisOperators.kt @@ -159,27 +159,31 @@ class ClauseOp(private val head: Head, private val body: Body) : open class Univ(private val term: Term, private val list: Term) : Operator("=..", term, list) { override fun satisfy(subs: Substitutions): Answers { - return when { - nonvariable(term, subs) && nonvariable(list, subs) -> { - val t = applySubstitution(term, subs) - val l = applySubstitution(list, subs) as List - unifyLazy(t, listToTerm(l), subs) - } + if (variable(term, subs)) { + require(nonvariable(list, subs)) { "Arguments are not sufficiently instantiated" } + val l = applySubstitution(list, subs) as List + require(nonvariable(l.head, subs)) { "Arguments are not sufficiently instantiated" } - variable(term, subs) && nonvariable(list, subs) -> { - val l = applySubstitution(list, subs) as List - val t = listToTerm(l) - unifyLazy(term, t, subs) - } - - nonvariable(term, subs) && variable(list, subs) -> { - val t = applySubstitution(term, subs) - val l = termToList(t) - unifyLazy(l, list, subs) - } - - else -> throw Exception("Arguments are not sufficiently instantiated") + val t = listToTerm(l) + return unifyLazy(term, t, subs) } + + if (nonvariable(list, subs)) { + val l = applySubstitution(list, subs) as List + + if (nonvariable(l.head, subs)) { + val t = applySubstitution(term, subs) + return unifyLazy(t, listToTerm(l), subs) + } + + val t = applySubstitution(term, subs) + val expectedL = termToList(t) + return unifyLazy(expectedL, l, subs) + } + + val t = applySubstitution(term, subs) + val l = termToList(t) + return unifyLazy(l, list, subs) } protected open fun listToTerm(list: List): Term { diff --git a/src/repl/Repl.kt b/src/repl/Repl.kt index 688cea4..70b95db 100644 --- a/src/repl/Repl.kt +++ b/src/repl/Repl.kt @@ -20,6 +20,7 @@ class Repl { printAnswers(query()) } catch (e: Exception) { Logger.error("Error parsing REPL: ${e.message}") + Logger.debug(e.stackTraceToString()) } } } diff --git a/tests/prolog/builtins/AnalysisOperatorsTests.kt b/tests/prolog/builtins/AnalysisOperatorsTests.kt index a6866c3..1f4981a 100644 --- a/tests/prolog/builtins/AnalysisOperatorsTests.kt +++ b/tests/prolog/builtins/AnalysisOperatorsTests.kt @@ -568,6 +568,23 @@ class AnalysisOperatorsTests { assertEquals(0, result.size, "Expected 0 results") } + @Test + fun `univ extra debugging`() { + val univ = Univ( + Structure("baz", Variable("Foo")), + Cons(Variable("Baz"), Cons(Structure("foo", Integer(1)), Empty)) + ) + + val result = univ.satisfy(emptyMap()).toList() + + assertEquals(1, result.size, "Expected 1 result") + assertTrue(result[0].isSuccess, "Expected success") + val subs = result[0].getOrNull()!! + assertEquals(2, subs.size, "Expected 2 substitutions") + assertEquals(Atom("baz"), subs[Variable("Baz")], "Expected Baz to be baz") + assertEquals(Structure("foo", Integer(1)), subs[Variable("Foo")], "Expected Foo to be foo(1)") + } + @Test fun `univ with two vars should throw`() { val univ = Univ(Variable("X"), Variable("Y"))