[m-dev.] for review: add mdb support for interactive queries

Fergus Henderson fjh at cs.mu.OZ.AU
Thu Mar 4 11:29:09 AEDT 1999


On 04-Mar-1999, Peter Ross <petdr at cs.mu.OZ.AU> wrote:
> On 04-Mar-1999, Fergus Henderson <fjh at cs.mu.OZ.AU> wrote:
> >  
> > cvs diff: browser/dl.m is a new entry, no comparison available
> > cvs diff: browser/interactive_query.m is a new entry, no comparison available
> > cvs diff: browser/name_mangle.m is a new entry, no comparison available
> 
> These files are missing.

dl.m and name_mangle.m are substantially the same as the copies
currently in extras/dynamic_linking; I did include a diff of
my changes to dl.m.

I did however omit browser/interactive_query.m; that was of course
a mistake.  It's included below.

> I have the following line inside ~/.cvsrc to make sure that new files are
> included in diffs and other things.
> 
> diff -u -N -b
> 
> I believe it is the -N which includes new files in diffs.

That's correct.  I don't have `-N' in my `/.cvsrc' because some versions
of cvs have a bug which causes it to dump core (leaving a cvs lock behind)
whenever you do `cvs diff -N' and one of the files being diffed
has been removed.

--------------------

% Module to invoke interactive queries 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',
% dynamically 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.

:- module interactive_query.
:- interface.
:- import_module io, list.

:- pred query(query_type::in, imports::in, options::in,
		io__input_stream::in, io__output_stream::in,
		state::di, state::uo) is det.

:- type query_type ---> normal_query ; cc_query ; io_query.
:- type imports == list(string).
:- type options == string.

:- implementation.
:- import_module std_util, bool, string, term, varset, term_io.
:- import_module dl, name_mangle.

:- pragma export(query(in, in, in, in, in, di, uo), "ML_query").

:- type prog ---> prog(query_type, imports, term, varset).

query(QueryType, Imports, Options, MDB_Stdin, MDB_Stdout) -->
	% write_import_list(Imports),
	print(MDB_Stdout, query_prompt(QueryType)),
	io__set_input_stream(MDB_Stdin, OldStdin),
	term_io__read_term(Result),
	io__set_input_stream(OldStdin, _),
	( { Result = eof },
		io__nl(MDB_Stdout)
	; { Result = error(Msg, _Line) },
		io__write_string(MDB_Stdout, Msg), io__nl(MDB_Stdout),
		query(QueryType, Imports, Options, MDB_Stdin, MDB_Stdout)
	; { Result = term(VarSet, Term) },
		% io__write_string("Read term: "),
		% term_io__write_term(Term, VarSet),
		% io__write_string("\n"),
		(if { Term = term__functor(term__atom("quit"), [], _) } then
			io__nl(MDB_Stdout)
		else if { Term = term__functor(term__atom("options"),
				[term__functor(term__string(NewOptions),
					[], _)], _) } then
			print(MDB_Stdout, "Compilation options: "),
			print(MDB_Stdout, NewOptions),
			io__nl(MDB_Stdout),
			query(QueryType, Imports, NewOptions,
				MDB_Stdin, MDB_Stdout)
		else if { term_to_list(Term, ModuleList) } then
			{ list__append(Imports, ModuleList, NewImports) },
			write_import_list(MDB_Stdout, NewImports),
			query(QueryType, NewImports, Options,
				MDB_Stdin, MDB_Stdout)
		else
			run_query(Options,
				prog(QueryType, Imports, Term, VarSet)),
			query(QueryType, Imports, Options,
				MDB_Stdin, MDB_Stdout)
		)
	).

:- func query_prompt(query_type) = string.
query_prompt(normal_query) = "?- ".
query_prompt(cc_query) = "?- ".
query_prompt(io_query) = "run <-- ".

:- 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) is det.

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 output:\n\t",
			ErrorMessage, "\n"],
			Message) },
		io__write_string(Message),
		% XXX we really ought to throw an exception here;
		%     instead, we just return a bogus stream (stdout)
		io__stdout_stream(Stream)
	).

:- pred write_prog_to_stream(prog::in, io__state::di, io__state::uo) is det.

write_prog_to_stream(prog(QueryType, Imports, Term, VarSet)) -->
	io__write_string("
			:- module query.
			:- interface.
			:- import_module io.
			:- pred run(io__state::di, io__state::uo) is cc_multi.
			:- implementation.
			"),
	io__output_stream(Out),
	write_import_list(Out, ["std_util" | Imports]),
	io__write_string("
			:- pragma source_file(""<stdin>"").
			run -->
	"),
	( { QueryType = normal_query },
		{ term__vars(Term, Vars0) },
		{ list__remove_dups(Vars0, Vars) },
/*
	For a normal query, we generate code that looks like this:

		run -->
			unsorted_aggregate(
				(pred(res(A,B,C)::out) is nondet :-
					query(A,B,C)),
				(pred(res(A,B,C)::in, di, uo) -->
					print("A = "), print(A), print(","),
					print("B = "), print(B), print(","),
					print("C = "), print(C), print(","),
					print("true ;\n"))
			),
			print(""fail.\n""),
			print(""No (more) solutions.\n"").

		:- type res(A, B, C) ---> res(A, B, C).

		% :- mode query(out, out, out) is nondet.
		query(res(A, B, C, D)) :-
				...
*/
		io__write_string("
				unsorted_aggregate(
					(pred(res"),
		write_args(Vars, VarSet),
		io__write_string("::out) is nondet :-
						query"),
		write_args(Vars, VarSet),
		io__write_string("),"),
		io__write_string("(pred(res"),
		write_args(Vars, VarSet),
		io__write_string("::in, di, uo) is det -->
						"),
		list__foldl(write_code_to_print_one_var(VarSet), Vars),
		io__write_string("
					io__write_string(""true ;\n""))
				),
				io__write_string(""fail.\n""),
				io__write_string(""No (more) solutions.\n"").

			:- type res"),
		write_args(Vars, VarSet),
		io__write_string(" ---> res"),
		write_args(Vars, VarSet),
		io__write_string(".\n"),

/******
		io__write_string("
			:- mode query"),
		( { Vars \= [] } ->
			{ list__length(Vars, NumVars) },
			{ list__duplicate(NumVars, "out", Modes) },
			io__write_string("("),
			io__write_list(Modes, ", ", io__write_string),
			io__write_string(")")
		;
			[]
		),
		io__write_string(" is nondet."),
******/

		io__write_string("
			query"),
		write_args(Vars, VarSet),
		io__write_string(" :- "),
		write_line_directive,
		term_io__write_term(VarSet, Term),
		io__write_string(" .\n")
	; { QueryType = cc_query },
		%
		% For a cc_query, we generate code that looks like this:
		%
		%	run --> if { query(A, B, C) } then 
		%			print("A = "), print(A), print(", "),
		%			print("B = "), print(B), print(", "),
		%			print("C = "), print(C), print(", "),
		%			print("Yes.\n"))
		%		else
		%			print("No solution.\n").
		%
		%	query(A, B, C) :- ...
		%

		{ term__vars(Term, Vars0) },
		{ list__remove_dups(Vars0, Vars) },
		io__write_string("(if { query"),
		write_args(Vars, VarSet),
		io__write_string(" } then\n"),
		list__foldl(write_code_to_print_one_var(VarSet), Vars),
		io__write_string("
					io__write_string(""true.\\n"")
				else
					io__write_string(""No solution.\\n"")
				).
		"),
		io__write_string("query"),
		write_args(Vars, VarSet),
		io__write_string(" :-\n"),
		write_line_directive,
		term_io__write_term(VarSet, Term),
		io__write_string(" .\n")
	; { QueryType = io_query },
		%
		% For an io_query, we just spit the code straight out:
		%
		%	run --> ...
		%
		write_line_directive,
		term_io__write_term(VarSet, Term),
		io__write_string(" .\n")
	).

:- pred write_line_directive(io__state::di, io__state::uo) is det.

write_line_directive -->
	io__write_string("\n#"),
	io__get_line_number(LineNum),
	io__write_int(LineNum),
	io__nl.

:- pred write_code_to_print_one_var(varset::in, var::in,
		io__state::di, io__state::uo) is det.

write_code_to_print_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("), io__write_string("", ""), ").

:- pred write_args(list(var)::in, varset::in,
		io__state::di, io__state::uo) is det.

write_args(Vars, VarSet) -->
	( { Vars \= [] } ->
		io__write_string("("),
		io__write_list(Vars, ", ", write_one_var(VarSet)),
		io__write_string(")")
	;
		[]
	).

:- pred write_one_var(varset::in, var::in,
		io__state::di, io__state::uo) is det.

write_one_var(VarSet, Var) -->
	term_io__write_variable(Var, VarSet).

:- pred write_import_list(io__output_stream::in, imports::in,
		io__state::di, io__state::uo) is det.

write_import_list(Out, Imports) -->
	io__write_string(Out, ":- import_module "),
	io__write_list(Out, Imports, ", ", term_io__quote_atom),
	io__write_string(Out, ".\n").

%-----------------------------------------------------------------------------%
%
% 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) -->
	%
	% We use the following options:
	%	--pic-reg
	%		needed for shared libraries / dynamic linking
	%	--infer-all
	%		for inferring the type etc. of query/N
	%	-O0 --no-c-optimize
	%		to improve compilation speed
	%	--no-warn-det-decls-too-lax
	%	--no-warn-simple-code
	%		to avoid spurious warnings in the automatically
	%		generated parts of the query predicate
	%
	{ string__append_list([
		"mmc --grade ", grade_option, " ",
		"--infer-all ",
		"--pic-reg ", "-O0 --no-c-optimize ",
		"--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 --grade ", grade_option, " ",
			"--make-shared-lib -o libquery.so ", Options,
			" query.o"], Command2) },
		invoke_system_command(Command2, Succeeded)
	;
		{ Succeeded = no }
	).

:- func grade_option = string.
%
% `grade_option' returns MR_GRADE_OPT,
% which is defined in runtime/mercury_grade.h.
% This is a string containing the grade that the current
% executable was compiled in, in a form suitable for
% passing as a `--grade' option to mmc or ml.
%
:- pragma c_header_code("#include ""mercury_grade.h""").
:- pragma c_code(grade_option = (GradeOpt::out),
	[thread_safe, will_not_call_mercury],
	"make_aligned_string(GradeOpt, (String) MR_GRADE_OPT);").

:- func verbose = bool.
verbose = no.

:- pred invoke_system_command(string, bool, state, state).
:- mode invoke_system_command(in, out, di, uo) is det.

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 run/2 in the module query.
		%
		{ QueryProc = mercury_proc(predicate, unqualified("query"),
					"run", 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.
			%
			call(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