Files
mercury/browser/save_term.m
Zoltan Somogyi 781d827ca1 Don't allow stubs in the browser directory.
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.
2025-10-09 05:18:37 +11:00

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.
%---------------------------------------------------------------------------%