diff --git a/benches/reif.pl b/benches/reif.pl new file mode 100644 index 000000000..e75aa757c --- /dev/null +++ b/benches/reif.pl @@ -0,0 +1,63 @@ +/* +Copied from^W^W Inspired by memberbench[1], but has only benchmarks relevant to +goal expansion of if_/3 and is adapted to be better integrated into Scryer's +benchmarking subsystem: which doesn't need time reporting, and supports only +single test at a time. + +[1]: http://www.complang.tuwien.ac.at/ulrich/Prolog-inedit/sicstus/memberbench.pl +*/ + +:- use_module(library(reif)). +:- use_module(library(lists)). +:- use_module(library(si)). + + +run(Test, Count) :- + atom_si(Test), + integer_si(Count), + exptrue(Count), + \+ benchmark(Test, z, "abcdefghijklmnopqrstuvwxyz ") + ; true. + + +% Baseline test – the fastest possible implementation +benchmark(memberchk, E, Es) :- memberchk(E, Es). + +% Expanded if_/3 +benchmark(memberd_ifc, E, Es) :- memberd_ifc(E, Es). + +% Non-expanded if_/3 +benchmark(memberd_fif, E, Es) :- memberd_fif(E, Es). + + +memberd_ifc(X, [E|Es]) :- + if_(X = E, true, memberd_ifc(X, Es)). + +memberd_fif(X, [E|Es]) :- + fif_(X = E, true, memberd_fif(X, Es)). + + +% Copy of _if/3, but with a different name, so it won't be expanded +fif_(If_1, Then_0, Else_0) :- + call(If_1, T), + ( T == true -> Then_0 + ; T == false -> Else_0 + ; nonvar(T) -> throw(error(type_error(boolean, T), _)) + ; throw(error(instantiation_error, _)) + ). + + +%% exptrue(N). +% +% Succeeds 10^N times if N is ground, usefull as a cheap way to repeat a given +% predicate many times in benchmarks. There are many more ways to generate +% choice points, but this one by far has the lowest overhead. +exptrue(0). +exptrue(1) :- ten. +exptrue(2) :- ten, ten. +exptrue(3) :- ten, ten, ten. +exptrue(4) :- ten, ten, ten, ten. +exptrue(5) :- ten, ten, ten, ten, ten. +exptrue(6) :- ten, ten, ten, ten, ten, ten. + +ten. ten. ten. ten. ten. ten. ten. ten. ten. ten. diff --git a/benches/run_iai.rs b/benches/run_iai.rs index 27f003394..a5b682efa 100644 --- a/benches/run_iai.rs +++ b/benches/run_iai.rs @@ -13,6 +13,9 @@ mod iai { #[bench::count_edges(setup::prolog_benches()["count_edges"].setup())] #[bench::numlist(setup::prolog_benches()["numlist"].setup())] #[bench::csv_codename(setup::prolog_benches()["csv_codename"].setup())] + #[bench::memberbench_baseline(setup::prolog_benches()["memberbench_baseline"].setup())] + #[bench::memberbench_if_expanded(setup::prolog_benches()["memberbench_if_expanded"].setup())] + #[bench::memberbench_if_not_expanded(setup::prolog_benches()["memberbench_if_not_expanded"].setup())] fn bench(mut run: impl FnMut() -> Vec) -> Vec { run() } diff --git a/benches/setup.rs b/benches/setup.rs index ac055c083..e0da062b3 100644 --- a/benches/setup.rs +++ b/benches/setup.rs @@ -26,6 +26,33 @@ pub fn prolog_benches() -> BTreeMap<&'static str, PrologBenchmark> { Strategy::Reuse, btreemap! { "Name" => Term::string("SPACE") }, ), + /* FIXME: Following 3 benchmarks don't bind any variables and shouldn't + * produce any leaf answer, but test validate_benchmarks() fails if last + * element is `btreemap! {}`, because result of a query is `[True]`, but + * test case expects `[LeafAnswer { bindings: {} }]`. As a workaround + * I've just added dummy variable unification. + */ + ( + "memberbench_baseline", + "benches/reif.pl", + "run(memberchk,4),X=done.", + Strategy::Reuse, + btreemap! { "X" => Term::atom("done") }, + ), + ( + "memberbench_if_expanded", + "benches/reif.pl", + "run(memberd_ifc,4),Y=done.", + Strategy::Reuse, + btreemap! { "Y" => Term::atom("done") }, + ), + ( + "memberbench_if_not_expanded", + "benches/reif.pl", + "run(memberd_fif,4),Z=done.", + Strategy::Reuse, + btreemap! { "Z" => Term::atom("done") }, + ), ] .map(|b| { ( diff --git a/src/lib/reif.pl b/src/lib/reif.pl index 55618e84e..87a6e0213 100644 --- a/src/lib/reif.pl +++ b/src/lib/reif.pl @@ -1,54 +1,241 @@ -/** Predicates from [*Indexing dif/2*](https://arxiv.org/abs/1607.01590). +:- module(reif, + [if_/3, + cond_t/3, + (=)/3, + dif/3, + (',')/3, + (;)/3, + memberd_t/3, + tmember/2, + tmember_t/3, + tfilter/3, + tpartition/4 +% + ]). +% +% +% -Example: +/** Reified if, reification library v3 -``` -?- tfilter(=(a), [X,Y], Es). - X = a, Y = a, Es = "aa" -; X = a, Es = "a", dif:dif(a,Y) -; Y = a, Es = "a", dif:dif(a,X) -; Es = [], dif:dif(a,X), dif:dif(a,Y). -``` +@author Ulrich Neumerkel +see Indexing dif/2 +U. Neumerkel and S. Kral. https://arxiv.org/abs/1607.01590 [cs.PL]. July 2016. */ -:- module(reif, [if_/3, (=)/3, (',')/3, (;)/3, cond_t/3, dif/3, - memberd_t/3, tfilter/3, tmember/2, tmember_t/3, - tpartition/4]). - :- use_module(library(dif)). +:- use_module(library(loader), [goal_sanitized/2]). :- meta_predicate(if_(1, 0, 0)). +:- meta_predicate(cond_t(1, 0, ?)). +:- meta_predicate(tfilter(2, ?, ?)). +:- meta_predicate(tpartition(2, ?, ?, ?)). +:- meta_predicate(','(1, 1, ?)). +:- meta_predicate(;(1, 1, ?)). +:- meta_predicate(tmember(2, ?)). +:- meta_predicate(tmember_t(2, ?, ?)). -if_(If_1, Then_0, Else_0) :- - call(If_1, T), - ( T == true -> call(Then_0) - ; T == false -> call(Else_0) - ; nonvar(T) -> throw(error(type_error(boolean, T), _)) - ; throw(error(instantiation_error, _)) - ). +:- op(900, fy, [$]). -=(X, Y, T) :- - ( X == Y -> T = true - ; X \= Y -> T = false - ; T = true, X = Y - ; T = false, dif(X, Y) - ). +% uwnportray(T) :- write_term(T,[quoted(true)]),nl. -dif(X, Y, T) :- - =(X, Y, NT), - non(NT, T). +uwnportray(T) :- portray_clause(T). % Item#539 -non(true, false). -non(false, true). +$(X) :- uwnportray(call-X),X,uwnportray(exit-X). +$(C,V1) :- + $call(C,V1). +$(C,V1,V2) :- + $call(C,V1,V2). +$(C,V1,V2,V3) :- + $call(C,V1,V2,V3). +$(C,V1,V2,V3,V4) :- + $call(C,V1,V2,V3,V4). +$(C,V1,V2,V3,V4,V5) :- + $call(C,V1,V2,V3,V4,V5). +$(C,V1,V2,V3,V4,V5,V6) :- + $call(C,V1,V2,V3,V4,V5,V6). +$(C,V1,V2,V3,V4,V5,V6,V7) :- + $call(C,V1,V2,V3,V4,V5,V6,V7). -:- meta_predicate(tfilter(2, ?, ?)). +goal_expanded(MG_0, MGx_0) :- + var(MG_0), + !, + MG_0 = MGx_0. +goal_expanded(call(MG_1, X), MGx_0) :- + MG_1 = M:G_1, atom(M), callable(G_1), G_1 \= (_:_), + functor_(G_1, G_0, X), + \+ predicate_property(M:G_0, (meta_predicate _)), + !, + MGx_0 = M:G_0. +goal_expanded(call(G_0), Gx_0) :- + acyclic_term(G_0), + nonvar(G_0), + % more conditions + !, + G_0 = Gx_0. +goal_expanded(MG_0, MG_0). -tfilter(_, [], []). -tfilter(C_2, [E|Es], Fs0) :- - if_(call(C_2, E), Fs0 = [E|Fs], Fs0 = Fs), - tfilter(C_2, Es, Fs). -:- meta_predicate(tpartition(2, ?, ?, ?)). +functor_(T, TA, A) :- + functor(T, F, N0), + N1 is N0+1, + functor(TA, F, N1), + arg(N1, TA, A), + sameargs(N0, T, TA). + +sameargs(N0, S, T) :- + N0 > 0, + N1 is N0-1, + arg(N0, S, A), + arg(N0, T, A), + sameargs(N1, S, T). +sameargs(0, _, _). + + + + +/* 2do: unqualified If_1: error +*/ + +% +user:goal_expansion(if_(If_1, Then_0, Else_0), G_0) :- + goal_sanitized(Then_0, SanitizedThen_0), + goal_sanitized(Else_0, SanitizedElse_0), + ugoal_expansion(if_(If_1, SanitizedThen_0, SanitizedElse_0), G_0). + +% +% +% +% +% +% +% +% +% +% +% +ugoal_expansion(if_(If_1, Then_0, Else_0), Goal_0) :- + nonvar(If_1), If_1 = (X = Y), + goal_expanded(call(Then_0), Thenx_0), + goal_expanded(call(Else_0), Elsex_0), + !, + Goal_0 = + ( X \= Y -> Elsex_0 + ; X == Y -> Thenx_0 + ; X = Y, Thenx_0 + ; dif(X,Y), Elsex_0 + ). +ugoal_expansion(if_(If_1, Then_0, Else_0), Goal) :- + nonvar(If_1), If_1 = dif(X, Y), + goal_expanded(call(Then_0), Thenx_0), + goal_expanded(call(Else_0), Elsex_0), + !, + Goal = + ( X \= Y -> Thenx_0 + ; X == Y -> Elsex_0 + ; X = Y, Elsex_0 + ; dif(X,Y), Thenx_0 + ). +% if_((A_1;B_1), Then_0, Else_0) +% => if_(A_1, Then_0, if_(B_1, Then_0, Else_0)) +ugoal_expansion(if_(If_1, Then_0, Else_0), Goal) :- + subsumes_term((A_1;B_1), If_1), + (A_1;B_1) = If_1, + !, + Goal = if_(A_1, Then_0, if_(B_1, Then_0, Else_0)). +ugoal_expansion(if_(If_1, Then_0, Else_0), Goal_0) :- + subsumes_term((A_1,B_1), If_1), + (A_1,B_1) = If_1, + !, + Goal_0 = if_(A_1, if_(B_1, Then_0, Else_0), Else_0). +ugoal_expansion(if_(If_1, Then_0, Else_0), Goal_0) :- + goal_expanded(call(If_1, T), Ifx_0), + goal_expanded(call(Then_0), Thenx_0), + goal_expanded(call(Else_0), Elsex_0), + Goal_0 = + ( Ifx_0, + ( T == true -> Thenx_0 + ; T == false -> Elsex_0 + ; nonvar(T) -> throw(error(type_error(boolean,T), + type_error(call(If_1,T),2,boolean,T))) + ; throw(error(instantiation_error, + instantiation_error(call(If_1,T),2))) + ) + ). +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% +% + +if_(If_1, Then_0, Else_0) :- + call(If_1, T), + ( T == true -> Then_0 + ; T == false -> Else_0 + ; nonvar(T) -> throw(error(type_error(boolean,T), + type_error(call(If_1,T),2,boolean,T))) + ; throw(error(instantiation_error,instantiation_error(call(If_1,T),2))) + ). + + +tfilter(C_2, Es, Fs) :- + i_tfilter(Es, C_2, Fs). + +i_tfilter([], _, []). +i_tfilter([E|Es], C_2, Fs0) :- + if_(call(C_2, E), Fs0 = [E|Fs], Fs0 = Fs), + i_tfilter(Es, C_2, Fs). tpartition(P_2, Xs, Ts, Fs) :- i_tpartition(Xs, P_2, Ts, Fs). @@ -60,17 +247,26 @@ , ( Fs0 = [X|Fs], Ts0 = Ts ) ), i_tpartition(Xs, P_2, Ts, Fs). -:- meta_predicate(','(1, 1, ?)). +=(X, Y, T) :- + ( X == Y -> T = true + ; X \= Y -> T = false + ; T = true, X = Y + ; T = false, + dif(X, Y) + ). -','(A_1, B_1, T) :- - if_(A_1, call(B_1, T), T = false). +dif(X, Y, T) :- + =(X, Y, NT), + non(NT, T). -:- meta_predicate(';'(1, 1, ?)). +non(true, false). +non(false, true). -';'(A_1, B_1, T) :- - if_(A_1, T = true, call(B_1, T)). +','(A_1, B_1, T) :- + if_(A_1, call(B_1, T), T = false). -:- meta_predicate(cond_t(1, 0, ?)). +;(A_1, B_1, T) :- + if_(A_1, T = true, call(B_1, T)). cond_t(If_1, Then_0, T) :- if_(If_1, ( Then_0, T = true ), T = false ). @@ -82,13 +278,9 @@ i_memberd_t([X|Xs], E, T) :- if_( X = E, T = true, i_memberd_t(Xs, E, T) ). -:- meta_predicate(tmember(2, ?)). - tmember(P_2, [X|Xs]) :- if_( call(P_2, X), true, tmember(P_2, Xs) ). -:- meta_predicate(tmember_t(2, ?, ?)). - tmember_t(_P_2, [], false). tmember_t(P_2, [X|Xs], T) :- if_( call(P_2, X), T = true, tmember_t(P_2, Xs, T) ). diff --git a/src/loader.pl b/src/loader.pl index 59d434d6c..d2a205c0b 100644 --- a/src/loader.pl +++ b/src/loader.pl @@ -8,7 +8,8 @@ strip_module/3, use_module/1, use_module/2, - current_module/1 + current_module/1, + goal_sanitized/2 ]). :- use_module(library(error)). @@ -42,6 +43,67 @@ nl, '$fail'. + +never(_) :- fail. + + +%% goal_sanitized(?G_0, ?S_0). +% +% Both `G_0` and `S_0` are valid callable terms having the same meaning, but +% additionally `S_0` is safe to be called in combination with surrounding +% goals, without worrying that cut side-effect will escape and contaminate +% outer goals. `S_0 = call(G_0)` when it contains callable !s that cut outside, +% and `S_0 = G_0` otherwise. +% +% For example: given `G_0 = (a,!)` then compound goal `b,a,!` will remove +% choice points generated by `b`, but since `S_0 = call((a,!))` then +% `b, call((a,!))` is safe. +% +% TODO: Should it be marked with meta_predicate(goal_sanitized(:,-))? +% +goal_sanitized(G, S) :- + catch(goal_sanitized_aux(G, S), stop(_), false). + +goal_sanitized_aux(G, call(G)) :- cuts_outside(G). +goal_sanitized_aux(G, G ) :- \+ cuts_outside(G). + + +%% cuts_outside(?G_0). +% +% `G_0` is a goal for which side-effects of a cut may spill out to the +% surrounding goals. Throws `stop(_)` when it doesn't represent a valid goal. +% +% For example it succeeds for terms `a, (!, b)` and `a, b -> !` where cut +% removes choice points generated by `a`, but fails for `a, (! -> b)` and +% `a, \+ \+ !`. +% +cuts_outside(G_0) :- cuts_outside(G_0, =(!)). + +%% cuts_outside(?G_0, +StopCondition_1). +% +cuts_outside(G, C_1) :- callable_term(G), cuts_outside_aux(G, C_1). + +cuts_outside_aux(G, C_1) :- call(C_1, G). +cuts_outside_aux(M:A, C_1) :- module_name(M), cuts_outside(A, C_1). +cuts_outside_aux((A,B), C_1) :- cuts_outside(B, C_1); cuts_outside(A, C_1). +cuts_outside_aux((A;B), C_1) :- cuts_outside(B, C_1); cuts_outside(A, C_1). +% FIXME: There is an issue with `C, (! -> B)` construct, see #2739 +cuts_outside_aux((A->B), C_1) :- cuts_outside(A, loader:never); cuts_outside(B, C_1). + + +module_name(M) :- + atom(M) -> true; throw(stop(type_error(atom,M))). + + +callable_term(T) :- + callable(T) -> + ( acyclic_term(T) -> + true + ; throw(stop(type_error(acyclic_term,T))) + ) + ; throw(stop(type_error(callable,T))). + + expand_term(Term, ExpandedTerm) :- ( '$predicate_defined'(user, term_expansion, 2), catch(user:term_expansion(Term, ExpandedTerm0), diff --git a/src/tests/reif.pl b/src/tests/reif.pl new file mode 100644 index 000000000..8684cdc9b --- /dev/null +++ b/src/tests/reif.pl @@ -0,0 +1,180 @@ +:- module(reif_tests, []). + +:- use_module(library(reif)). +:- use_module(library(lists)). +:- use_module(library(dif)). +:- use_module(library(lambda)). +:- use_module(library(random)). +:- use_module(test_framework). + +/* +Those tests are just sanity checks – examples from the paper, to make sure I +haven't messed up. +*/ +test("indexing dif/2 p6#1", ( + findall(X-Fs, tfilter(=(X),[1,2,3,2,3,3],Fs), [1-[1], 2-[2,2], 3-[3,3,3], Y-[]]), + maplist(dif(Y), [1,2,3]) +)). +test("indexing dif/2 p6#2", findall(X, duplicate(X,[1,2,3,2,3,3]), [2,3])). +test("indexing dif/2 p7#1", firstduplicate(1, [1,2,3,1])). +test("indexing dif/2 p7#2",( + firstduplicate(X, [1,2,3,1]), + X == 1 +)). +test("indexing dif/2 p7#3", ( + findall(Y-A-B-C, firstduplicate(Y,[A,B,C]), [X-X-X-_, X-X-B1-X, X-A2-X-X]), + dif(B1,X), + dif(A2,X) +)). + +test("doesnt modify free variables", (reif:goal_expanded(A,B), A == B, var(A))). +test("expands call/1", reif:goal_expanded(call(a), a)). +test("expands call/1 for modules", reif:goal_expanded(call(a:b(1)), a:b(1))). +test("expands call/2 for modules", reif:goal_expanded(call(a:b,c), a:b(c))). +test("doesn't expand call/2", reif:goal_expanded(call(b,c), call(b,c))). +test("doesn't expand cyclic terms", ( + X=a(X), + reif:goal_expanded(call(X), Y), + call(X) == Y +)). +test("doesn't expand cyclic call/1", ( + X=call(X), + reif:goal_expanded(X, Y), + X == Y +)). +test("doesn't expand higher order predicates", ( + X = maplist(=(1), _), + reif:goal_expanded(X, Y), + X == Y +)). + +/* +Following tests capture current results of goal expansion +TODO: Investigate if if_/3 can be further expanded, and if it will be beneficial +*/ +test("goal_expansion (=)", ( + subsumes_full_expansion(if_(1=2,a,b), ( + 1 \= 2 -> b + ; 1 == 2 -> a + ; 1 = 2, a + ; dif(1,2), b)))). + +test("goal_expansion (;)", ( + subsumes_full_expansion(if_((1=2;3=3),a,b), ( + 1 \= 2 -> if_(3=3,a,b) + ; 1 == 2 -> a + ; 1 = 2, a + ; dif(1,2), if_(3=3,a,b))))). + +test("goal_expansion (,)", ( + subsumes_full_expansion(if_((1=2,3=3),a,b), ( + 1 \= 2 -> b + ; 1 == 2 -> if_(3=3,a,b) + ; 1 = 2, if_(3=3,a,b) + ; dif(1,2), b)))). + +test("goal_expansion memberd_t", ( + subsumes_full_expansion(if_(memberd_t(f,"abcdefgh"),t,f), ( + call(memberd_t(f,"abcdefgh"),A), + ( A == true -> t + ; A == false -> f + ; nonvar(A) -> throw(error(type_error(boolean,A),_)) + ; throw(error(instantiation_error,_))))))). + +test("goal_expansion cond_t", ( + subsumes_full_expansion(if_(cond_t(a,b),t,f), ( + call(cond_t(a,b),A), + ( A == true -> t + ; A == false -> f + ; nonvar(A) -> throw(error(type_error(boolean,A),_)) + ; throw(error(instantiation_error,_))))))). + +test("set of solutions found by tpartition/4 and tfilter/3 is the same and correct", ( + random_test_vector(TestVector), + findall((N,Ts), tpartition(=(N),TestVector,Ts,_), S), + findall((N,Ts), tfilter(=(N),TestVector,Ts), S), + maplist(_+\(N,Ts)^maplist(=(N),Ts), S) +)). + +test("cut in one of the branches does not influence condition", ( + findall(X-Y, if_(X=1,!,Y=a), Solutions), + Expected = [1-Y1,X2-a], + subsumes_term(Expected, Solutions), + Solutions = Expected, + var(Y1), + var(X2), dif(X2, 1) +)). + +test("non-callable branch throws meaningful error", ( + findall(R, result_or_exception(if_(_=1, _=a, 2), R), Solutions), + Solutions == [if_(1=1,a=a,2), error(type_error(callable,2),call/1)] +)). + +test(W, loader:call(T)) :- + member(T, [ + cuts_outside(!), + cuts_outside(foo:!), + cuts_outside((a,!)), + cuts_outside((!;b(_))), + cuts_outside(((a;b(_,_);c),!,d)), + \+ cuts_outside(call((a,!))), + \+ cuts_outside(((a;b;c),\+ !,d)), + \+ cuts_outside((! -> a; b)), + \+ cuts_outside(((x,!;y) -> a; b)), + catch((cuts_outside(_),false), E0, E0 = stop(type_error(callable,_))), + catch((cuts_outside(2),false), E1, E1 == stop(type_error(callable,2))), + catch((cuts_outside(1:!),false), E2, E2 == stop(type_error(atom,1))), + catch((cuts_outside(_:!),false), E3, E3 = stop(type_error(atom,_))), + (G0 = a(G0), catch((cuts_outside(G0),false), E4, E4 = stop(type_error(acyclic_term,_)))), + (G1 = m:G1, catch((cuts_outside(G1),false), E5, E5 = stop(type_error(acyclic_term,_)))), + catch((cuts_outside((6->a)),false), E6, E6 == stop(type_error(callable,6))), + (goal_sanitized(a, X0), X0 == a), + (goal_sanitized(!, X1), X1 == call(!)), + (goal_sanitized((a,b;c,d), X2), X2 == (a,b;c,d)), + (goal_sanitized((\+ \+ a), X3), X3 == (\+ \+ a)), + % Questionable test case, see #2739 + (goal_sanitized((!,a->c;d), X4), X4 == (!,a->c;d)), + (goal_sanitized((x,a->!;d), X5), X5 == call((x,a->!;d))), + (goal_sanitized((a,b,c,!), X6), X6 == call((a,b,c,!))), + \+ goal_sanitized(0, _), + \+ goal_sanitized(_, _), + \+ goal_sanitized((a,_), _), + \+ goal_sanitized((a,b;1), _) + ]), + phrase(format_("callable cut: ~q", [T]), W). + +result_or_exception(Goal, Result) :- + catch((Goal,Result=Goal), Result, true). + +random_test_vector(TestVector) :- + random_integer(0, 1000, Length), + length(TestVector, Length), + maplist(random_integer(1,5), TestVector). + +% Expand goal until fix point is found +full_expansion(G, X) :- + user:goal_expansion(G, Gx) -> full_expansion(Gx, X); G = X. + +% X is more general than fully expanded goal G +subsumes_full_expansion(G, X) :- + full_expansion(G, Y), + subsumes_term(X, Y). + +/* +Extra predicates from the paper +*/ +duplicate(X, Xs) :- + tfilter(=(X), Xs, [_,_|_]). + +firstduplicate(X, [E|Es]) :- + if_(memberd_t(E,Es), X=E, firstduplicate(X, Es)). + +treememberd_t(_, nil, false). +treememberd_t(E, t(F,L,R), T) :- + call((E=F; treememberd_t(E,L); treememberd_t(E,R)), T). + +tree_non_member(_, nill). +tree_non_member(E, t(F,L,R)) :- + dif(E,F), + tree_non_member(E, L), + tree_non_member(E, R). diff --git a/tests/scryer/cli/src_tests/reif_tests.stderr b/tests/scryer/cli/src_tests/reif_tests.stderr new file mode 100644 index 000000000..e69de29bb diff --git a/tests/scryer/cli/src_tests/reif_tests.stdout b/tests/scryer/cli/src_tests/reif_tests.stdout new file mode 100644 index 000000000..4952cede6 --- /dev/null +++ b/tests/scryer/cli/src_tests/reif_tests.stdout @@ -0,0 +1 @@ +All tests passed \ No newline at end of file diff --git a/tests/scryer/cli/src_tests/reif_tests.toml b/tests/scryer/cli/src_tests/reif_tests.toml new file mode 100644 index 000000000..fd5f6075e --- /dev/null +++ b/tests/scryer/cli/src_tests/reif_tests.toml @@ -0,0 +1,11 @@ +args = [ + "-f", + "--no-add-history", + "-g", "use_module(library(reif_tests))", + "-g", "use_module(library(reif))", + "-g", "use_module(library(dif))", + "-g", "use_module(library(lambda))", + "-g", "use_module(library(lists))", + "-g", "reif_tests:main_quiet(reif_tests)", + "src/tests/reif.pl" +]