interactive queries
Fergus Henderson
fjh at cs.mu.OZ.AU
Fri Feb 26 08:49:18 AEDT 1999
Someone mailed me asking about how to convert a read_term into
a pred again. I got tired of giving the long-winded answer yet
again so I wrote the following code. Here it is. Maybe someone
will find this useful.
This would need a bit of work to clean it up, but the following
ought to be sufficient to demonstrate the proof of concept.
I tested it on a couple of simple queries on dec-alpha-osf3.2
and it seemed to work OK.
% Example interactive query program using dynamic linking.
% This module reads in a query, writes out Mercury code for it to `query.m',
% invokes the Mercury compiler mmc to compile `query.m' to `libquery.so',
% loads in the object code for the module `query'
% from the file `libquery.so', looks up the address of the
% procedure query/2 in that module, and then calls that procedure.
% This source file is hereby placed in the public domain. -fjh (the author).
:- module interactive.
:- interface.
:- import_module io.
:- pred main(state::di, state::uo) is det.
:- implementation.
:- import_module std_util, bool, string, list, term, varset, term_io.
:- import_module dl, name_mangle.
:- import_module exception.
:- type prog ---> prog(imports, term, varset).
:- type imports == list(string).
:- type options == string.
main --> query([], "").
:- pred query(imports::in, options::in, state::di, state::uo) is det.
query(Imports, Options) -->
print(Imports),
print(" ?- "),
term_io__read_term(Result),
( { Result = eof },
io__write_string("\nBye.\n")
; { Result = error(Msg, _Line) },
io__write_string(Msg), nl,
main
; { Result = term(VarSet, Term) },
% io__write_string("Read term: "),
% term_io__write_term(Term, VarSet),
% io__write_string("\n"),
(if { term_to_list(Term, ModuleList) } then
{ list__append(Imports, ModuleList, NewImports) },
query(NewImports, Options)
else if { Term = term__functor(term__atom("options"),
[term__functor(term__string(NewOptions),
[], _)], _) } then
print("Compilation options: "), print(Options), nl,
query(Imports, NewOptions)
else
run_query(Options, prog(Imports, Term, VarSet)),
query(Imports, Options)
)
).
:- pred term_to_list(term, list(string)).
:- mode term_to_list(in, out) is semidet.
term_to_list(term__functor(term__atom("[]"), [], _), []).
term_to_list(term__functor(term__atom("."),
[term__functor(term__atom(Module), [], _C1), Rest], _C2),
[Module | Modules]) :-
term_to_list(Rest, Modules).
:- pred run_query(options, prog, io__state, io__state).
:- mode run_query(in, in, di, uo) is det.
run_query(Options, Program) -->
{ SourceFile = "query.m" },
write_prog_to_file(Program, SourceFile),
compile_file(Options, Succeeded),
(if { Succeeded = yes } then
dynamically_load_and_run
else
{ true }
).
%-----------------------------------------------------------------------------%
%
% print the program to a file
%
:- pred write_prog_to_file(prog, string, io__state, io__state).
:- mode write_prog_to_file(in, in, di, uo).
write_prog_to_file(Program, FileName) -->
open_output_file(FileName, Stream),
io__set_output_stream(Stream, OldStream),
write_prog_to_stream(Program),
io__set_output_stream(OldStream, _),
io__close_output(Stream).
:- pred open_output_file(string::in, io__output_stream::out,
io__state::di, io__state::uo) is det.
open_output_file(File, Stream) -->
io__open_output(File, Result),
( { Result = ok(Stream0) },
{ Stream = Stream0 }
; { Result = error(Error) },
io__progname("interactive", Progname),
{ io__error_message(Error, ErrorMessage) },
{ string__append_list([
Progname, ": ",
"error opening file `", File, "' for input:\n\t",
ErrorMessage, "\n"],
Message) },
io__write_string(Message),
{ throw(Message) }
).
:- pred write_prog_to_stream(prog::in, io__state::di, io__state::uo) is det.
write_prog_to_stream(prog(Imports, Term, VarSet)) -->
io__write_string("
:- module query.
:- interface.
:- import_module io.
:- pred query(io__state::di, io__state::uo) is cc_multi.
:- implementation.
:- import_module
"),
io__write_list(["io" | Imports], ", ", term_io__quote_atom),
io__write_string(".
:- pragma source_file(""<stdin>"").
query -->
(if
{
#"),
io__get_line_number(LineNum),
io__write_int(LineNum),
io__nl,
term_io__write_term(VarSet, Term),
print("
}
then
"),
{ term__vars(Term, Vars0) },
{ list__remove_dups(Vars0, Vars) },
list__foldl(write_one_var(VarSet), Vars),
print("
io__write_string(""Yes.\\n"")
else
io__write_string(""No solution.\\n"")
).
").
:- pred write_one_var(varset::in, var::in,
io__state::di, io__state::uo) is det.
write_one_var(VarSet, Var) -->
io__write_string("io__write_string("""),
term_io__write_variable(Var, VarSet),
io__write_string(" = ""), write("),
term_io__write_variable(Var, VarSet),
print("), nl, ").
%-----------------------------------------------------------------------------%
%
% invoke the Mercury compile to compile the file to a shared object
%
:- pred compile_file(options, bool, state, state).
:- mode compile_file(in, out, di, uo) is det.
compile_file(Options, Succeeded) -->
{ string__append_list([
"mmc --pic-reg --no-warn-simple-code ",
"--no-warn-det-decls-too-lax -c ", Options,
" query.m"], Command) },
invoke_system_command(Command, Succeeded0),
( { Succeeded0 = yes } ->
{ string__append_list([
"ml --make-shared-lib -o libquery.so ", Options,
" query.o"], Command2) },
invoke_system_command(Command2, Succeeded)
;
{ Succeeded = no }
).
:- func verbose = bool.
verbose = yes.
:- pred invoke_system_command(string, bool, state, state).
:- mode invoke_system_command(in, out, di, uo).
invoke_system_command(Command, Succeeded) -->
(if { verbose = yes } then
io__write_string("% Invoking system command `"),
io__write_string(Command),
io__write_string("'...\n"),
io__flush_output
else
[]
),
io__call_system(Command, Result),
(if { Result = ok(0) } then
( if { verbose = yes } then print("% done.\n") else [] ),
{ Succeeded = yes }
else if { Result = ok(_) } then
print("Compilation error(s) occurred.\n"),
{ Succeeded = no }
else
print("Error: unable to invoke the compiler.\n"),
{ Succeeded = no }
).
%-----------------------------------------------------------------------------%
%
% dynamically load the shared object and execute the query
%
:- pred dynamically_load_and_run(io__state::di, io__state::uo) is det.
dynamically_load_and_run -->
%
% Load in the object code for the module `query' from
% the file `libquery.so'.
%
dl__open("./libquery.so", lazy, local, MaybeHandle),
(
{ MaybeHandle = error(Msg) },
print("dlopen failed: "), print(Msg), nl
;
{ MaybeHandle = ok(Handle) },
%
% Look up the address of the first mode (mode number 0)
% of the predicate query/2 in the module query.
%
{ QueryProc = mercury_proc(predicate, unqualified("query"),
"query", 2, 0) },
dl__mercury_sym(Handle, QueryProc, MaybeQuery),
(
{ MaybeQuery = error(Msg) },
print("dlsym failed: "), print(Msg), nl
;
{ MaybeQuery = ok(QueryPred0) },
%
% Cast the higher-order term that we obtained
% to the correct higher-order inst.
%
{ QueryPred = inst_cast(QueryPred0) },
%
% Call the procedure whose address
% we just obtained.
%
QueryPred
),
%
% unload the object code in the libquery.so file
%
dl__close(Handle, Result),
(
{ Result = error(CloseMsg) },
print("dlclose failed: "), print(CloseMsg), nl
;
{ Result = ok }
)
).
%
% dl__mercury_sym returns a higher-order term with inst `ground'.
% We need to cast it to the right higher-order inst, namely
% `pred(di, uo) is det' before we can actually call it.
% The function inst_cast/1 defined below does that.
%
:- type io_pred == pred(io__state, io__state).
:- inst io_pred == (pred(di, uo) is det).
:- func inst_cast(io_pred) = io_pred.
:- mode inst_cast(in) = out(io_pred) is det.
:- pragma c_code(inst_cast(X::in) = (Y::out(io_pred)),
[will_not_call_mercury, thread_safe], "Y = X").
%-----------------------------------------------------------------------------%
--
Fergus Henderson <fjh at cs.mu.oz.au> | "Binaries may die
WWW: <http://www.cs.mu.oz.au/~fjh> | but source code lives forever"
PGP: finger fjh at 128.250.37.3 | -- leaked Microsoft memo.
More information about the developers
mailing list