mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-06 07:49:02 +00:00
browser/Mmakefile:
Do not specify --allow-stubs.
browser/*.m:
We used to use --allow-stubs to allow us to get away with
predicate definitions containing a foreign_proc for C and
no definition for Java or C#.
Stop relying on --allow-stubs by providing a Mercury clause
(which throws an exception) for every predicate defined by
a foreign_proc in C.
Put short lines around predicates defined in this way,
to make clear which blocks of text are related.
Delete old Java and sometimes C# foreign_procs that throw
an exception in those languages, since the Mercury version
is shorter, simpler, and works for both languages.
In a few cases, fix formatting.
414 lines
15 KiB
Mathematica
414 lines
15 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2023, 2025 The Mercury team.
|
|
% This file is distributed under the terms specified in COPYING.LIB.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: save_term.m.
|
|
%
|
|
% This module implements saving terms to files.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module mdb.save_term.
|
|
:- interface.
|
|
|
|
:- import_module mdb.browser_info.
|
|
:- import_module mdb.browser_term.
|
|
|
|
:- import_module io.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% save_term_to_file(OutputStream, FileName, Format, BrowserTerm, !IO):
|
|
%
|
|
% Save BrowserTerm to the file FileName. If there is an error,
|
|
% print an error message to OutputStream.
|
|
%
|
|
% The format of the saved term can be influenced by the Format
|
|
% argument, but how this works is not specified.
|
|
%
|
|
:- pred save_term_to_file(io.text_output_stream::in, string::in, string::in,
|
|
browser_term::in, io::di, io::uo) is cc_multi.
|
|
|
|
% save_term_to_file_xml(OutputStream, FileName, BrowserTerm, !IO):
|
|
%
|
|
% Save BrowserTerm to FileName as an XML document. If there is an error,
|
|
% print an error message to OutputStream.
|
|
%
|
|
:- pred save_term_to_file_xml(io.text_output_stream::in, string::in,
|
|
browser_term::in, io::di, io::uo) is cc_multi.
|
|
|
|
% save_term_to_file_doc(OutputStream, FileName, BrowserTerm, !IO):
|
|
%
|
|
% Save BrowserTerm to FileName as a document prettyprinted by the
|
|
% pretty_printer module in the Mercury standard library. If there is
|
|
% an error, print an error message to OutputStream.
|
|
%
|
|
:- pred save_term_to_file_doc(io.text_output_stream::in, string::in,
|
|
browser_term::in, io::di, io::uo) is det.
|
|
|
|
% Save BrowserTerm in an HTML file and launch the web browser specified
|
|
% by the web_browser_cmd field in the browser_persistent_state.
|
|
%
|
|
:- pred save_and_browse_browser_term_web(io.text_output_stream::in,
|
|
io.text_output_stream::in, browser_term::in,
|
|
browser_persistent_state::in, io::di, io::uo) is cc_multi.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module mdb.term_to_html.
|
|
|
|
:- import_module deconstruct.
|
|
:- import_module dir.
|
|
:- import_module int.
|
|
:- import_module io.call_system.
|
|
:- import_module io.environment.
|
|
:- import_module io.file.
|
|
:- import_module list.
|
|
:- import_module maybe.
|
|
:- import_module pretty_printer.
|
|
:- import_module stream.
|
|
:- import_module string.
|
|
:- import_module term_to_xml.
|
|
:- import_module type_desc.
|
|
:- import_module univ.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% We export these predicates to C for use by the tracer:
|
|
% they are used in trace/mercury_trace_browse.c.
|
|
%
|
|
|
|
:- pragma foreign_export("C",
|
|
save_term_to_file(in, in, in, in, di, uo),
|
|
"ML_BROWSE_save_term_to_file").
|
|
:- pragma foreign_export("C",
|
|
save_term_to_file_xml(in, in, in, di, uo),
|
|
"ML_BROWSE_save_term_to_file_xml").
|
|
:- pragma foreign_export("C",
|
|
save_term_to_file_doc(in, in, in, di, uo),
|
|
"ML_BROWSE_save_term_to_file_doc").
|
|
:- pragma foreign_export("C",
|
|
save_and_browse_browser_term_web(in, in, in, in, di, uo),
|
|
"ML_BROWSE_save_and_browse_browser_term_web").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
save_term_to_file(OutputStream, FileName, _Format, BrowserTerm, !IO) :-
|
|
trace [compile_time(flag("debug_save_term_to_file")), io(!TIO)] (
|
|
io.format(OutputStream, "%s\n", [s(FileName)], !TIO),
|
|
io.write_line(OutputStream, BrowserTerm, !TIO)
|
|
),
|
|
io.open_output(FileName, FileStreamResult, !IO),
|
|
(
|
|
FileStreamResult = ok(FileStream),
|
|
(
|
|
BrowserTerm = plain_term(Term),
|
|
save_univ(FileStream, 0, Term, !IO),
|
|
io.nl(FileStream, !IO)
|
|
;
|
|
BrowserTerm = synthetic_term(Functor, ArgUnivs, MaybeResultUniv),
|
|
io.write_string(FileStream, Functor, !IO),
|
|
io.write_string(FileStream, "(\n", !IO),
|
|
save_arg_univs(FileStream, 1, ArgUnivs, !IO),
|
|
io.write_string(FileStream, "\n)\n", !IO),
|
|
(
|
|
MaybeResultUniv = no
|
|
;
|
|
MaybeResultUniv = yes(ResultUniv),
|
|
io.write_string(FileStream, "=\n", !IO),
|
|
save_univ(FileStream, 1, ResultUniv, !IO),
|
|
io.write_string(FileStream, "\n", !IO)
|
|
)
|
|
),
|
|
io.close_output(FileStream, !IO)
|
|
;
|
|
FileStreamResult = error(Error),
|
|
io.error_message(Error, Msg),
|
|
io.write_string(OutputStream, Msg, !IO)
|
|
).
|
|
|
|
:- pred save_univ(io.text_output_stream::in, int::in, univ::in,
|
|
io::di, io::uo) is cc_multi.
|
|
|
|
save_univ(OutputStream, Indent, Univ, !IO) :-
|
|
save_term(OutputStream, Indent, univ_value(Univ), !IO).
|
|
|
|
:- pred save_term(io.text_output_stream::in, int::in, T::in,
|
|
io::di, io::uo) is cc_multi.
|
|
|
|
save_term(OutputStream, Indent, Term, !IO) :-
|
|
( if dynamic_cast_to_list(Term, List) then
|
|
(
|
|
List = [],
|
|
write_indent(OutputStream, Indent, !IO),
|
|
io.write_string(OutputStream, "[]", !IO)
|
|
;
|
|
List = [_ | _],
|
|
MakeUniv =
|
|
( func(Element) = (ElementUniv) :-
|
|
ElementUniv = univ(Element)
|
|
),
|
|
Univs = list.map(MakeUniv, List),
|
|
write_indent(OutputStream, Indent, !IO),
|
|
io.write_string(OutputStream, "[\n", !IO),
|
|
save_arg_univs(OutputStream, Indent + 1, Univs, !IO),
|
|
io.write_string(OutputStream, "\n", !IO),
|
|
write_indent(OutputStream, Indent, !IO),
|
|
io.write_string(OutputStream, "]", !IO)
|
|
)
|
|
else
|
|
deconstruct(Term, include_details_cc, Functor, _Arity, Args),
|
|
write_indent(OutputStream, Indent, !IO),
|
|
io.write_string(OutputStream, Functor, !IO),
|
|
(
|
|
Args = []
|
|
;
|
|
Args = [_ | _],
|
|
io.write_string(OutputStream, "(\n", !IO),
|
|
save_arg_univs(OutputStream, Indent + 1, Args, !IO),
|
|
io.write_string(OutputStream, "\n", !IO),
|
|
write_indent(OutputStream, Indent, !IO),
|
|
io.write_string(OutputStream, ")", !IO)
|
|
)
|
|
).
|
|
|
|
:- pred save_arg_univs(io.text_output_stream::in, int::in, list(univ)::in,
|
|
io::di, io::uo) is cc_multi.
|
|
|
|
save_arg_univs(_OutputStream, _Indent, [], !IO).
|
|
save_arg_univs(OutputStream, Indent, [Univ | Univs], !IO) :-
|
|
save_univ(OutputStream, Indent, Univ, !IO),
|
|
(
|
|
Univs = []
|
|
;
|
|
Univs = [_ | _],
|
|
io.write_string(OutputStream, ",\n", !IO),
|
|
save_arg_univs(OutputStream, Indent, Univs, !IO)
|
|
).
|
|
|
|
:- some [T2] pred dynamic_cast_to_list(T1::in, list(T2)::out) is semidet.
|
|
|
|
dynamic_cast_to_list(X, L) :-
|
|
% The code of this predicate is copied from pprint.m.
|
|
[ArgTypeDesc] = type_args(type_of(X)),
|
|
(_ `with_type` ArgType) `has_type` ArgTypeDesc,
|
|
dynamic_cast(X, L `with_type` list(ArgType)).
|
|
|
|
:- pred write_indent(io.text_output_stream::in, int::in, io::di, io::uo)
|
|
is det.
|
|
|
|
write_indent(OutputStream, Indent, !IO) :-
|
|
( if Indent =< 0 then
|
|
true
|
|
else
|
|
io.write_char(OutputStream, ' ', !IO),
|
|
write_indent(OutputStream, Indent - 1, !IO)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
:- type xml_predicate_wrapper
|
|
---> predicate(
|
|
predicate_name :: string,
|
|
predicate_arguments :: list(univ)
|
|
).
|
|
|
|
:- type xml_function_wrapper
|
|
---> function(
|
|
function_name :: string,
|
|
function_arguments :: list(univ),
|
|
return_value :: univ
|
|
).
|
|
|
|
save_term_to_file_xml(OutputStream, FileName, BrowserTerm, !IO) :-
|
|
io.open_output(FileName, FileStreamResult, !IO),
|
|
(
|
|
FileStreamResult = ok(FileStream),
|
|
% Note that the three calls to write_xml_doc_general_cc cannot be
|
|
% replaced by one call, because the type of the second arguments
|
|
% is different in each of the three calls.
|
|
(
|
|
BrowserTerm = plain_term(Univ),
|
|
Term = univ_value(Univ),
|
|
term_to_xml.write_xml_doc_general_cc(FileStream, Term,
|
|
simple, no_stylesheet, no_dtd, _, !IO)
|
|
;
|
|
BrowserTerm = synthetic_term(Functor, ArgUnivs, MaybeResultUniv),
|
|
(
|
|
MaybeResultUniv = no,
|
|
PredicateTerm = predicate(Functor, ArgUnivs),
|
|
term_to_xml.write_xml_doc_general_cc(FileStream, PredicateTerm,
|
|
simple, no_stylesheet, no_dtd, _, !IO)
|
|
;
|
|
MaybeResultUniv = yes(ResultUniv),
|
|
FunctionTerm = function(Functor, ArgUnivs, ResultUniv),
|
|
term_to_xml.write_xml_doc_general_cc(FileStream, FunctionTerm,
|
|
simple, no_stylesheet, no_dtd, _, !IO)
|
|
)
|
|
),
|
|
io.close_output(FileStream, !IO)
|
|
;
|
|
FileStreamResult = error(Error),
|
|
io.error_message(Error, Msg),
|
|
io.format(OutputStream, "%s\n", [s(Msg)], !IO)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
save_term_to_file_doc(OutputStream, FileName, BrowserTerm, !IO) :-
|
|
io.open_output(FileName, FileStreamResult, !IO),
|
|
(
|
|
FileStreamResult = ok(FileStream),
|
|
(
|
|
BrowserTerm = plain_term(Univ),
|
|
Term = univ_value(Univ),
|
|
Doc = pretty_printer.format(Term)
|
|
;
|
|
BrowserTerm = synthetic_term(Functor, ArgUnivs, MaybeResultUniv),
|
|
(
|
|
MaybeResultUniv = no,
|
|
PredicateTerm = predicate(Functor, ArgUnivs),
|
|
Doc = pretty_printer.format(PredicateTerm)
|
|
;
|
|
MaybeResultUniv = yes(ResultUniv),
|
|
FunctionTerm = function(Functor, ArgUnivs, ResultUniv),
|
|
Doc = pretty_printer.format(FunctionTerm)
|
|
)
|
|
),
|
|
Canonicalize = include_details_cc,
|
|
get_default_formatter_map(FMap, !IO),
|
|
Params = pp_params(78, int.max_int, triangular(int.max_int)),
|
|
promise_equivalent_solutions [!:IO] (
|
|
pretty_printer.put_doc(FileStream, Canonicalize, FMap, Params,
|
|
Doc, !IO)
|
|
),
|
|
io.close_output(FileStream, !IO)
|
|
;
|
|
FileStreamResult = error(Error),
|
|
io.error_message(Error, Msg),
|
|
io.format(OutputStream, "%s\n", [s(Msg)], !IO)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
save_and_browse_browser_term_web(OutputStream, ErrorStream, Term, State,
|
|
!IO) :-
|
|
get_mdb_dir(MaybeMdbDir, !IO),
|
|
(
|
|
MaybeMdbDir = yes(MdbDir),
|
|
MaybeBrowserCmd = State ^ web_browser_cmd,
|
|
(
|
|
MaybeBrowserCmd = yes(BrowserCmd),
|
|
io.file.get_temp_directory(TmpDir, !IO),
|
|
io.file.make_temp_file(TmpDir, "mdb", ".html", TmpResult, !IO),
|
|
(
|
|
TmpResult = ok(TmpFileName0),
|
|
( if string.suffix(TmpFileName0, ".html") then
|
|
TmpFileName = TmpFileName0
|
|
else
|
|
% Work around io.make_temp_file ignoring suffix.
|
|
io.file.remove_file(TmpFileName0, _, !IO),
|
|
TmpFileName = TmpFileName0 ++ ".html"
|
|
),
|
|
save_term_to_file_web(TmpFileName, Term, MdbDir,
|
|
SaveResult, !IO),
|
|
(
|
|
SaveResult = ok(_),
|
|
% We should actually quote the file name.
|
|
CommandStr = BrowserCmd ++ " " ++ TmpFileName,
|
|
launch_web_browser(OutputStream, ErrorStream,
|
|
CommandStr, !IO)
|
|
;
|
|
SaveResult = error(Error),
|
|
io.error_message(Error, Msg),
|
|
io.format(ErrorStream,
|
|
"Error opening file `%s': %s\n",
|
|
[s(TmpFileName), s(Msg)], !IO)
|
|
)
|
|
;
|
|
TmpResult = error(Error),
|
|
io.error_message(Error, Msg),
|
|
io.format(ErrorStream,
|
|
"Error opening temporary file: %s\n",
|
|
[s(Msg)], !IO)
|
|
)
|
|
;
|
|
MaybeBrowserCmd = no,
|
|
io.write_string(ErrorStream,
|
|
"mdb: You need to specify the shell command that launches " ++
|
|
"your preferred web browser, by issuing an mdb command " ++
|
|
"\"web_browser_cmd <command>\".\n", !IO)
|
|
)
|
|
;
|
|
MaybeMdbDir = no,
|
|
io.write_string(ErrorStream,
|
|
"Could not determine directory containing mdb files.\n", !IO)
|
|
).
|
|
|
|
:- pred get_mdb_dir(maybe(string)::out, io::di, io::uo) is det.
|
|
|
|
get_mdb_dir(Res, !IO) :-
|
|
io.environment.get_environment_var("MERCURY_DEBUGGER_INIT",
|
|
MaybeValue, !IO),
|
|
( if
|
|
MaybeValue = yes(Path),
|
|
dir.path_name_is_absolute(Path),
|
|
dir.split_name(Path, MdbDir, "mdbrc")
|
|
then
|
|
Res = yes(MdbDir)
|
|
else
|
|
Res = no
|
|
).
|
|
|
|
:- pred save_term_to_file_web(string::in, browser_term::in, string::in,
|
|
io.res(io.text_output_stream)::out, io::di, io::uo) is cc_multi.
|
|
|
|
save_term_to_file_web(FileName, BrowserTerm, MdbDir, FileStreamResult, !IO) :-
|
|
io.open_output(FileName, FileStreamResult, !IO),
|
|
(
|
|
FileStreamResult = ok(OutputStream),
|
|
term_to_html.write_html_doc(OutputStream, BrowserTerm, MdbDir, _, !IO),
|
|
io.close_output(OutputStream, !IO)
|
|
;
|
|
FileStreamResult = error(_)
|
|
).
|
|
|
|
:- pred launch_web_browser(io.text_output_stream::in,
|
|
io.text_output_stream::in, string::in, io::di, io::uo) is det.
|
|
|
|
launch_web_browser(OutputStream, ErrorStream, CommandStr, !IO) :-
|
|
io.write_string(OutputStream, "Launching web browser...\n", !IO),
|
|
io.flush_output(OutputStream, !IO),
|
|
io.call_system.call_system_return_signal(CommandStr, Result, !IO),
|
|
(
|
|
Result = ok(ExitStatus),
|
|
(
|
|
ExitStatus = exited(ExitCode),
|
|
( if ExitCode = 0 then
|
|
true
|
|
else
|
|
io.format(ErrorStream,
|
|
"mdb: The command `%s' terminated with " ++
|
|
"a non-zero exit code.\n",
|
|
[s(CommandStr)], !IO)
|
|
)
|
|
;
|
|
ExitStatus = signalled(_),
|
|
io.write_string(ErrorStream, "mdb: The browser was killed.\n", !IO)
|
|
)
|
|
;
|
|
Result = error(Error),
|
|
io.format(ErrorStream, "mdb: Error launching browser: %s.\n",
|
|
[s(string.string(Error))], !IO)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module mdb.save_term.
|
|
%---------------------------------------------------------------------------%
|