mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-18 19:03:45 +00:00
extras/curs/curs.m:
extras/curs/curs.panel.m:
Make panel a separate submodule of curs, not a nested submodule.
extras/base64/base64.m:
extras/curses/mcurses.basics.m:
extras/curses/mcurses.m:
extras/curses/mcurses.misc.m:
extras/curses/mcurses.user.m:
extras/gator/evolve.m:
extras/gator/genotype.m:
extras/gator/phenotype.m:
extras/gator/tausworthe3.m:
extras/monte/dots.m:
extras/monte/geom.m:
extras/monte/hg.m:
extras/monte/monte.m:
extras/monte/rnd.m:
extras/moose/grammar.m:
extras/moose/moose.m:
extras/mopenssl/mopenssl.m:
extras/net/echo.m:
extras/net/errno.m:
extras/net/getaddrinfo.m:
extras/net/net.m:
extras/net/netdb.m:
extras/net/sockets.m:
extras/net/streams.m:
extras/net/tcp.m:
extras/net/test_lookups.m:
extras/net/types.m:
extras/odbc/odbc.m:
extras/odbc/odbc_test.m:
extras/references/README:
extras/references/reference.m:
extras/references/scoped_update.m:
extras/solver_types/library/any.m:
extras/solver_types/library/any_array.m:
extras/solver_types/library/any_assoc_list.m:
extras/solver_types/library/any_list.m:
extras/solver_types/library/any_map.m:
extras/solver_types/library/any_tree234.m:
extras/solver_types/library/any_util.m:
extras/trail/trail.m:
extras/trailed_update/samples/interpreter.m:
extras/trailed_update/samples/vqueens.m:
extras/trailed_update/tests/var_test.m:
extras/trailed_update/tr_array.m:
extras/trailed_update/tr_store.m:
extras/trailed_update/trailed_update.m:
extras/trailed_update/unsafe.m:
extras/trailed_update/var.m:
Bring programming style up to date.
612 lines
21 KiB
Mathematica
612 lines
21 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: interpreter.m.
|
|
% Main author: fjh.
|
|
%
|
|
% This is an interpreter for definite logic programs
|
|
% (i.e. pure Prolog with no negation or if-then-else.)
|
|
%
|
|
% This is just intended as a demonstration of the use of the
|
|
% library module tr_store.m.
|
|
%
|
|
% There are many extensions/improvements that could be made;
|
|
% they are left as an exercise for the reader.
|
|
%
|
|
% This source file is hereby placed in the public domain. -fjh (the author).
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module interpreter.
|
|
:- interface.
|
|
:- import_module io.
|
|
|
|
:- pred main(io::di, io::uo) is cc_multi.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module tr_store.
|
|
:- import_module unsafe.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module mercury_term_parser.
|
|
:- import_module multi_map.
|
|
:- import_module pair.
|
|
:- import_module require.
|
|
:- import_module solutions.
|
|
:- import_module store.
|
|
:- import_module string.
|
|
:- import_module term.
|
|
:- import_module term_context.
|
|
:- import_module term_io.
|
|
:- import_module varset.
|
|
|
|
:- pragma require_feature_set([trailing]).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
main(!IO) :-
|
|
io.write_string("Pure Prolog Interpreter.\n\n", !IO),
|
|
io.command_line_arguments(Args, !IO),
|
|
(
|
|
Args = [],
|
|
io.stderr_stream(StdErr, !IO),
|
|
io.write_string(StdErr, "Usage: interpreter filename ...\n", !IO),
|
|
io.set_exit_status(1, !IO)
|
|
;
|
|
Args = [_ | _],
|
|
database_init(Database0),
|
|
consult_list(Args, Database0, Database, !IO),
|
|
main_loop(Database, !IO)
|
|
).
|
|
|
|
:- pred main_loop(database::in, io::di, io::uo) is cc_multi.
|
|
|
|
main_loop(Database, !IO) :-
|
|
io.write_string("?- ", !IO),
|
|
read_term(ReadTerm, !IO),
|
|
main_loop_2(ReadTerm, Database, !IO).
|
|
|
|
:- pred main_loop_2(read_term::in, database::in,
|
|
io::di, io::uo) is cc_multi.
|
|
|
|
main_loop_2(eof, _Database, !IO).
|
|
main_loop_2(error(ErrorMessage, LineNumber), Database, !IO) :-
|
|
io.format("Error reading term at line %d of standard input: %s\n",
|
|
[i(LineNumber), s(ErrorMessage)], !IO),
|
|
main_loop(Database, !IO).
|
|
main_loop_2(term(VarSet, Goal), Database, !IO) :-
|
|
%%% It would be a good idea to add some special commands
|
|
%%% with side-effects (such as `consult' and `listing');
|
|
%%% these could be identified and processed here.
|
|
store.init(Store0),
|
|
map.init(VarMap0),
|
|
term_to_my_term(Goal, MyGoal, VarMap0, VarMap, Store0, Store1),
|
|
print_solutions(VarSet, VarMap, MyGoal, Store1, Database, !IO),
|
|
main_loop(Database, !IO).
|
|
|
|
:- pred print_solutions(varset::in, map(var, my_var(S))::in, my_term(S)::in,
|
|
store(S)::mdi, database::in, io::di, io::uo) is cc_multi.
|
|
|
|
% The call to unsafe_promise_unique here is needed because without it,
|
|
% the following code gets a (spurious) unique mode error,
|
|
% because the compiler thinks that `Store0' has inst `ground'
|
|
% rather than `mostly_unique' when it is passed as a curried
|
|
% argument of a higher-order term. The compiler doesn't know
|
|
% that unsorted_aggregate will only call its higher-order argument
|
|
% once per forward execution.
|
|
%
|
|
% It might be nicer to use do_while rather than unsorted_aggregate,
|
|
% so that we can prompt the user after each solution to see if they
|
|
% want to see the next solution.
|
|
%
|
|
print_solutions(VarSet, VarMap, MyGoal, Store0, Database, !IO) :-
|
|
SolvePred =
|
|
( pred(Store::muo) is nondet :-
|
|
solve(Database, MyGoal, unsafe_promise_unique(Store0), Store)
|
|
),
|
|
unsorted_aggregate(SolvePred, write_solution(VarSet, VarMap, MyGoal), !IO),
|
|
io.write_string("No (more) solutions.\n", !IO).
|
|
|
|
:- pred write_solution(varset::in, map(var, my_var(S))::in,
|
|
my_term(S)::in, store(S)::mdi, io::di, io::uo) is det.
|
|
|
|
write_solution(VarSet0, VarToMyVarMap, MyGoal, Store0, !IO) :-
|
|
map.keys(VarToMyVarMap, Vars),
|
|
map.values(VarToMyVarMap, MyVars),
|
|
assoc_list.from_corresponding_lists(MyVars, Vars, VarMap0),
|
|
my_term_to_term(MyGoal, Goal, VarSet0, VarSet, VarMap0, _VarMap,
|
|
Store0, _Store),
|
|
term_io.write_term_nl(VarSet, Goal, !IO).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred consult_list(list(string)::in, database::in, database::out,
|
|
io::di, io::uo) is det.
|
|
|
|
consult_list([], !Database, !IO).
|
|
consult_list([File | Files], !Database, !IO) :-
|
|
consult(File, !Database, !IO),
|
|
consult_list(Files, !Database, !IO).
|
|
|
|
:- pred consult(string::in, database::in, database::out,
|
|
io::di, io::uo) is det.
|
|
|
|
consult(File, !Database, !IO) :-
|
|
io.format("Consulting file `%s'...\n", [s(File)], !IO),
|
|
io.open_input(File, Result, !IO),
|
|
(
|
|
Result = ok(InStream),
|
|
consult_until_eof(InStream, !Database, !IO),
|
|
io.close_input(InStream, !IO)
|
|
;
|
|
Result = error(_),
|
|
io.format("Error opening file `%s' for input.\n", [s(File)], !IO)
|
|
).
|
|
|
|
:- pred consult_until_eof(io.text_input_stream::in,
|
|
database::in, database::out, io::di, io::uo) is det.
|
|
|
|
consult_until_eof(InStream, !Database, !IO) :-
|
|
read_term(InStream, ReadTerm, !IO),
|
|
consult_until_eof_loop(InStream, ReadTerm, !Database, !IO).
|
|
|
|
:- pred consult_until_eof_loop(io.text_input_stream::in, read_term::in,
|
|
database::in, database::out, io::di, io::uo) is det.
|
|
|
|
consult_until_eof_loop(_, eof, !Database, !IO).
|
|
consult_until_eof_loop(InStream, error(ErrorMessage, LineNumber),
|
|
!Database, !IO) :-
|
|
io.format("Error reading term at line %d of standard input: %s\n",
|
|
[i(LineNumber), s(ErrorMessage)], !IO),
|
|
consult_until_eof(InStream, !Database, !IO).
|
|
consult_until_eof_loop(InStream, term(VarSet, Term), !Database, !IO) :-
|
|
database_assert_clause(VarSet, Term, !Database),
|
|
consult_until_eof(InStream, !Database, !IO).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Here's how we represent terms.
|
|
% We don't use the Mercury standard library type `term', because
|
|
% that isn't efficient enough; we want variables to be represented
|
|
% as mutable variables using the store.mutvar type, so that we
|
|
% can implement variable binding as backtrackable destructive update,
|
|
% using the tr_store module.
|
|
|
|
:- type my_var(S) == generic_mutvar(my_term(S), S).
|
|
|
|
:- type my_term(S)
|
|
---> var(my_var(S))
|
|
; free
|
|
; functor(const, list(my_term(S))).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Convert from the standard Mercury `term' representation to
|
|
% our `my_term' representation.
|
|
|
|
:- pred term_to_my_term(term::in, my_term(S)::out,
|
|
store(S)::mdi, store(S)::muo) is det.
|
|
|
|
term_to_my_term(Term, MyTerm, !S) :-
|
|
map.init(VarMap0),
|
|
term_to_my_term(Term, MyTerm, VarMap0, _VarMap, !S).
|
|
|
|
:- pred term_to_my_term_list(list(term)::in, list(my_term(S))::out,
|
|
store(S)::mdi, store(S)::muo) is det.
|
|
|
|
term_to_my_term_list(Terms, MyTerm, !S) :-
|
|
map.init(VarMap0),
|
|
term_to_my_term_list(Terms, MyTerm, VarMap0, _VarMap, !S).
|
|
|
|
:- pred term_to_my_term(term::in, my_term(S)::out,
|
|
map(var, my_var(S))::in, map(var, my_var(S))::out,
|
|
store(S)::mdi, store(S)::muo) is det.
|
|
|
|
term_to_my_term(variable(Var, _), var(Ref), !VarMap, !S) :-
|
|
( if map.search(!.VarMap, Var, Ref0) then
|
|
Ref = Ref0
|
|
else
|
|
tr_store.new_mutvar(free, Ref, !S),
|
|
map.det_insert(Var, Ref, !VarMap)
|
|
).
|
|
term_to_my_term(functor(Functor, Args0, _Context), functor(Functor, Args),
|
|
!VarMap, !S) :-
|
|
term_to_my_term_list(Args0, Args, !VarMap, !S).
|
|
|
|
:- pred term_to_my_term_list(list(term)::in, list(my_term(S))::out,
|
|
map(var, my_var(S))::in, map(var, my_var(S))::out,
|
|
store(S)::mdi, store(S)::muo) is det.
|
|
|
|
term_to_my_term_list([], [], !VarMap, !S).
|
|
term_to_my_term_list([Term0 | Terms0], [Term | Terms], !VarMap, !S) :-
|
|
term_to_my_term(Term0, Term, !VarMap, !S),
|
|
term_to_my_term_list(Terms0, Terms, !VarMap, !S).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Convert from our `my_term' representation to
|
|
% the standard Mercury `term' representation.
|
|
|
|
:- pred my_term_to_term(my_term(S)::in, term::out,
|
|
store(S)::mdi, store(S)::muo) is det.
|
|
|
|
my_term_to_term(MyTerm, Term, !S) :-
|
|
varset.init(VarSet0),
|
|
VarMap0 = [],
|
|
my_term_to_term(MyTerm, Term, VarSet0, _VarSet, VarMap0, _VarMap, !S).
|
|
|
|
:- pred my_term_to_term_list(list(my_term(S))::in, list(term)::out,
|
|
store(S)::mdi, store(S)::muo) is det.
|
|
|
|
my_term_to_term_list(MyTerms, Terms, !S) :-
|
|
varset.init(VarSet0),
|
|
VarMap0 = [],
|
|
my_term_to_term_list(MyTerms, Terms, VarSet0, _VarSet,
|
|
VarMap0, _VarMap, !S).
|
|
|
|
% Note that we need to use an assoc_list here rather than a map,
|
|
% because store mutvars can only be tested for equality, not compared
|
|
% (this in turn is because in implementations which use copying GC,
|
|
% the relative addresses of different mutvars might change after
|
|
% a garbage collection).
|
|
|
|
:- pred my_term_to_term(my_term(S)::in, term::out, varset::in, varset::out,
|
|
assoc_list(my_var(S), var)::in, assoc_list(my_var(S), var)::out,
|
|
store(S)::mdi, store(S)::muo) is det.
|
|
|
|
my_term_to_term(var(MyVar), variable(Var, Context), !VarSet, !VarMap, !S) :-
|
|
Context = dummy_context,
|
|
% Check whether MyVar is in the VarMap;
|
|
% if so, use its corresponding Var,
|
|
% otherwise, create a fresh Var and insert it into the VarMap.
|
|
( if assoc_list.search(!.VarMap, MyVar, Var0) then
|
|
Var = Var0
|
|
else
|
|
varset.new_var(Var, !VarSet),
|
|
!:VarMap = [MyVar - Var | !.VarMap]
|
|
),
|
|
% Check whether MyVar is bound;
|
|
% if so, insert its binding into the VarSet.
|
|
tr_store.get_mutvar(MyVar, MyValue, !S),
|
|
( if MyValue \= free then
|
|
my_term_to_term(MyValue, Value, !VarSet, !VarMap, !S),
|
|
varset.bind_var(Var, Value, !VarSet)
|
|
else
|
|
true
|
|
).
|
|
my_term_to_term(free, variable(Var, Context), !VarSet, !VarMap, !S) :-
|
|
Context = dummy_context,
|
|
varset.new_var(Var, !VarSet),
|
|
error("my_term_to_term: unexpected free var").
|
|
my_term_to_term(functor(Functor, Args0), functor(Functor, Args, Context),
|
|
!VarSet, !VarMap, !S) :-
|
|
Context = dummy_context,
|
|
my_term_to_term_list(Args0, Args, !VarSet, !VarMap, !S).
|
|
|
|
:- pred my_term_to_term_list(list(my_term(S))::in, list(term)::out,
|
|
varset::in, varset::out,
|
|
assoc_list(my_var(S), var)::in, assoc_list(my_var(S), var)::out,
|
|
store(S)::mdi, store(S)::muo) is det.
|
|
|
|
my_term_to_term_list([], [], !VarSet, !VarMap, !S).
|
|
my_term_to_term_list([Term0 | Terms0], [Term | Terms], !VarSet, !VarMap, !S) :-
|
|
my_term_to_term(Term0, Term, !VarSet, !VarMap, !S),
|
|
my_term_to_term_list(Terms0, Terms, !VarSet, !VarMap, !S).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Solve takes a database of rules and facts, a goal to be solved, and a varset
|
|
% (which includes a supply of fresh vars, a substitution, and names for [some
|
|
% subset of] the variables). It updates the varset, producing a new
|
|
% substitution and perhaps introducing some new vars, and returns the result.
|
|
|
|
% Goals are stored just as terms.
|
|
% (It might be more efficient to parse them before storing them in the
|
|
% database. Currently we do this parsing work every time we interpret a
|
|
% clause.)
|
|
|
|
:- pred solve(database::in, my_term(S)::in,
|
|
store(S)::mdi, store(S)::muo) is nondet.
|
|
|
|
solve(_Database, functor(atom("true"), []), !S).
|
|
|
|
solve(Database, functor(atom(","), [A, B]), !S) :-
|
|
solve(Database, A, !S),
|
|
solve(Database, B, !S).
|
|
|
|
solve(Database, functor(atom(";"), [A, B]), !S) :-
|
|
(
|
|
solve(Database, A, !S)
|
|
;
|
|
solve(Database, B, !S)
|
|
).
|
|
|
|
solve(_Database, functor(atom("="), [A, B]), !S) :-
|
|
unify(A, B, !S).
|
|
|
|
solve(Database, Goal, !S) :-
|
|
database_lookup_clause(Database, Goal, _VarSet, Head0, Body0),
|
|
term_to_my_term_list([Head0, Body0], [Head, Body], !S),
|
|
unify(Goal, Head, !S),
|
|
solve(Database, Body, !S).
|
|
|
|
% solve(Database, var(Var)) -->
|
|
% get_mutvar(Var, Value),
|
|
% solve(Database, Value).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred unify(my_term(S)::in, my_term(S)::in, store(S)::mdi, store(S)::muo)
|
|
is semidet.
|
|
|
|
unify(var(X), var(Y), !S) :-
|
|
tr_store.get_mutvar(X, BindingOfX, !S),
|
|
tr_store.get_mutvar(Y, BindingOfY, !S),
|
|
( if BindingOfX \= free then
|
|
( if BindingOfY \= free then
|
|
% Both X and Y already have bindings - just
|
|
% unify the terms they are bound to.
|
|
unify(BindingOfX, BindingOfY)
|
|
else
|
|
% Y is a variable which hasn't been bound yet.
|
|
deref(BindingOfX, SubstBindingOfX, !S),
|
|
( if SubstBindingOfX = var(Y) then
|
|
true
|
|
else
|
|
not_occurs(SubstBindingOfX, Y, !S),
|
|
tr_store.set_mutvar(Y, SubstBindingOfX, !S)
|
|
)
|
|
)
|
|
else
|
|
( if BindingOfY \= free then
|
|
% X is a variable which hasn't been bound yet.
|
|
deref(BindingOfY, SubstBindingOfY, !S),
|
|
( if SubstBindingOfY = var(X) then
|
|
true
|
|
else
|
|
not_occurs(SubstBindingOfY, X, !S),
|
|
tr_store.set_mutvar(X, SubstBindingOfY, !S)
|
|
)
|
|
else
|
|
% Both X and Y are unbound variables -
|
|
% bind one to the other.
|
|
( if X = Y then
|
|
true
|
|
else
|
|
tr_store.set_mutvar(X, var(Y), !S)
|
|
)
|
|
)
|
|
).
|
|
|
|
unify(var(X), functor(F, As), !S) :-
|
|
tr_store.get_mutvar(X, BindingOfX, !S),
|
|
( if BindingOfX \= free then
|
|
unify(BindingOfX, functor(F, As), !S)
|
|
else
|
|
not_occurs_list(As, X, !S),
|
|
tr_store.set_mutvar(X, functor(F, As), !S)
|
|
).
|
|
|
|
unify(functor(F, As), var(X), !S) :-
|
|
tr_store.get_mutvar(X, BindingOfX, !S),
|
|
( if BindingOfX \= free then
|
|
unify(functor(F, As), BindingOfX, !S)
|
|
else
|
|
not_occurs_list(As, X, !S),
|
|
tr_store.set_mutvar(X, functor(F, As), !S)
|
|
).
|
|
|
|
unify(functor(F, AsX), functor(F, AsY), !S) :-
|
|
unify_list(AsX, AsY, !S).
|
|
|
|
:- pred unify_list(list(my_term(S))::in, list(my_term(S))::in,
|
|
store(S)::mdi, store(S)::muo) is semidet.
|
|
|
|
unify_list([], [], !S).
|
|
unify_list([X | Xs], [Y | Ys], !S) :-
|
|
unify(X, Y, !S),
|
|
unify_list(Xs, Ys, !S).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% not_occurs(Term, Var, Store0, Store) fails if Term contains Var,
|
|
% perhaps indirectly via the substitution in Store0.
|
|
% (The variable must not be mapped by the substitution.)
|
|
|
|
:- pred not_occurs(my_term(S)::in, my_var(S)::in,
|
|
store(S)::mdi, store(S)::muo) is semidet.
|
|
|
|
not_occurs(var(X), Y, !S) :-
|
|
X \= Y,
|
|
tr_store.get_mutvar(X, BindingOfX, !S),
|
|
( if BindingOfX = free then
|
|
true
|
|
else
|
|
not_occurs(BindingOfX, Y, !S)
|
|
).
|
|
not_occurs(functor(_F, As), Y, !S) :-
|
|
not_occurs_list(As, Y, !S).
|
|
|
|
:- pred not_occurs_list(list(my_term(S))::in, my_var(S)::in,
|
|
store(S)::mdi, store(S)::muo) is semidet.
|
|
|
|
not_occurs_list([], _, !S).
|
|
not_occurs_list([Term | Terms], Y, !S) :-
|
|
not_occurs(Term, Y, !S),
|
|
not_occurs_list(Terms, Y, !S).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% deref(Term0, Term, !Store):
|
|
% Recursively apply substitution to Term0 until no more substitutions can
|
|
% be applied, and then return the result in Term.
|
|
%
|
|
:- pred deref(my_term(S)::in, my_term(S)::out, store(S)::mdi, store(S)::muo)
|
|
is det.
|
|
|
|
deref(free, _, _, _) :-
|
|
error("interpreter.deref: unexpected occurrence of `free'").
|
|
deref(var(Var), Term, !S) :-
|
|
tr_store.get_mutvar(Var, Replacement, !S),
|
|
( if Replacement \= free then
|
|
% Recursively apply the substitution to the replacement.
|
|
deref(Replacement, Term, !S)
|
|
else
|
|
Term = var(Var)
|
|
).
|
|
deref(functor(Name, Args0), functor(Name, Args), !S) :-
|
|
deref_list(Args0, Args, !S).
|
|
|
|
:- pred deref_list(list(my_term(S))::in, list(my_term(S))::out,
|
|
store(S)::mdi, store(S)::muo) is det.
|
|
|
|
deref_list([], [], !S).
|
|
deref_list([Term0 | Terms0], [Term | Terms], !S) :-
|
|
deref(Term0, Term, !S),
|
|
deref_list(Terms0, Terms, !S).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% The database of clauses is indexed by predicate name/arity,
|
|
% and for each predicate the clauses are indexed according to the
|
|
% name/arity of their first argument.
|
|
|
|
:- type database
|
|
---> database(
|
|
list(clause),
|
|
% Clauses with variable as head.
|
|
|
|
map(string/int, db_pred)
|
|
% Preds, indexed on name/arity.
|
|
).
|
|
|
|
:- type db_pred
|
|
---> db_pred(
|
|
list(clause),
|
|
% Unindexed clauses
|
|
% (ones with var as first arg, or with no args).
|
|
|
|
multi_map(string/int, clause)
|
|
% Clauses, indexed on the name/arity of first arg.
|
|
).
|
|
|
|
:- type Name/Arity
|
|
---> Name/Arity.
|
|
|
|
:- type clause
|
|
---> clause(varset, term, term). % varset, head, body
|
|
|
|
:- pred database_init(database::out) is det.
|
|
|
|
database_init(database([], Preds)) :-
|
|
map.init(Preds).
|
|
|
|
:- pred database_assert_clause(varset::in, term::in,
|
|
database::in, database::out) is det.
|
|
|
|
database_assert_clause(VarSet, Term, !Database) :-
|
|
% Add `:- true' if clause not already in the form `H :- B'.
|
|
( if Term = functor(atom(":-"), [H, B], _) then
|
|
Head = H,
|
|
Body = B
|
|
else
|
|
Head = Term,
|
|
Context = dummy_context,
|
|
Body = functor(atom("true"), [], Context)
|
|
),
|
|
Clause = clause(VarSet, Head, Body),
|
|
|
|
% Insert clause into database.
|
|
!.Database = database(UnindexedClauses, Preds0),
|
|
( if Head = functor(atom(PredName), PredArgs, _) then
|
|
% We can do predicate name/arity indexing.
|
|
list.length(PredArgs, PredArity),
|
|
PredId = PredName / PredArity,
|
|
( if
|
|
PredArgs = [FirstArg | _],
|
|
FirstArg = functor(atom(FirstArgName), FirstArgArgs, _)
|
|
then
|
|
% We can do first-argument name/arity indexing.
|
|
list.length(FirstArgArgs, FirstArgArity),
|
|
FirstArgId = FirstArgName / FirstArgArity,
|
|
( if map.search(Preds0, PredId, Pred0) then
|
|
Pred0 = db_pred(PredUnindexedClauses,
|
|
PredIndexedClauses0),
|
|
multi_map.set(FirstArgId, Clause,
|
|
PredIndexedClauses0, PredIndexedClauses),
|
|
Pred = db_pred(PredUnindexedClauses,
|
|
PredIndexedClauses),
|
|
map.det_update(PredId, Pred, Preds0, Preds)
|
|
else
|
|
multi_map.init(PredIndexedClauses0),
|
|
multi_map.set(FirstArgId, Clause,
|
|
PredIndexedClauses0, PredIndexedClauses),
|
|
Pred = db_pred([], PredIndexedClauses),
|
|
map.det_insert(PredId, Pred, Preds0, Preds)
|
|
)
|
|
else
|
|
% We can't do first-argument indexing -- just insert
|
|
% into the unindexed clauses.
|
|
( if map.search(Preds0, PredId, Pred0) then
|
|
Pred0 = db_pred(PredUnindexedClauses, PredIndexedClauses),
|
|
Pred = db_pred([Clause | PredUnindexedClauses],
|
|
PredIndexedClauses),
|
|
map.det_update(PredId, Pred, Preds0, Preds)
|
|
else
|
|
multi_map.init(PredIndexedClauses),
|
|
Pred = db_pred([Clause], PredIndexedClauses),
|
|
map.det_insert(PredId, Pred, Preds0, Preds)
|
|
)
|
|
),
|
|
!:Database = database(UnindexedClauses, Preds)
|
|
else
|
|
!:Database = database([Clause | UnindexedClauses], Preds0)
|
|
).
|
|
|
|
:- pred database_lookup_clause(database::in, my_term(_)::in, varset::out,
|
|
term::out, term::out) is nondet.
|
|
|
|
database_lookup_clause(Database, Goal, VarSet, Head, Body) :-
|
|
database_lookup_raw_clause(Database, Goal, Clause),
|
|
Clause = clause(VarSet, Head, Body).
|
|
|
|
:- pred database_lookup_raw_clause(database::in, my_term(_)::in, clause::out)
|
|
is nondet.
|
|
|
|
database_lookup_raw_clause(database(Clauses, _Preds), _Goal, Clause) :-
|
|
list.member(Clause, Clauses).
|
|
database_lookup_raw_clause(database(_Clauses, Preds), Goal, Clause) :-
|
|
Goal = functor(atom(PredName), PredArgs),
|
|
list.length(PredArgs, PredArity),
|
|
map.search(Preds, PredName/PredArity, PredClauses),
|
|
database_lookup_pred_clause(PredClauses, PredArgs, Clause).
|
|
|
|
:- pred database_lookup_pred_clause(db_pred::in, list(my_term(_))::in,
|
|
clause::out) is nondet.
|
|
|
|
database_lookup_pred_clause(db_pred(Clauses, _IndexedClauses), _, Clause) :-
|
|
list.member(Clause, Clauses).
|
|
database_lookup_pred_clause(db_pred(_, IndexedClauses), PredArgs, Clause) :-
|
|
PredArgs = [FirstArg | _],
|
|
(
|
|
FirstArg = var(_),
|
|
multi_map.member(IndexedClauses, _, Clause)
|
|
;
|
|
FirstArg = functor(atom(FirstArgName), FirstArgArgs),
|
|
list.length(FirstArgArgs, FirstArgArity),
|
|
multi_map.nondet_lookup(IndexedClauses,
|
|
FirstArgName/FirstArgArity, Clause)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module interpreter.
|
|
%---------------------------------------------------------------------------%
|
|
|