Files
mercury/browser/browse.m
Julien Fischer 578a42f407 Fix compilation in non-C grades.
Commit 781d827ca added Mercury clauses that call unexpected/2 to use a stubs in
non-C grades for predicates that are implemented using foreign_procs in C
grades. Calling unexpected/2 requires the require module to be imported, which
(a) wasn't done and (b) causes warning about unused imports in C grades.
Fix this by calling private_builtin.sorry/1 instead.

browse/*.m:
   Call private_builtin.sorry/1 instead of unexpected/2.

   Add some missing no_determinism_warning pragmas.
2025-10-09 23:34:51 +11:00

654 lines
24 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 1998-2007, 2009-2010 The University of Melbourne.
% Copyright (C) 2017-2018, 2020-2023, 2025 The Mercury team.
% This file is distributed under the terms specified in COPYING.LIB.
%---------------------------------------------------------------------------%
%
% File: browse.m.
% Author: aet.
% Stability: low.
%
% Implements a very simple term browser.
% There are a number of features that haven't been incorporated:
%
% - Scripting language that allows precise control over
% how types are printed.
% - User preferences, which use the scripting language
% to allow user control beyond the provided defaults.
% - Node expansion and contraction in the style of Windows Explorer.
%
%---------------------------------------------------------------------------%
:- module mdb.browse.
:- interface.
:- import_module mdb.browser_info.
:- import_module mdb.browser_term.
:- import_module io.
:- import_module list.
:- import_module maybe.
%---------------------------------------------------------------------------%
% The non-interactive term browser. The caller type should be either
% `print' or `print_all'. The default portray format for that
% caller type is used.
%
:- pred print_browser_term(io.text_output_stream::in, browse_caller_type::in,
browser_term::in, browser_persistent_state::in,
io::di, io::uo) is cc_multi.
% As above, except that the supplied format will override the default.
%
:- pred print_browser_term_format(io.text_output_stream::in,
browse_caller_type::in, portray_format::in, browser_term::in,
browser_persistent_state::in, io::di, io::uo) is cc_multi.
%---------------------------------------------------------------------------%
% The interactive term browser. The caller type will be `browse', and
% the default format for the `browse' caller type will be used. Since
% this predicate is exported to be used by C code, no browser term
% mode function can be supplied.
%
:- pred browse_browser_term_no_modes(io.text_input_stream::in,
io.text_output_stream::in, browser_term::in,
maybe_track_subterm(list(down_dir))::out,
browser_persistent_state::in, browser_persistent_state::out,
io::di, io::uo) is cc_multi.
% The interactive term browser. The caller type will be `browse' and
% the default format for the `browse' caller type will be used.
%
:- pred browse_browser_term(io.text_input_stream::in,
io.text_output_stream::in, maybe(browser_mode_func)::in, browser_term::in,
maybe_track_subterm(list(down_dir))::out,
browser_persistent_state::in, browser_persistent_state::out,
io::di, io::uo) is cc_multi.
% As above, except that the supplied format will override the default.
% Again, this is exported to C code, so the browser term mode function
% can't be supplied.
%
:- pred browse_browser_term_format_no_modes(io.text_input_stream::in,
io.text_output_stream::in, portray_format::in, browser_term::in,
browser_persistent_state::in, browser_persistent_state::out,
io::di, io::uo) is cc_multi.
% As above, except that the supplied format will override the default.
%
:- pred browse_browser_term_format(io.text_input_stream::in,
io.text_output_stream::in, portray_format::in,
maybe(browser_mode_func)::in, browser_term::in,
browser_persistent_state::in, browser_persistent_state::out,
io::di, io::uo) is cc_multi.
% The browser interface for the external debugger. The caller type
% will be `browse', and the default format will be used.
% This version is exported for use in C code, so no browser term mode
% function can be supplied.
%
:- pred browse_external_no_modes(io.text_input_stream::in,
io.text_output_stream::in, T::in,
browser_persistent_state::in, browser_persistent_state::out,
io::di, io::uo) is cc_multi.
% The browser interface for the external debugger. The caller type
% will be `browse', and the default format will be used.
%
:- pred browse_external(io.text_input_stream::in, io.text_output_stream::in,
maybe(browser_mode_func)::in, T::in,
browser_persistent_state::in, browser_persistent_state::out,
io::di, io::uo) is cc_multi.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module mdb.parse.
:- import_module mdb.print_term.
:- import_module mdb.term_paths.
:- import_module bool.
:- import_module getopt.
:- import_module map.
:- import_module pair.
:- import_module stream.
:- import_module string.
:- 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",
browse_browser_term_no_modes(in, in, in, out, in, out, di, uo),
"ML_BROWSE_browse_browser_term_no_modes").
:- pragma foreign_export("C",
browse_browser_term_format_no_modes(in, in, in, in, in, out, di, uo),
"ML_BROWSE_browse_browser_term_format_no_modes").
:- pragma foreign_export("C",
browse_external_no_modes(in, in, in, in, out, di, uo),
"ML_BROWSE_browse_external_no_modes").
:- pragma foreign_export("C",
print_browser_term(in, in, in, in, di, uo),
"ML_BROWSE_print_browser_term").
:- pragma foreign_export("C",
print_browser_term_format(in, in, in, in, in, di, uo),
"ML_BROWSE_print_browser_term_format").
%---------------------------------------------------------------------------%
%
% Non-interactive display.
%
print_browser_term(OutputStream, Caller, Term, State, !IO) :-
print_common(OutputStream, Caller, no, Term, State, !IO).
print_browser_term_format(OutputStream, Caller, Format, Term, State, !IO) :-
print_common(OutputStream, Caller, yes(Format), Term, State, !IO).
:- pred print_common(io.text_output_stream::in, browse_caller_type::in,
maybe(portray_format)::in, browser_term::in,
browser_persistent_state::in, io::di, io::uo) is cc_multi.
print_common(OutputStream, Caller, MaybeFormat, BrowserTerm, State, !IO) :-
MaybeModeFunc = no,
Info = browser_info_init(BrowserTerm, Caller, MaybeFormat,
MaybeModeFunc, State),
browser_info.get_format(Info, Caller, MaybeFormat, Format),
% For plain terms, we assume that the variable name has been printed
% on the first part of the line. If the format is something other than
% `flat', then we need to start on the next line.
( if
BrowserTerm = plain_term(_),
Format \= flat
then
io.nl(OutputStream, !IO)
else
true
),
portray(debugger_internal(OutputStream), Caller, Format, Info, !IO).
%---------------------------------------------------------------------------%
%
% Interactive display.
%
browse_browser_term_no_modes(InputStream, OutputStream, Term, MaybeTrack,
!State, !IO) :-
MaybeFormat = no,
MaybeModeFunc = no,
browse_common(InputStream, debugger_internal(OutputStream),
MaybeFormat, MaybeModeFunc, Term, MaybeTrack, !State, !IO).
browse_browser_term(InputStream, OutputStream, MaybeModeFunc, Term, MaybeTrack,
!State, !IO) :-
MaybeFormat = no,
browse_common(InputStream, debugger_internal(OutputStream),
MaybeFormat, MaybeModeFunc, Term, MaybeTrack, !State, !IO).
browse_browser_term_format_no_modes(InputStream, OutputStream, Format, Term,
!State, !IO) :-
MaybeModeFunc = no,
browse_common(InputStream, debugger_internal(OutputStream),
yes(Format), MaybeModeFunc, Term, _, !State, !IO).
browse_browser_term_format(InputStream, OutputStream, Format, MaybeModeFunc,
Term, !State, !IO) :-
browse_common(InputStream, debugger_internal(OutputStream),
yes(Format), MaybeModeFunc, Term, _, !State, !IO).
browse_external_no_modes(InputStream, OutputStream, Term, !State, !IO) :-
MaybeFormat = no,
MaybeModeFunc = no,
browse_common(InputStream, debugger_external(OutputStream),
MaybeFormat, MaybeModeFunc, plain_term(univ(Term)), _, !State, !IO).
browse_external(InputStream, OutputStream, MaybeModeFunc, Term, !State, !IO) :-
MaybeFormat = no,
browse_common(InputStream, debugger_external(OutputStream),
MaybeFormat, MaybeModeFunc, plain_term(univ(Term)), _, !State, !IO).
%---------------------%
:- pred browse_common(io.text_input_stream::in, debugger::in,
maybe(portray_format)::in, maybe(browser_mode_func)::in, browser_term::in,
maybe_track_subterm(list(down_dir))::out,
browser_persistent_state::in, browser_persistent_state::out,
io::di, io::uo) is cc_multi.
browse_common(InputStream, Debugger, MaybeFormat, MaybeModeFunc, Object,
MaybeTrack, !State, !IO) :-
Info0 = browser_info_init(Object, browse, MaybeFormat, MaybeModeFunc,
!.State),
browse_main_loop(InputStream, Debugger, Info0, Info, !IO),
MaybeTrack = Info ^ bri_maybe_track,
!:State = Info ^ bri_state.
:- pred browse_main_loop(io.text_input_stream::in, debugger::in,
browser_info::in, browser_info::out, io::di, io::uo) is cc_multi.
browse_main_loop(InputStream, Debugger, !Info, !IO) :-
(
Debugger = debugger_internal(OutputStream),
parse.read_browser_command(InputStream, OutputStream, prompt,
Command, !IO)
;
Debugger = debugger_external(OutputStream),
parse.read_browser_command_external(InputStream, Command, !IO)
),
run_browse_cmd(Debugger, Command, Quit, !Info, !IO),
(
Quit = yes,
% write_string_debugger(Debugger, "quitting...\n", !IO)
(
Debugger = debugger_external(_),
send_term_to_socket(OutputStream, browser_quit, !IO)
;
Debugger = debugger_internal(_)
)
;
Quit = no,
browse_main_loop(InputStream, Debugger, !Info, !IO)
).
:- func prompt = string.
prompt = "browser> ".
%---------------------------------------------------------------------------%
:- pred run_browse_cmd(debugger::in, command::in, bool::out,
browser_info::in, browser_info::out, io::di, io::uo) is cc_multi.
run_browse_cmd(Debugger, Command, !:Quit, !Info, !IO) :-
% Please keep the code implementing commands in the same order
% as the definition of the command type.
% This is the default, which is overridden in some places below.
!:Quit = no,
% XXX The commands `set', `ls' and `print' should allow the format
% to be specified by an option. In each case we pass a default format
% the respective handler.
(
Command = cmd_print(PrintOption, MaybePath),
run_browse_cmd_print(Debugger, browse, PrintOption, !.Info,
MaybePath, !IO)
;
Command = cmd_display,
write_string_debugger(Debugger, "command not yet implemented\n", !IO)
;
Command = cmd_write,
write_string_debugger(Debugger, "command not yet implemented\n", !IO)
;
Command = cmd_memory_addr(MaybePath),
run_browse_cmd_memory_addr(Debugger, !.Info, MaybePath, !IO)
;
Command = cmd_cd_no_path,
set_path(root_rel([]), !Info)
;
Command = cmd_cd_path(Path),
change_dir(!.Info ^ bri_dirs, Path, NewPwd),
deref_subterm(!.Info ^ bri_term, NewPwd, Result),
(
Result = deref_result(_),
!Info ^ bri_dirs := NewPwd
;
Result = deref_error(OKPath, ErrorDir),
report_deref_error(Debugger, OKPath, ErrorDir, !IO)
)
;
Command = cmd_pwd,
write_down_path(Debugger, !.Info ^ bri_dirs, !IO),
nl_debugger(Debugger, !IO)
;
Command = cmd_track(HowTrack, ShouldAssertInvalid, MaybePath),
(
MaybePath = yes(Path),
change_dir(!.Info ^ bri_dirs, Path, NewPwd),
deref_subterm(!.Info ^ bri_term, NewPwd, SubResult),
(
SubResult = deref_result(_),
!Info ^ bri_maybe_track :=
track(HowTrack, ShouldAssertInvalid, NewPwd),
!:Quit = yes
;
SubResult = deref_error(_, _),
write_string_debugger(Debugger,
"error: cannot track subterm\n", !IO)
)
;
MaybePath = no,
!Info ^ bri_maybe_track :=
track(HowTrack, ShouldAssertInvalid, !.Info ^ bri_dirs),
!:Quit = yes
)
;
Command = cmd_mode_query(Path),
change_dir(!.Info ^ bri_dirs, Path, NewPwd),
MaybeModeFunc = !.Info ^ bri_maybe_mode_func,
run_browse_cmd_mode_query(Debugger, MaybeModeFunc, NewPwd, !IO)
;
Command = cmd_mode_query_no_path,
MaybeModeFunc = !.Info ^ bri_maybe_mode_func,
Dirs = !.Info ^ bri_dirs,
run_browse_cmd_mode_query(Debugger, MaybeModeFunc, Dirs, !IO)
;
Command = cmd_param(ParamCmd),
run_param_command(Debugger, ParamCmd, yes, !Info, !IO)
;
Command = cmd_help,
run_browse_cmnd_help(Debugger, !IO)
;
Command = cmd_quit,
!:Quit = yes
;
Command = cmd_empty
;
Command = cmd_unknown,
Msg =
"Error: unknown command or syntax error.\n" ++
"Type \"help\" for help.\n",
write_string_debugger(Debugger, Msg, !IO)
),
(
Debugger = debugger_external(OutputStream),
send_term_to_socket(OutputStream, browser_end_command, !IO)
;
Debugger = debugger_internal(_)
).
%---------------------------------------------------------------------------%
:- pred run_browse_cmd_print(debugger::in, browse_caller_type::in,
maybe(maybe_option_table(format_option))::in, browser_info::in,
maybe(path)::in, io::di, io::uo) is cc_multi.
run_browse_cmd_print(Debugger, Caller, MaybeMaybeOptionTable, Info,
MaybePath, !IO) :-
(
MaybeMaybeOptionTable = no,
browser_info.get_format(Info, Caller, no, Format),
portray_maybe_path(Debugger, Caller, Format, Info, MaybePath, !IO)
;
MaybeMaybeOptionTable = yes(MaybeOptionTable),
(
MaybeOptionTable = ok(OptionTable),
interpret_format_options(OptionTable, FormatResult),
(
FormatResult = ok(MaybeFormat),
browser_info.get_format(Info, Caller, MaybeFormat, Format),
portray_maybe_path(Debugger, Caller, Format, Info,
MaybePath, !IO)
;
FormatResult = error(Msg),
write_string_debugger(Debugger, Msg, !IO),
write_string_debugger(Debugger, "\n", !IO)
)
;
MaybeOptionTable = error(Msg),
write_string_debugger(Debugger, Msg, !IO),
write_string_debugger(Debugger, "\n", !IO)
)
).
%---------------------%
:- pred interpret_format_options(option_table(format_option)::in,
maybe_error(maybe(portray_format))::out) is det.
interpret_format_options(OptionTable, MaybeMaybeFormat) :-
map.to_assoc_list(OptionTable, OptionAssocList),
list.filter_map(bool_format_option_is_true, OptionAssocList,
TrueFormatOptions),
(
TrueFormatOptions = [],
MaybeMaybeFormat = ok(no)
;
TrueFormatOptions = [FormatOption],
(
FormatOption = flat,
Format = flat
;
FormatOption = raw_pretty,
Format = raw_pretty
;
FormatOption = pretty,
Format = pretty
;
FormatOption = verbose,
Format = verbose
),
MaybeMaybeFormat = ok(yes(Format))
;
TrueFormatOptions = [_, _ | _],
MaybeMaybeFormat = error("error: inconsistent format options")
).
:- pred bool_format_option_is_true(pair(format_option, option_data)::in,
format_option::out) is semidet.
bool_format_option_is_true(Format - bool(yes), Format).
%---------------------------------------------------------------------------%
:- pred run_browse_cmd_memory_addr(debugger::in, browser_info::in,
maybe(path)::in, io::di, io::uo) is cc_multi.
run_browse_cmd_memory_addr(Debugger, Info, MaybePath, !IO) :-
Dirs0 = Info ^ bri_dirs,
(
MaybePath = no,
Dirs = Dirs0
;
MaybePath = yes(Path),
change_dir(Dirs0, Path, Dirs)
),
deref_subterm(Info ^ bri_term, Dirs, DerefResult),
(
DerefResult = deref_result(BrowserTerm),
(
BrowserTerm = plain_term(Univ),
Value = univ_value(Univ),
get_value_representation(Value, Addr),
string.format("addr = %x\n", [i(Addr)], Str)
;
BrowserTerm = synthetic_term(_, _, _),
Str = "synthetic terms have no addresses\n"
),
write_string_debugger(Debugger, Str, !IO)
;
DerefResult = deref_error(OKPath, ErrorDir),
report_deref_error(Debugger, OKPath, ErrorDir, !IO),
nl_debugger(Debugger, !IO)
).
:- pred get_value_representation(T::in, int::out) is cc_multi.
:- pragma no_determinism_warning(pred(get_value_representation/2)).
:- pragma foreign_proc("C",
get_value_representation(Value::in, Addr::out),
[will_not_call_mercury, promise_pure],
"
Addr = (MR_Integer) Value;
").
get_value_representation(_, _) :-
private_builtin.sorry($pred).
%---------------------------------------------------------------------------%
:- pred run_browse_cmd_mode_query(debugger::in, maybe(browser_mode_func)::in,
list(down_dir)::in, io::di, io::uo) is det.
run_browse_cmd_mode_query(Debugger, MaybeModeFunc, Dirs, !IO) :-
(
MaybeModeFunc = yes(ModeFunc),
Mode = ModeFunc(Dirs),
ModeStr = browser_mode_to_string(Mode),
write_string_debugger(Debugger, ModeStr ++ "\n", !IO)
;
MaybeModeFunc = no,
write_string_debugger(Debugger,
"Mode information not available.\n", !IO)
).
:- func browser_mode_to_string(browser_term_mode) = string.
browser_mode_to_string(btm_input) = "Input".
browser_mode_to_string(btm_output) = "Output".
browser_mode_to_string(btm_not_applicable) = "Not Applicable".
browser_mode_to_string(btm_unbound) = "Unbound".
%---------------------------------------------------------------------------%
:- pred run_browse_cmnd_help(debugger::in, io::di, io::uo) is det.
run_browse_cmnd_help(Debugger, !IO) :-
string.append_list([
"Commands are:\n",
"\t[print|p|ls] [format_options] [path]\n",
"\t -- print the specified subterm using the `browse' params\n",
% "\t[d|display] [path]\n",
% The display command is not yet implemented
% "\t[w|write] [path]\n",
% The write command is not yet implemented
"\t[addr|memory_addr] [path]\n",
"\t -- print the raw memory address of the specified subterm\n",
"\tcd [path] -- cd to the specified subterm (default is root)\n",
"\tcdr n path -- repeatedly apply the cd command n times\n",
"\tpwd -- print the path to the current subterm\n",
% How should we document the "[a|accurate]" option on [t|track|m|mark]?
"\t[t|track] [path]\n",
"\t -- mark the specified subterm (default is current)\n",
"\t for tracking, and quit\n",
"\t[m|mark] [path]\n",
"\t -- mark the specified subterm (default is current)\n",
"\t for tracking, asserting for the declarative debugger\n",
"\t that it makes the current goal invalid\n",
"\tmode [path] -- show the mode of the specified subterm\n",
"\t (default is current)\n",
"\tformat [format_options] <flat|raw-pretty|verbose|pretty>\n",
"\t -- set the format\n",
"\tdepth [format_param_options] <n>\n",
"\tsize [format_param_options] <n>\n",
"\twidth [format_param_options] <n>\n",
"\tlines [format_param_options] <n>\n",
"\tnum_io_actions <n>\n",
"\t -- set the named parameter value\n",
"\tparams -- show format and parameter values\n",
"\tquit -- quit browser\n",
"\thelp -- show this help message\n",
"SICStus Prolog style commands are:\n",
"\tp -- print\n",
"\t< n -- set depth\n",
"\t^ [path] -- cd to the specified subterm (default is root)\n",
"\t? -- help\n",
"\th -- help\n",
"\n",
"-- Paths can be Unix-style or SICStus-style: /2/3/1 or ^2^3^1\n",
"\n"],
HelpMessage),
write_string_debugger(Debugger, HelpMessage, !IO).
%---------------------------------------------------------------------------%
%
% Display predicates.
%
:- pred show_settings(debugger::in, browser_info::in, io::di, io::uo) is det.
% XXX Why is this here? Neither this predicate, nor the rest of this section,
% is used anywhere.
% XXX There *is* a show_settings predicate in browser_info.m, which is
% similar, but not identical, to this code.
:- pragma consider_used(pred(show_settings/4)).
show_settings(Debugger, Info, !IO) :-
show_settings_caller(Debugger, Info, browse, "Browser", !IO),
show_settings_caller(Debugger, Info, print, "Print", !IO),
show_settings_caller(Debugger, Info, print_all, "Printall", !IO),
write_string_debugger(Debugger, "Current path is: ", !IO),
write_down_path(Debugger, Info ^ bri_dirs, !IO),
nl_debugger(Debugger, !IO),
write_string_debugger(Debugger, "Number of I/O actions printed is: ", !IO),
write_int_debugger(Debugger,
get_num_printed_io_actions(Info ^ bri_state), !IO),
nl_debugger(Debugger, !IO).
:- pred show_settings_caller(debugger::in, browser_info::in,
browse_caller_type::in, string::in, io::di, io::uo) is det.
show_settings_caller(Debugger, Info, Caller, CallerName, !IO) :-
browser_info.get_format(Info, Caller, no, Format),
write_string_debugger(Debugger, CallerName ++ " default format: ", !IO),
print_format_debugger(Debugger, Format, !IO),
nl_debugger(Debugger, !IO),
write_string_debugger(Debugger, pad_right("", ' ', row_name_len), !IO),
write_string_debugger(Debugger, pad_right("depth", ' ', depth_len), !IO),
write_string_debugger(Debugger, pad_right("size", ' ', size_len), !IO),
write_string_debugger(Debugger, pad_right("x clip", ' ', width_len), !IO),
write_string_debugger(Debugger, pad_right("y clip", ' ', lines_len), !IO),
nl_debugger(Debugger, !IO),
show_settings_caller_format(Debugger, Info, Caller, CallerName,
flat, "flat", !IO),
show_settings_caller_format(Debugger, Info, Caller, CallerName,
verbose, "verbose", !IO),
show_settings_caller_format(Debugger, Info, Caller, CallerName,
pretty, "pretty", !IO),
show_settings_caller_format(Debugger, Info, Caller, CallerName,
raw_pretty, "raw_pretty", !IO),
nl_debugger(Debugger, !IO).
:- pred show_settings_caller_format(debugger::in, browser_info::in,
browse_caller_type::in, string::in, portray_format::in, string::in,
io::di, io::uo) is det.
show_settings_caller_format(Debugger, Info, Caller, CallerName,
Format, FormatName, !IO) :-
browser_info.get_format_params(Info, Caller, Format, Params),
write_string_debugger(Debugger,
pad_right(CallerName ++ " " ++ FormatName ++ ":", ' ', row_name_len),
!IO),
write_string_debugger(Debugger,
pad_right(" ", ' ', centering_len), !IO),
write_string_debugger(Debugger,
pad_right(int_to_string(Params ^ depth), ' ', depth_len), !IO),
write_string_debugger(Debugger,
pad_right(int_to_string(Params ^ size), ' ', size_len), !IO),
write_string_debugger(Debugger,
pad_right(int_to_string(Params ^ width), ' ', width_len), !IO),
write_string_debugger(Debugger,
pad_right(int_to_string(Params ^ lines), ' ', lines_len), !IO),
nl_debugger(Debugger, !IO).
:- func row_name_len = int.
:- func centering_len = int.
:- func depth_len = int.
:- func size_len = int.
:- func width_len = int.
:- func lines_len = int.
row_name_len = 30.
centering_len = 3.
depth_len = 10.
size_len = 10.
width_len = 10.
lines_len = 10.
%---------------------------------------------------------------------------%
:- end_module mdb.browse.
%---------------------------------------------------------------------------%