mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-17 23:05:21 +00:00
Estimated hours taken: 30 Branches: main Use dicing information in the declarative debugger. Each label in the program is assigned a suspicion based on a supplied dice. A new search mode then performs divide and query using the total suspicion of a subtree as the weighting of the subtree. browser/declarative_analyser.m: Parameterize the divide and query search mode by allowing it to work with an arbitrary weighting heuristic. Support two weighting heuristics: number of events and suspicion. Since there is still only one weight field for each suspect, if the weighting heuristic changes, then update all the weights of all the suspects. Return a different reason for asking a divide and query question, depending on the weighting heuristic. Some information (specifically how many suspect events remain and the estimated number of questions remaining for divide and query) returned by the info command depends on the current weighting heuristic being the number of events. If the current weighting heuristic is not the number of events then do not show this information. browser/declarative_debugger.m: Pass the trace node store to set_fallback_search_mode so that the weights can be recalculated if the search strategy changes. browser/declarative_edt.m: In the mercury_edt typeclass, rename the edt_weight method to edt_number_of_events and add a new method edt_subtree_suspicion. The weight field of each suspect in the search space can either be based on suspicion or number of events. Add a field to the search_space type to determine which weighting heuristic to use. Export predicates to get and set the current weighting heuristic being used. If the weighting heuristic changes the recalculate the weights of all the suspects. When calculating the weight of a suspect use the current weighting heuristic. browser/declarative_execution.m: Record a suspicion accumulator at each interface event which can be used to calculate the suspicion of a subtree in the EDT. Move the label_layout and proc_layout types as well as all utility predicates for those types to a new module, mdbcomp.label_layout. browser/declarative_oracle.m: browser/declarative_user.m: browser/debugger_interface.m: Import mdbcomp.label_layout. browser/declarative_tree.m: Adjust for the extra field in interface nodes in the annotated trace. Look at the weighting heuristic when calculating the weight of a subtree. browser/util.m: mdbcomp/program_representation.m: Move goal_path_string to mdbcomp.program_representation since it is needed in mdbcomp.label_layout. doc/user_guide.texi: Document the new search mode. mdbcomp/label_layout.m: This module contains the types label_layout and proc_layout and supporting predicates which were in mdb.declarative_execution. These types are needed in the mdbcomp.slice_and_dice module. mdbcomp/mdbcomp.m: Include label_layout. mdbcomp/slice_and_dice.m: Add functions for calculating different suspicion formulas. The intention is to experiment with different formulas in the future. Export predicates for reading a dice from the C backend. Export a predicate for retrieving the suspicion of a label given a dice. This predicate uses the suspicion_ratio_binary formula, since that seems most effective in my (as yet very limited) experience. I will implement better ways to control and customise the formula used in the future. mdbcomp/trace_counts.m: Add a function for constructing a path_port given a goal path and a trace port. If there is an unexpected exception when reading a trace counts file then print the unexpected exception. Add a predicate to convert trace count file types to a string and vica versa. runtime/mercury_stack_layout.h: Fix a typo. runtime/mercury_trace_base.c: runtime/mercury_trace_base.h: Export a function to look up the trace count slot for a label. Use this function when recording trace counts. This function will also be used in the declarative debugger backend to look up suspicions for labels. Add a function to initialise the array which records which ports need a goal path to uniquely identifiy the label. Initially I used this array elsewhere which is why I exported it. I didn't actually end up needing to use it in the final version, but I'm still exporting it, since it might be useful in the future. tests/debugger/declarative/Mmakefile: tests/debugger/declarative/dice.exp: tests/debugger/declarative/dice.inp: tests/debugger/declarative/dice.m: Test the new search mode. tests/debugger/declarative/info.exp: tests/debugger/declarative/change_search.exp: The weigting heuristic is now printed with the info command. Also some information, such as the number of suspect events, is no longer printed if the weigthing heuristic is not the number of events (since then that information is not available). trace/mercury_trace_declarative.c: Add a function to setup the trace counts array with a suspicion for each label. For efficiency the suspicion is converted from a float to an integer between 0 and 100. If a flag is set, then increment an accumulator with the suspicion of each label executed as the annotated trace is being constructed. Store the value of the accumulator at interface events, so that the frontend can efficiently calculate the suspicion of any subtree. Remove a redundant variable and comment: the goal path is no longer passed to the frontend, because the frontend has access to the label_layout from which it can get the goal path (the variable and comment are artifacts of a previous change). When checking if a search mode is valid also check if failing and passing trace counts are required for the search mode. Allow abbreviations for the search mode arguments. trace/mercury_trace_declarative.h: Export the predicate to set up the suspicions for each label. trace/mercury_trace_internal.c: Allow passing and failing test case(s) to be passed as arguments to the dd command. If passing and failing test case(s) are supplied then record suspicions in the annotated trace even if the sdq search mode is not specified. The user could switch to the sdq search mode later on. Initialise some values which were causing warnings from the C compiler. browser/debugger_interface.m: browser/declarative_analyser.m: browser/declarative_debugger.m: browser/declarative_edt.m: browser/declarative_execution.m: browser/declarative_oracle.m: browser/declarative_tree.m: browser/declarative_user.m: browser/util.m: doc/user_guide.texi: mdbcomp/mdbcomp.m: mdbcomp/program_representation.m: mdbcomp/rtti_access.m: mdbcomp/slice_and_dice.m: mdbcomp/trace_counts.m: runtime/mercury_stack_layout.h: runtime/mercury_trace_base.c: runtime/mercury_trace_base.h: tests/debugger/declarative/Mmakefile: tests/debugger/declarative/dice.exp: tests/debugger/declarative/dice.inp: tests/debugger/declarative/dice.m: tests/debugger/declarative/info.exp: trace/mercury_trace_declarative.c: trace/mercury_trace_declarative.h: trace/mercury_trace_internal.c:
1268 lines
38 KiB
Mathematica
1268 lines
38 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1999-2005 The University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU Library General
|
|
% Public License - see the file COPYING.LIB in the Mercury distribution.
|
|
%-----------------------------------------------------------------------------%
|
|
% File: declarative_user.m
|
|
% Author: Mark Brown
|
|
% Purpose:
|
|
% This module performs all the user interaction of the front
|
|
% end of the declarative debugger. It is responsible for displaying
|
|
% questions and bugs in a human-readable format, and for getting
|
|
% responses to debugger queries from the user.
|
|
%
|
|
|
|
:- module mdb.declarative_user.
|
|
|
|
:- interface.
|
|
|
|
:- import_module mdb.browser_info.
|
|
:- import_module mdb.declarative_debugger.
|
|
:- import_module mdb.help.
|
|
|
|
:- import_module bool.
|
|
:- import_module io.
|
|
|
|
:- type user_question(T)
|
|
---> plain_question(decl_question(T))
|
|
; question_with_default(decl_question(T), decl_truth).
|
|
|
|
:- type user_response(T)
|
|
---> user_answer(decl_question(T), decl_answer(T))
|
|
; trust_predicate(decl_question(T))
|
|
; trust_module(decl_question(T))
|
|
% Request that the analyser display some information
|
|
% about the state of the search and the current
|
|
% question to the given output stream.
|
|
; show_info(io.output_stream)
|
|
% Request that a new search strategy be used.
|
|
; change_search(user_search_mode)
|
|
% The user wants to undo the last answer they gave.
|
|
; undo
|
|
; exit_diagnosis(T)
|
|
; abort_diagnosis.
|
|
|
|
:- type user_search_mode
|
|
---> top_down
|
|
; divide_and_query
|
|
; suspicion_divide_and_query
|
|
; binary.
|
|
|
|
:- type user_state.
|
|
|
|
:- pred user_state_init(io.input_stream::in, io.output_stream::in,
|
|
browser_info.browser_persistent_state::in, help.system::in,
|
|
user_state::out) is det.
|
|
|
|
% This predicate handles the interactive part of the declarative
|
|
% debugging process. The user is presented with a question,
|
|
% possibly with a default answer, and is asked to respond about the
|
|
% truth of it in the intended interpretation.
|
|
%
|
|
:- pred query_user(user_question(T)::in, user_response(T)::out,
|
|
user_state::in, user_state::out, io::di, io::uo) is cc_multi.
|
|
|
|
% Confirm that the node found is indeed an e_bug or an i_bug.
|
|
%
|
|
:- pred user_confirm_bug(decl_bug::in, decl_confirmation::out,
|
|
user_state::in, user_state::out, io::di, io::uo) is cc_multi.
|
|
|
|
% Returns the state of the term browser.
|
|
%
|
|
:- func get_browser_state(user_state) = browser_info.browser_persistent_state.
|
|
|
|
% Sets the state of the term browser.
|
|
%
|
|
:- pred set_browser_state(browser_info.browser_persistent_state::in,
|
|
user_state::in, user_state::out) is det.
|
|
|
|
% Return the output stream used for interacting with the user.
|
|
%
|
|
:- func get_user_output_stream(user_state) = io.output_stream.
|
|
|
|
% Set the testing flag of the user_state.
|
|
%
|
|
:- pred set_user_testing_flag(bool::in, user_state::in, user_state::out)
|
|
is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module mdb.browse.
|
|
:- import_module mdb.browser_term.
|
|
:- import_module mdb.declarative_execution.
|
|
:- import_module mdb.declarative_tree.
|
|
:- import_module mdb.io_action.
|
|
:- import_module mdb.parse.
|
|
:- import_module mdb.term_rep.
|
|
:- import_module mdb.util.
|
|
:- import_module mdbcomp.prim_data.
|
|
:- import_module mdbcomp.program_representation.
|
|
:- import_module mdbcomp.rtti_access.
|
|
|
|
:- import_module bool.
|
|
:- import_module char.
|
|
:- import_module deconstruct.
|
|
:- import_module exception.
|
|
:- import_module getopt.
|
|
:- import_module int.
|
|
:- import_module list.
|
|
:- import_module std_util.
|
|
:- import_module string.
|
|
|
|
:- type user_state
|
|
---> user(
|
|
instr :: io.input_stream,
|
|
outstr :: io.output_stream,
|
|
browser :: browser_persistent_state,
|
|
|
|
% yes if the question should be displayed when
|
|
% querying the user. This is used to
|
|
% supress the displaying of the question after
|
|
% the user issues a command which does not
|
|
% answer the question (such as an `info'
|
|
% command).
|
|
display_question :: bool,
|
|
help_system :: help.system,
|
|
|
|
% If this following flag is set to yes then
|
|
% user responses will be simulated and will
|
|
% always be `no', except when confirming a
|
|
% bug in which case the response will be `yes'.
|
|
testing :: bool
|
|
).
|
|
|
|
user_state_init(InStr, OutStr, Browser, HelpSystem,
|
|
user(InStr, OutStr, Browser, yes, HelpSystem, no)).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
query_user(UserQuestion, Response, !User, !IO) :-
|
|
Question = get_decl_question(UserQuestion),
|
|
(
|
|
!.User ^ testing = yes,
|
|
Node = get_decl_question_node(Question),
|
|
Response = user_answer(Question, truth_value(Node, erroneous))
|
|
;
|
|
!.User ^ testing = no,
|
|
(
|
|
!.User ^ display_question = yes,
|
|
write_decl_question(Question, !.User, !IO),
|
|
user_question_prompt(UserQuestion, Prompt),
|
|
!:User = !.User ^ display_question := no
|
|
;
|
|
!.User ^ display_question = no,
|
|
Prompt = "dd> "
|
|
),
|
|
get_command(Prompt, Command, !User, !IO),
|
|
handle_command(Command, UserQuestion, Response, !User, !IO),
|
|
(
|
|
Response \= show_info(_)
|
|
->
|
|
!:User = !.User ^ display_question := yes
|
|
;
|
|
true
|
|
)
|
|
).
|
|
|
|
:- pred handle_command(user_command::in, user_question(T)::in,
|
|
user_response(T)::out, user_state::in, user_state::out,
|
|
io::di, io::uo) is cc_multi.
|
|
|
|
handle_command(yes, UserQuestion, Response, !User, !IO) :-
|
|
Question = get_decl_question(UserQuestion),
|
|
Node = get_decl_question_node(Question),
|
|
Response = user_answer(Question, truth_value(Node, correct)).
|
|
|
|
handle_command(no, UserQuestion, Response, !User, !IO) :-
|
|
Question = get_decl_question(UserQuestion),
|
|
Node = get_decl_question_node(Question),
|
|
Response = user_answer(Question, truth_value(Node, erroneous)).
|
|
|
|
handle_command(inadmissible, UserQuestion, Response, !User, !IO) :-
|
|
Question = get_decl_question(UserQuestion),
|
|
Node = get_decl_question_node(Question),
|
|
Response = user_answer(Question, truth_value(Node, inadmissible)).
|
|
|
|
handle_command(skip, UserQuestion, Response, !User, !IO) :-
|
|
Question = get_decl_question(UserQuestion),
|
|
Node = get_decl_question_node(Question),
|
|
Response = user_answer(Question, skip(Node)).
|
|
|
|
handle_command(browse_arg(MaybeArgNum), UserQuestion, Response,
|
|
!User, !IO) :-
|
|
Question = get_decl_question(UserQuestion),
|
|
edt_node_trace_atoms(Question, InitAtom, FinalAtom),
|
|
(
|
|
MaybeArgNum = yes(ArgNum),
|
|
browse_atom_argument(InitAtom, FinalAtom, ArgNum, MaybeMark,
|
|
!User, !IO),
|
|
(
|
|
MaybeMark = no,
|
|
query_user(UserQuestion, Response,
|
|
!User, !IO)
|
|
;
|
|
MaybeMark = yes(Mark),
|
|
ArgPos = arg_num_to_arg_pos(ArgNum),
|
|
Node = get_decl_question_node(Question),
|
|
Answer = suspicious_subterm(Node, ArgPos, Mark),
|
|
Response = user_answer(Question, Answer)
|
|
)
|
|
;
|
|
MaybeArgNum = no,
|
|
browse_atom(InitAtom, FinalAtom, MaybeMark, !User, !IO),
|
|
(
|
|
MaybeMark = no,
|
|
query_user(UserQuestion, Response,
|
|
!User, !IO)
|
|
;
|
|
%
|
|
% If the user marks the predicate or function,
|
|
% we make the atom erroneous.
|
|
%
|
|
MaybeMark = yes([]),
|
|
Node = get_decl_question_node(Question),
|
|
Answer = truth_value(Node, erroneous),
|
|
Response = user_answer(Question, Answer)
|
|
;
|
|
MaybeMark = yes([ArgNum | Mark]),
|
|
ArgPos = arg_num_to_arg_pos(ArgNum),
|
|
Node = get_decl_question_node(Question),
|
|
Answer = suspicious_subterm(Node, ArgPos, Mark),
|
|
Response = user_answer(Question, Answer)
|
|
)
|
|
).
|
|
|
|
handle_command(browse_xml_arg(MaybeArgNum), UserQuestion, Response,
|
|
!User, !IO) :-
|
|
Question = get_decl_question(UserQuestion),
|
|
edt_node_trace_atoms(Question, _, FinalAtom),
|
|
(
|
|
MaybeArgNum = yes(ArgNum),
|
|
browse_xml_atom_argument(FinalAtom, ArgNum, !.User, !IO)
|
|
;
|
|
MaybeArgNum = no,
|
|
browse_xml_atom(FinalAtom, !.User, !IO)
|
|
),
|
|
query_user(UserQuestion, Response, !User, !IO).
|
|
|
|
handle_command(print_arg(From, To), UserQuestion, Response,
|
|
!User, !IO) :-
|
|
Question = get_decl_question(UserQuestion),
|
|
edt_node_trace_atoms(Question, _, TraceAtom),
|
|
print_atom_arguments(TraceAtom, From, To, !.User, !IO),
|
|
query_user(UserQuestion, Response, !User, !IO).
|
|
|
|
handle_command(set(MaybeOptionTable, Setting), UserQuestion, Response, !User,
|
|
!IO) :-
|
|
(
|
|
MaybeOptionTable = ok(OptionTable),
|
|
browser_info.set_param(no, OptionTable, Setting,
|
|
!.User ^ browser, Browser),
|
|
!:User = !.User ^ browser := Browser
|
|
;
|
|
MaybeOptionTable = error(Msg),
|
|
io.write_string(Msg++"\n", !IO)
|
|
),
|
|
query_user(UserQuestion, Response, !User, !IO).
|
|
|
|
handle_command(trust_predicate, UserQuestion, trust_predicate(Question),
|
|
!User, !IO) :-
|
|
Question = get_decl_question(UserQuestion).
|
|
|
|
handle_command(trust_module, UserQuestion, trust_module(Question),
|
|
!User, !IO) :-
|
|
Question = get_decl_question(UserQuestion).
|
|
|
|
handle_command(info, _, show_info(!.User ^ outstr), !User, !IO).
|
|
|
|
handle_command(undo, _, undo, !User, !IO).
|
|
|
|
handle_command(browse_io(ActionNum), UserQuestion, Response,
|
|
!User, !IO) :-
|
|
Question = get_decl_question(UserQuestion),
|
|
edt_node_io_actions(Question, MaybeIoActions),
|
|
% We don't have code yet to trace a marked I/O action.
|
|
browse_chosen_io_action(MaybeIoActions, ActionNum, _MaybeMark,
|
|
!User, !IO),
|
|
query_user(UserQuestion, Response, !User, !IO).
|
|
|
|
handle_command(print_io(From, To), UserQuestion, Response,
|
|
!User, !IO) :-
|
|
Question = get_decl_question(UserQuestion),
|
|
edt_node_io_actions(Question, MaybeIoActions),
|
|
print_chosen_io_actions(MaybeIoActions, From, To, !.User, !IO),
|
|
query_user(UserQuestion, Response, !User, !IO).
|
|
|
|
handle_command(change_search(Mode), _, change_search(Mode), !User, !IO).
|
|
|
|
handle_command(ask, UserQuestion, Response, !User, !IO) :-
|
|
!:User = !.User ^ display_question := yes,
|
|
query_user(UserQuestion, Response, !User, !IO).
|
|
|
|
handle_command(pd, UserQuestion, Response, !User, !IO) :-
|
|
Question = get_decl_question(UserQuestion),
|
|
Node = get_decl_question_node(Question),
|
|
Response = exit_diagnosis(Node).
|
|
|
|
handle_command(quit, _, Response, !User, !IO) :-
|
|
Response = abort_diagnosis.
|
|
|
|
handle_command(help(MaybeCmd), UserQuestion, Response, !User, !IO) :-
|
|
(
|
|
MaybeCmd = yes(Cmd),
|
|
Path = ["decl", Cmd]
|
|
;
|
|
MaybeCmd = no,
|
|
Path = ["concepts", "decl_debug"]
|
|
),
|
|
help.path(!.User ^ help_system, Path, !.User ^ outstr, Res, !IO),
|
|
(
|
|
Res = help.ok
|
|
;
|
|
Res = help.error(Message),
|
|
io.write_strings([Message, "\n"], !IO)
|
|
),
|
|
query_user(UserQuestion, Response, !User, !IO).
|
|
|
|
handle_command(empty_command, UserQuestion, Response, !User,
|
|
!IO) :-
|
|
(
|
|
UserQuestion = plain_question(_),
|
|
Command = skip
|
|
;
|
|
UserQuestion = question_with_default(_, Truth),
|
|
(
|
|
Truth = correct,
|
|
Command = yes
|
|
;
|
|
Truth = erroneous,
|
|
Command = no
|
|
;
|
|
Truth = inadmissible,
|
|
Command = inadmissible
|
|
)
|
|
),
|
|
handle_command(Command, UserQuestion, Response, !User, !IO).
|
|
|
|
handle_command(illegal_command, UserQuestion, Response, !User, !IO) :-
|
|
io.write_string(!.User ^ outstr, "Unknown command, 'h' for help.\n",
|
|
!IO),
|
|
query_user(UserQuestion, Response, !User, !IO).
|
|
|
|
:- func arg_num_to_arg_pos(int) = arg_pos.
|
|
|
|
arg_num_to_arg_pos(ArgNum) = ArgPos :-
|
|
Which = chosen_head_vars_presentation,
|
|
(
|
|
Which = only_user_headvars,
|
|
ArgPos = user_head_var(ArgNum)
|
|
;
|
|
Which = all_headvars,
|
|
ArgPos = any_head_var(ArgNum)
|
|
).
|
|
|
|
:- func get_decl_question(user_question(T)) = decl_question(T).
|
|
|
|
get_decl_question(plain_question(Q)) = Q.
|
|
get_decl_question(question_with_default(Q, _)) = Q.
|
|
|
|
:- pred user_question_prompt(user_question(T)::in, string::out) is det.
|
|
|
|
user_question_prompt(plain_question(Question), Prompt) :-
|
|
decl_question_prompt(Question, Prompt).
|
|
|
|
user_question_prompt(question_with_default(Question, DefaultTruth), Prompt) :-
|
|
decl_question_prompt(Question, QuestionPrompt),
|
|
default_prompt(DefaultTruth, DefaultPrompt),
|
|
string.append(QuestionPrompt, DefaultPrompt, Prompt).
|
|
|
|
:- pred decl_question_prompt(decl_question(T)::in, string::out) is det.
|
|
|
|
decl_question_prompt(wrong_answer(_, _, _), "Valid? ").
|
|
decl_question_prompt(missing_answer(_, _, [_ | _]), "Complete? ").
|
|
decl_question_prompt(missing_answer(_, _, []), "Unsatisfiable? ").
|
|
decl_question_prompt(unexpected_exception(_, _, _), "Expected? ").
|
|
|
|
:- pred default_prompt(decl_truth::in, string::out) is det.
|
|
|
|
default_prompt(correct, "[yes] ").
|
|
default_prompt(erroneous, "[no] ").
|
|
default_prompt(inadmissible, "[inadmissible] ").
|
|
|
|
% Find the initial and final atoms for a question. For all
|
|
% questions besides wrong answer questions the initial and
|
|
% final atoms will be the same.
|
|
%
|
|
:- pred edt_node_trace_atoms(decl_question(T)::in, trace_atom::out,
|
|
trace_atom::out) is det.
|
|
|
|
edt_node_trace_atoms(wrong_answer(_, InitDeclAtom, FinalDeclAtom),
|
|
InitDeclAtom ^ init_atom, FinalDeclAtom ^ final_atom).
|
|
edt_node_trace_atoms(missing_answer(_, InitDeclAtom, _),
|
|
InitDeclAtom ^ init_atom, InitDeclAtom ^ init_atom).
|
|
edt_node_trace_atoms(unexpected_exception(_, InitDeclAtom, _),
|
|
InitDeclAtom ^ init_atom, InitDeclAtom ^ init_atom).
|
|
|
|
:- pred edt_node_io_actions(decl_question(T)::in, maybe(io_action_range)::out)
|
|
is det.
|
|
|
|
edt_node_io_actions(wrong_answer(_, _, FinalDeclAtom),
|
|
FinalDeclAtom ^ final_io_actions).
|
|
edt_node_io_actions(missing_answer(_, _, _), no).
|
|
edt_node_io_actions(unexpected_exception(_, _, _), no).
|
|
|
|
:- pred decl_bug_trace_atom(decl_bug::in, trace_atom::out, trace_atom::out)
|
|
is det.
|
|
|
|
decl_bug_trace_atom(e_bug(incorrect_contour(InitDeclAtom, FinalDeclAtom, _,
|
|
_)), InitDeclAtom ^ init_atom, FinalDeclAtom ^ final_atom).
|
|
decl_bug_trace_atom(e_bug(partially_uncovered_atom(InitDeclAtom, _)),
|
|
InitDeclAtom ^ init_atom, InitDeclAtom ^ init_atom).
|
|
decl_bug_trace_atom(e_bug(unhandled_exception(InitDeclAtom, _, _)),
|
|
InitDeclAtom ^ init_atom, InitDeclAtom ^ init_atom).
|
|
decl_bug_trace_atom(i_bug(inadmissible_call(_, _, InitDeclAtom, _)),
|
|
InitDeclAtom ^ init_atom, InitDeclAtom ^ init_atom).
|
|
|
|
:- pred decl_bug_io_actions(decl_bug::in, maybe(io_action_range)::out) is det.
|
|
|
|
decl_bug_io_actions(e_bug(incorrect_contour(_, FinalDeclAtom, _, _)),
|
|
FinalDeclAtom ^ final_io_actions).
|
|
decl_bug_io_actions(e_bug(partially_uncovered_atom(_, _)), no).
|
|
decl_bug_io_actions(e_bug(unhandled_exception(_, _, _)), no).
|
|
decl_bug_io_actions(i_bug(inadmissible_call(_, _, _, _)), no).
|
|
|
|
:- pred browse_chosen_io_action(maybe(io_action_range)::in, int::in,
|
|
maybe(term_path)::out, user_state::in, user_state::out,
|
|
io::di, io::uo) is cc_multi.
|
|
|
|
browse_chosen_io_action(MaybeIoActions, ActionNum, MaybeMark, !User, !IO) :-
|
|
(
|
|
MaybeIoActions = yes(IoActions),
|
|
find_tabled_io_action(IoActions, ActionNum, MaybeIoAction,
|
|
!IO),
|
|
(
|
|
MaybeIoAction = yes(IoAction),
|
|
browse_io_action(IoAction, MaybeMark, !User, !IO)
|
|
;
|
|
MaybeIoAction = no,
|
|
MaybeMark = no
|
|
)
|
|
;
|
|
MaybeIoActions = no,
|
|
io.write_string("No such IO action.\n", !IO),
|
|
MaybeMark = no
|
|
).
|
|
|
|
:- pred find_tabled_io_action(io_action_range::in, int::in,
|
|
maybe(io_action)::out, io::di, io::uo) is det.
|
|
|
|
find_tabled_io_action(io_action_range(Cur, End), TabledActionNum,
|
|
MaybeIoAction, !IO) :-
|
|
(
|
|
Cur = End
|
|
->
|
|
MaybeIoAction = no
|
|
;
|
|
get_maybe_io_action(Cur, MaybeTabledIoAction, !IO),
|
|
(
|
|
MaybeTabledIoAction = tabled(IoAction),
|
|
(
|
|
TabledActionNum = 1
|
|
->
|
|
MaybeIoAction = yes(IoAction)
|
|
;
|
|
find_tabled_io_action(io_action_range(Cur + 1,
|
|
End), TabledActionNum - 1,
|
|
MaybeIoAction, !IO)
|
|
)
|
|
;
|
|
MaybeTabledIoAction = untabled,
|
|
find_tabled_io_action(io_action_range(Cur + 1, End),
|
|
TabledActionNum, MaybeIoAction, !IO)
|
|
)
|
|
).
|
|
|
|
:- pred print_chosen_io_actions(maybe(io_action_range)::in, int::in, int::in,
|
|
user_state::in, io::di, io::uo) is cc_multi.
|
|
|
|
print_chosen_io_actions(MaybeIoActions, From, To, User0, !IO) :-
|
|
print_chosen_io_action(MaybeIoActions, From, User0, OK, !IO),
|
|
( OK = yes, From + 1 =< To ->
|
|
print_chosen_io_actions(MaybeIoActions, From + 1, To, User0,
|
|
!IO)
|
|
;
|
|
true
|
|
).
|
|
|
|
:- pred print_chosen_io_action(maybe(io_action_range)::in, int::in,
|
|
user_state::in, bool::out, io::di, io::uo) is cc_multi.
|
|
|
|
print_chosen_io_action(MaybeIoActions, ActionNum, User0, OK, !IO) :-
|
|
(
|
|
MaybeIoActions = yes(IoActions),
|
|
find_tabled_io_action(IoActions, ActionNum, MaybeIoAction,
|
|
!IO),
|
|
(
|
|
MaybeIoAction = yes(IoAction),
|
|
print_tabled_io_action(User0, tabled(IoAction), !IO),
|
|
OK = yes
|
|
;
|
|
MaybeIoAction = no,
|
|
io.write_string("No such IO action.\n", !IO),
|
|
OK = no
|
|
)
|
|
;
|
|
MaybeIoActions = no,
|
|
io.write_string("No such IO action.\n", !IO),
|
|
OK = no
|
|
).
|
|
|
|
:- pred browse_io_action(io_action::in, maybe(term_path)::out,
|
|
user_state::in, user_state::out, io::di, io::uo) is cc_multi.
|
|
|
|
browse_io_action(IoAction, MaybeMark, !User, !IO) :-
|
|
Term = io_action_to_browser_term(IoAction),
|
|
browse_browser_term(Term, !.User ^ instr, !.User ^ outstr, no,
|
|
MaybeDirs, !.User ^ browser, Browser, !IO),
|
|
maybe_convert_dirs_to_path(MaybeDirs, MaybeMark),
|
|
!:User = !.User ^ browser := Browser.
|
|
|
|
:- pred browse_decl_bug(decl_bug::in, maybe(int)::in, user_state::in,
|
|
user_state::out, io::di, io::uo) is cc_multi.
|
|
|
|
browse_decl_bug(Bug, MaybeArgNum, !User, !IO) :-
|
|
decl_bug_trace_atom(Bug, InitAtom, FinalAtom),
|
|
(
|
|
MaybeArgNum = yes(ArgNum),
|
|
browse_atom_argument(InitAtom, FinalAtom, ArgNum, _, !User,
|
|
!IO)
|
|
;
|
|
MaybeArgNum = no,
|
|
browse_atom(InitAtom, FinalAtom, _, !User, !IO)
|
|
).
|
|
|
|
:- pred browse_xml_decl_bug(decl_bug::in, maybe(int)::in, user_state::in,
|
|
io::di, io::uo) is cc_multi.
|
|
|
|
browse_xml_decl_bug(Bug, MaybeArgNum, User, !IO) :-
|
|
decl_bug_trace_atom(Bug, _, FinalAtom),
|
|
(
|
|
MaybeArgNum = yes(ArgNum),
|
|
browse_xml_atom_argument(FinalAtom, ArgNum, User, !IO)
|
|
;
|
|
MaybeArgNum = no,
|
|
browse_xml_atom(FinalAtom, User, !IO)
|
|
).
|
|
|
|
:- pred browse_atom_argument(trace_atom::in, trace_atom::in, int::in,
|
|
maybe(term_path)::out, user_state::in, user_state::out,
|
|
io::di, io::uo) is cc_multi.
|
|
|
|
browse_atom_argument(InitAtom, FinalAtom, ArgNum, MaybeMark, !User, !IO) :-
|
|
FinalAtom = atom(_, Args0),
|
|
maybe_filter_headvars(chosen_head_vars_presentation, Args0, Args),
|
|
(
|
|
list.index1(Args, ArgNum, ArgInfo),
|
|
ArgInfo = arg_info(_, _, MaybeArg),
|
|
MaybeArg = yes(ArgRep),
|
|
term_rep.rep_to_univ(ArgRep, Arg)
|
|
->
|
|
browse_browser_term(univ_to_browser_term(Arg),
|
|
!.User ^ instr, !.User ^ outstr,
|
|
yes(get_subterm_mode_from_atoms_for_arg(ArgNum,
|
|
InitAtom, FinalAtom)),
|
|
MaybeDirs, !.User ^ browser, Browser, !IO),
|
|
maybe_convert_dirs_to_path(MaybeDirs, MaybeMark),
|
|
!:User = !.User ^ browser := Browser
|
|
;
|
|
io.write_string(!.User ^ outstr, "Invalid argument number\n",
|
|
!IO),
|
|
MaybeMark = no
|
|
).
|
|
|
|
:- pred browse_xml_atom_argument(trace_atom::in, int::in, user_state::in,
|
|
io::di, io::uo) is cc_multi.
|
|
|
|
browse_xml_atom_argument(Atom, ArgNum, User, !IO) :-
|
|
Atom = atom(_, Args0),
|
|
maybe_filter_headvars(chosen_head_vars_presentation, Args0, Args),
|
|
(
|
|
list.index1(Args, ArgNum, ArgInfo),
|
|
ArgInfo = arg_info(_, _, MaybeArg),
|
|
MaybeArg = yes(ArgRep),
|
|
term_rep.rep_to_univ(ArgRep, Arg)
|
|
->
|
|
save_and_browse_browser_term_xml(univ_to_browser_term(Arg),
|
|
User ^ outstr, User ^ outstr, User ^ browser, !IO)
|
|
;
|
|
io.write_string(User ^ outstr, "Invalid argument number\n",
|
|
!IO)
|
|
).
|
|
|
|
:- pred browse_atom(trace_atom::in, trace_atom::in, maybe(term_path)::out,
|
|
user_state::in, user_state::out, io::di, io::uo) is cc_multi.
|
|
|
|
browse_atom(InitAtom, FinalAtom, MaybeMark, !User, !IO) :-
|
|
FinalAtom = atom(ProcLayout, Args),
|
|
ProcLabel = get_proc_label_from_layout(ProcLayout),
|
|
get_user_arg_values(Args, ArgValues),
|
|
get_pred_attributes(ProcLabel, Module, Name, _, PredOrFunc),
|
|
IsFunction = pred_to_bool(unify(PredOrFunc, function)),
|
|
sym_name_to_string(Module, ".", ModuleStr),
|
|
BrowserTerm = synthetic_term_to_browser_term(ModuleStr ++ "." ++ Name,
|
|
ArgValues, IsFunction),
|
|
browse_browser_term(BrowserTerm, !.User ^ instr, !.User ^ outstr,
|
|
yes(get_subterm_mode_from_atoms(InitAtom, FinalAtom)),
|
|
MaybeDirs, !.User ^ browser, Browser, !IO),
|
|
maybe_convert_dirs_to_path(MaybeDirs, MaybeMark),
|
|
!:User = !.User ^ browser := Browser.
|
|
|
|
:- pred browse_xml_atom(trace_atom::in, user_state::in, io::di, io::uo)
|
|
is cc_multi.
|
|
|
|
browse_xml_atom(Atom, User, !IO) :-
|
|
Atom = atom(ProcLayout, Args),
|
|
ProcLabel = get_proc_label_from_layout(ProcLayout),
|
|
get_user_arg_values(Args, ArgValues),
|
|
get_pred_attributes(ProcLabel, Module, Name, _, PredOrFunc),
|
|
IsFunction = pred_to_bool(unify(PredOrFunc, function)),
|
|
sym_name_to_string(Module, ".", ModuleStr),
|
|
BrowserTerm = synthetic_term_to_browser_term(ModuleStr ++ "." ++ Name,
|
|
ArgValues, IsFunction),
|
|
save_and_browse_browser_term_xml(BrowserTerm, User ^ outstr,
|
|
User ^ outstr, User ^ browser, !IO).
|
|
|
|
:- func get_subterm_mode_from_atoms(trace_atom, trace_atom, list(dir))
|
|
= browser_term_mode.
|
|
|
|
get_subterm_mode_from_atoms(InitAtom, FinalAtom, Dirs) = Mode :-
|
|
convert_dirs_to_term_path(Dirs, Path),
|
|
(
|
|
Path = [ArgNum | TermPath],
|
|
ArgPos = arg_num_to_arg_pos(ArgNum),
|
|
Mode = get_subterm_mode_from_atoms_and_term_path(InitAtom,
|
|
FinalAtom, ArgPos, TermPath)
|
|
;
|
|
Path = [],
|
|
Mode = not_applicable
|
|
).
|
|
|
|
:- func get_subterm_mode_from_atoms_and_term_path(trace_atom, trace_atom,
|
|
arg_pos, term_path) = browser_term_mode.
|
|
|
|
get_subterm_mode_from_atoms_and_term_path(InitAtom, FinalAtom, ArgPos,
|
|
TermPath) = Mode :-
|
|
( trace_atom_subterm_is_ground(InitAtom, ArgPos, TermPath) ->
|
|
Mode = input
|
|
; trace_atom_subterm_is_ground(FinalAtom, ArgPos, TermPath) ->
|
|
Mode = output
|
|
;
|
|
Mode = unbound
|
|
).
|
|
|
|
:- func get_subterm_mode_from_atoms_for_arg(int, trace_atom, trace_atom,
|
|
list(dir)) = browser_term_mode.
|
|
|
|
get_subterm_mode_from_atoms_for_arg(ArgNum, InitAtom, FinalAtom, Dirs)
|
|
= Mode :-
|
|
convert_dirs_to_term_path(Dirs, TermPath),
|
|
ArgPos = arg_num_to_arg_pos(ArgNum),
|
|
Mode = get_subterm_mode_from_atoms_and_term_path(InitAtom, FinalAtom,
|
|
ArgPos, TermPath).
|
|
|
|
:- pred get_user_arg_values(list(trace_atom_arg)::in, list(univ)::out) is det.
|
|
|
|
get_user_arg_values([], []).
|
|
get_user_arg_values([arg_info(UserVisible, _, MaybeValue) | Args], Values) :-
|
|
get_user_arg_values(Args, Values0),
|
|
(
|
|
UserVisible = yes
|
|
->
|
|
(
|
|
MaybeValue = yes(ValueRep),
|
|
term_rep.rep_to_univ(ValueRep, Value)
|
|
;
|
|
MaybeValue = no,
|
|
Value = univ('_'`with_type`unbound)
|
|
),
|
|
Values = [Value | Values0]
|
|
;
|
|
Values = Values0
|
|
).
|
|
|
|
:- pred print_atom_arguments(trace_atom::in, int::in, int::in, user_state::in,
|
|
io::di, io::uo) is cc_multi.
|
|
|
|
print_atom_arguments(Atom, From, To, User, !IO) :-
|
|
print_atom_argument(Atom, From, User, OK, !IO),
|
|
(
|
|
OK = yes,
|
|
From + 1 =< To
|
|
->
|
|
print_atom_arguments(Atom, From + 1, To, User, !IO)
|
|
;
|
|
true
|
|
).
|
|
|
|
:- pred print_atom_argument(trace_atom::in, int::in, user_state::in, bool::out,
|
|
io::di, io::uo) is cc_multi.
|
|
|
|
print_atom_argument(Atom, ArgNum, User, OK, !IO) :-
|
|
Atom = atom(_, Args0),
|
|
maybe_filter_headvars(chosen_head_vars_presentation, Args0, Args),
|
|
(
|
|
list.index1(Args, ArgNum, ArgInfo),
|
|
ArgInfo = arg_info(_, _, MaybeArg),
|
|
MaybeArg = yes(ArgRep),
|
|
term_rep.rep_to_univ(ArgRep, Arg)
|
|
->
|
|
print_browser_term(univ_to_browser_term(Arg), User ^ outstr,
|
|
decl_caller_type, User ^ browser, !IO),
|
|
OK = yes
|
|
;
|
|
io.write_string(User ^ outstr, "Invalid argument number\n",
|
|
!IO),
|
|
OK = no
|
|
).
|
|
|
|
:- pred maybe_convert_dirs_to_path(maybe(list(dir))::in,
|
|
maybe(term_path)::out) is det.
|
|
|
|
maybe_convert_dirs_to_path(no, no).
|
|
maybe_convert_dirs_to_path(yes(Dirs), yes(TermPath)) :-
|
|
convert_dirs_to_term_path(Dirs, TermPath).
|
|
|
|
% Reverse the first argument and append the second to it.
|
|
%
|
|
:- pred reverse_and_append(list(T)::in, list(T)::in, list(T)::out) is det.
|
|
|
|
reverse_and_append([], Bs, Bs).
|
|
reverse_and_append([A | As], Bs, Cs) :-
|
|
reverse_and_append(As, [A | Bs], Cs).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type user_command
|
|
% The node is correct.
|
|
---> yes
|
|
|
|
% The node is erroneous.
|
|
; no
|
|
|
|
% The node is inadmissible.
|
|
; inadmissible
|
|
|
|
% The user has no answer.
|
|
; skip
|
|
|
|
% Browse the nth argument before answering. Or browse
|
|
% the whole predicate/function if the maybe is no.
|
|
; browse_arg(maybe(int))
|
|
|
|
% Browse the argument using an XML browser.
|
|
; browse_xml_arg(maybe(int))
|
|
|
|
% Browse the nth IO action before answering.
|
|
; browse_io(int)
|
|
|
|
% Print the nth to the mth arguments
|
|
% before answering.
|
|
; print_arg(int, int)
|
|
|
|
% Print the nth to the mth IO actions
|
|
% before answering.
|
|
; print_io(int, int)
|
|
|
|
% Commence procedural debugging from
|
|
% this point.
|
|
; pd
|
|
|
|
% Set a browser option.
|
|
; set(maybe_option_table(setting_option), setting)
|
|
|
|
% Trust the predicate being asked
|
|
% about.
|
|
; trust_predicate
|
|
|
|
% Trust the module being asked about.
|
|
; trust_module
|
|
|
|
% Print some information about the current question.
|
|
; info
|
|
|
|
% Undo the user's last answer.
|
|
; undo
|
|
|
|
% The user wants the current question re-asked.
|
|
; ask
|
|
|
|
% Change the current search strategy.
|
|
; change_search(user_search_mode)
|
|
|
|
% Abort this diagnosis session.
|
|
; quit
|
|
|
|
% Request help before answering. If the maybe argument
|
|
% is no then a general help message is displayed,
|
|
% otherwise help on the given command is displayed.
|
|
; help(maybe(string))
|
|
|
|
% User just pressed return.
|
|
; empty_command
|
|
|
|
% None of the above.
|
|
; illegal_command.
|
|
|
|
:- pred user_confirm_bug_help(user_state::in, io::di, io::uo) is det.
|
|
|
|
user_confirm_bug_help(User, !IO) :-
|
|
io.write_strings(User ^ outstr, [
|
|
"Answer one of:\n",
|
|
"\ty\tyes\t\tconfirm that the suspect is a bug\n",
|
|
"\tn\tno\t\tdo not accept that the suspect is a bug\n",
|
|
"\tb\tbrowse\t\tbrowse the suspect\n",
|
|
"\tq\tquit\t\t",
|
|
"abort this diagnosis session and return to mdb\n",
|
|
"\th, ?\thelp\t\tthis help message\n"
|
|
], !IO).
|
|
|
|
:- pred get_command(string::in, user_command::out,
|
|
user_state::in, user_state::out, io::di, io::uo) is det.
|
|
|
|
get_command(Prompt, Command, User, User, !IO) :-
|
|
util.trace_getline(Prompt, Result, User ^ instr, User ^ outstr, !IO),
|
|
(
|
|
Result = ok(String),
|
|
Words = string.words(char.is_whitespace, String),
|
|
(
|
|
Words = [CmdWord | CmdArgs],
|
|
(
|
|
cmd_handler(CmdWord, CmdHandler),
|
|
CommandPrime = CmdHandler(CmdArgs)
|
|
->
|
|
Command = CommandPrime
|
|
;
|
|
Command = illegal_command
|
|
)
|
|
;
|
|
Words = [],
|
|
Command = empty_command
|
|
)
|
|
;
|
|
Result = error(Error),
|
|
io.error_message(Error, Msg),
|
|
io.write_string(User ^ outstr, Msg, !IO),
|
|
io.nl(User ^ outstr, !IO),
|
|
Command = quit
|
|
;
|
|
Result = eof,
|
|
Command = quit
|
|
).
|
|
|
|
:- pred cmd_handler(string::in,
|
|
(func(list(string)) = user_command)::out(func(in) = out is semidet))
|
|
is semidet.
|
|
|
|
cmd_handler("y", one_word_cmd(yes)).
|
|
cmd_handler("yes", one_word_cmd(yes)).
|
|
cmd_handler("n", one_word_cmd(no)).
|
|
cmd_handler("no", one_word_cmd(no)).
|
|
cmd_handler("i", one_word_cmd(inadmissible)).
|
|
cmd_handler("inadmissible", one_word_cmd(inadmissible)).
|
|
cmd_handler("s", one_word_cmd(skip)).
|
|
cmd_handler("skip", one_word_cmd(skip)).
|
|
cmd_handler("pd", one_word_cmd(pd)).
|
|
% `abort' is a synonym for `quit' and is just here for backwards compatibility.
|
|
cmd_handler("a", one_word_cmd(quit)).
|
|
cmd_handler("abort", one_word_cmd(quit)).
|
|
cmd_handler("q", one_word_cmd(quit)).
|
|
cmd_handler("quit", one_word_cmd(quit)).
|
|
cmd_handler("?", help_cmd).
|
|
cmd_handler("h", help_cmd).
|
|
cmd_handler("help", help_cmd).
|
|
cmd_handler("info", one_word_cmd(info)).
|
|
cmd_handler("b", browse_arg_cmd).
|
|
cmd_handler("browse", browse_arg_cmd).
|
|
cmd_handler("p", print_arg_cmd).
|
|
cmd_handler("print", print_arg_cmd).
|
|
cmd_handler("set", set_arg_cmd).
|
|
cmd_handler("t", trust_arg_cmd).
|
|
cmd_handler("trust", trust_arg_cmd).
|
|
cmd_handler("mode", search_mode_cmd).
|
|
cmd_handler("m", search_mode_cmd).
|
|
cmd_handler("undo", one_word_cmd(undo)).
|
|
|
|
:- func one_word_cmd(user_command::in, list(string)::in) = (user_command::out)
|
|
is semidet.
|
|
|
|
one_word_cmd(Cmd, []) = Cmd.
|
|
|
|
:- func browse_arg_cmd(list(string)::in) = (user_command::out) is semidet.
|
|
|
|
browse_arg_cmd([]) = browse_arg(no).
|
|
browse_arg_cmd([Arg]) = BrowseCmd :-
|
|
(
|
|
string.to_int(Arg, ArgNum)
|
|
->
|
|
BrowseCmd = browse_arg(yes(ArgNum))
|
|
;
|
|
( Arg = "-x" ; Arg = "--xml" ),
|
|
BrowseCmd = browse_xml_arg(no)
|
|
).
|
|
browse_arg_cmd(["-x", Arg]) = browse_xml_arg(yes(ArgNum)) :-
|
|
string.to_int(Arg, ArgNum).
|
|
browse_arg_cmd(["--xml", Arg]) = browse_xml_arg(yes(ArgNum)) :-
|
|
string.to_int(Arg, ArgNum).
|
|
browse_arg_cmd(["io", Arg]) = browse_io(ArgNum) :-
|
|
string.to_int(Arg, ArgNum).
|
|
|
|
:- func print_arg_cmd(list(string)::in) = (user_command::out) is semidet.
|
|
|
|
print_arg_cmd([]) = ask.
|
|
print_arg_cmd([Arg]) = print_arg(From, To) :-
|
|
string_to_range(Arg, From, To).
|
|
print_arg_cmd(["io", Arg]) = print_io(From, To) :-
|
|
string_to_range(Arg, From, To).
|
|
|
|
:- pred string_to_range(string::in, int::out, int::out) is semidet.
|
|
|
|
:- func set_arg_cmd(list(string)::in) = (user_command::out) is semidet.
|
|
|
|
set_arg_cmd(ArgWords) = set(MaybeOptionTable, Setting) :-
|
|
ArgWords \= [],
|
|
parse.parse(["set" | ArgWords], set(MaybeOptionTable, Setting)).
|
|
|
|
:- func trust_arg_cmd(list(string)::in) = (user_command::out) is semidet.
|
|
|
|
trust_arg_cmd([]) = trust_predicate.
|
|
trust_arg_cmd(["module"]) = trust_module.
|
|
|
|
:- func search_mode_cmd(list(string)::in) = (user_command::out) is semidet.
|
|
|
|
search_mode_cmd(["top-down"]) = change_search(top_down).
|
|
search_mode_cmd(["top_down"]) = change_search(top_down).
|
|
search_mode_cmd(["td"]) = change_search(top_down).
|
|
search_mode_cmd(["divide-and-query"]) = change_search(divide_and_query).
|
|
search_mode_cmd(["divide_and_query"]) = change_search(divide_and_query).
|
|
search_mode_cmd(["dq"]) = change_search(divide_and_query).
|
|
search_mode_cmd(["binary"]) = change_search(binary).
|
|
search_mode_cmd(["b"]) = change_search(binary).
|
|
search_mode_cmd(["suspicion-divide-and-query"]) =
|
|
change_search(suspicion_divide_and_query).
|
|
search_mode_cmd(["suspicion_divide_and_query"]) =
|
|
change_search(suspicion_divide_and_query).
|
|
search_mode_cmd(["sdq"]) =
|
|
change_search(suspicion_divide_and_query).
|
|
|
|
:- func help_cmd(list(string)::in) = (user_command::out) is semidet.
|
|
|
|
help_cmd([]) = help(no).
|
|
help_cmd([Cmd]) = help(yes(Cmd)).
|
|
|
|
string_to_range(Arg, From, To) :-
|
|
( string.to_int(Arg, Num) ->
|
|
From = Num,
|
|
To = Num
|
|
;
|
|
[FirstStr, SecondStr] = string.words(is_dash, Arg),
|
|
string.to_int(FirstStr, First),
|
|
string.to_int(SecondStr, Second),
|
|
( First =< Second ->
|
|
From = First,
|
|
To = Second
|
|
;
|
|
From = Second,
|
|
To = First
|
|
)
|
|
).
|
|
|
|
:- pred is_dash(char::in) is semidet.
|
|
|
|
is_dash('-').
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
user_confirm_bug(Bug, Response, !User, !IO) :-
|
|
(
|
|
!.User ^ testing = yes,
|
|
Response = confirm_bug
|
|
;
|
|
!.User ^ testing = no,
|
|
write_decl_bug(Bug, !.User, !IO),
|
|
get_command("Is this a bug? ", Command, !User, !IO),
|
|
(
|
|
Command = yes
|
|
->
|
|
Response = confirm_bug
|
|
;
|
|
Command = no
|
|
->
|
|
Response = overrule_bug
|
|
;
|
|
Command = quit
|
|
->
|
|
Response = abort_diagnosis
|
|
;
|
|
Command = browse_arg(MaybeArgNum)
|
|
->
|
|
browse_decl_bug(Bug, MaybeArgNum, !User, !IO),
|
|
user_confirm_bug(Bug, Response, !User, !IO)
|
|
;
|
|
Command = browse_xml_arg(MaybeArgNum)
|
|
->
|
|
browse_xml_decl_bug(Bug, MaybeArgNum, !.User, !IO),
|
|
user_confirm_bug(Bug, Response, !User, !IO)
|
|
;
|
|
Command = browse_io(ActionNum)
|
|
->
|
|
decl_bug_io_actions(Bug, MaybeIoActions),
|
|
browse_chosen_io_action(MaybeIoActions, ActionNum,
|
|
_MaybeMark, !User, !IO),
|
|
user_confirm_bug(Bug, Response, !User, !IO)
|
|
;
|
|
user_confirm_bug_help(!.User, !IO),
|
|
user_confirm_bug(Bug, Response, !User, !IO)
|
|
)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Returns the caller type we want to use throughout the
|
|
% declarative debugger.
|
|
:- func decl_caller_type = browse_caller_type.
|
|
|
|
decl_caller_type = print.
|
|
|
|
% Display the node in user readable form on the current
|
|
% output stream.
|
|
%
|
|
:- pred write_decl_question(decl_question(T)::in, user_state::in,
|
|
io::di, io::uo) is cc_multi.
|
|
|
|
write_decl_question(wrong_answer(_, _, Atom), User, !IO) :-
|
|
write_decl_final_atom(User, "", decl_caller_type, Atom, !IO).
|
|
|
|
write_decl_question(missing_answer(_, Call, Solns), User, !IO) :-
|
|
write_decl_init_atom(User, "Call ", decl_caller_type, Call, !IO),
|
|
(
|
|
Solns = []
|
|
;
|
|
Solns = [_ | _],
|
|
io.write_string(User ^ outstr, "Solutions:\n", !IO),
|
|
list.foldl(write_decl_final_atom(User, "\t", print_all), Solns,
|
|
!IO)
|
|
).
|
|
|
|
write_decl_question(unexpected_exception(_, Call, ExceptionRep), User, !IO) :-
|
|
write_decl_init_atom(User, "Call ", decl_caller_type, Call, !IO),
|
|
io.write_string(User ^ outstr, "Throws ", !IO),
|
|
term_rep.rep_to_univ(ExceptionRep, Exception),
|
|
io.write(User ^ outstr, include_details_cc, univ_value(Exception),
|
|
!IO),
|
|
io.nl(User ^ outstr, !IO).
|
|
|
|
:- pred write_decl_bug(decl_bug::in, user_state::in, io::di, io::uo)
|
|
is cc_multi.
|
|
|
|
write_decl_bug(e_bug(EBug), User, !IO) :-
|
|
(
|
|
EBug = incorrect_contour(_, Atom, Contour, _),
|
|
io.write_string(User ^ outstr, "Found incorrect contour:\n",
|
|
!IO),
|
|
io.write_list(Contour, "", write_decl_final_atom(User, "",
|
|
decl_caller_type), !IO),
|
|
write_decl_final_atom(User, "", decl_caller_type, Atom, !IO)
|
|
;
|
|
EBug = partially_uncovered_atom(Atom, _),
|
|
io.write_string(User ^ outstr,
|
|
"Found partially uncovered atom:\n", !IO),
|
|
write_decl_init_atom(User, "", decl_caller_type, Atom, !IO)
|
|
;
|
|
EBug = unhandled_exception(Atom, ExceptionRep, _),
|
|
io.write_string(User ^ outstr,
|
|
"Found unhandled or incorrect exception:\n", !IO),
|
|
write_decl_init_atom(User, "", decl_caller_type, Atom, !IO),
|
|
term_rep.rep_to_univ(ExceptionRep, Exception),
|
|
io.write(User ^ outstr, include_details_cc,
|
|
univ_value(Exception), !IO),
|
|
io.nl(User ^ outstr, !IO)
|
|
).
|
|
|
|
write_decl_bug(i_bug(IBug), User, !IO) :-
|
|
IBug = inadmissible_call(Parent, _, Call, _),
|
|
io.write_string(User ^ outstr, "Found inadmissible call:\n", !IO),
|
|
write_decl_atom(User, "Parent ", decl_caller_type, init(Parent), !IO),
|
|
write_decl_atom(User, "Call ", decl_caller_type, init(Call), !IO).
|
|
|
|
:- pred write_decl_init_atom(user_state::in, string::in,
|
|
browse_caller_type::in, init_decl_atom::in,
|
|
io::di, io::uo) is cc_multi.
|
|
|
|
write_decl_init_atom(User, Indent, CallerType, InitAtom, !IO) :-
|
|
write_decl_atom(User, Indent, CallerType, init(InitAtom), !IO).
|
|
|
|
:- pred write_decl_final_atom(user_state::in, string::in,
|
|
browse_caller_type::in, final_decl_atom::in,
|
|
io::di, io::uo) is cc_multi.
|
|
|
|
write_decl_final_atom(User, Indent, CallerType, FinalAtom, !IO) :-
|
|
write_decl_atom(User, Indent, CallerType, final(FinalAtom), !IO).
|
|
|
|
:- pred write_decl_atom(user_state::in, string::in, browse_caller_type::in,
|
|
some_decl_atom::in, io::di, io::uo) is cc_multi.
|
|
|
|
write_decl_atom(User, Indent, CallerType, DeclAtom, !IO) :-
|
|
io.write_string(User ^ outstr, Indent, !IO),
|
|
unravel_decl_atom(DeclAtom, TraceAtom, MaybeIoActions),
|
|
TraceAtom = atom(ProcLayout, Args0),
|
|
ProcLabel = get_proc_label_from_layout(ProcLayout),
|
|
get_pred_attributes(ProcLabel, _, Functor, _, PredOrFunc),
|
|
Which = chosen_head_vars_presentation,
|
|
maybe_filter_headvars(Which, Args0, Args1),
|
|
list.map(trace_atom_arg_to_univ, Args1, Args),
|
|
%
|
|
% Call the term browser to print the atom (or part of it
|
|
% up to a size limit) as a goal.
|
|
%
|
|
BrowserTerm = synthetic_term_to_browser_term(Functor, Args,
|
|
is_function(PredOrFunc)),
|
|
browse.print_browser_term(BrowserTerm, User ^ outstr, CallerType,
|
|
User ^ browser, !IO),
|
|
write_maybe_tabled_io_actions(User, MaybeIoActions, !IO).
|
|
|
|
:- pred write_maybe_tabled_io_actions(user_state::in,
|
|
maybe(io_action_range)::in, io::di, io::uo) is cc_multi.
|
|
|
|
write_maybe_tabled_io_actions(User, MaybeIoActions, !IO) :-
|
|
(
|
|
MaybeIoActions = yes(IoActions),
|
|
count_tabled_io_actions(IoActions, NumTabled, NumUntabled,
|
|
!IO),
|
|
write_io_actions(User, NumTabled, IoActions, !IO),
|
|
(
|
|
NumUntabled > 0
|
|
->
|
|
io.write_string(User ^ outstr, "Warning: some IO " ++
|
|
"actions for this atom are not tabled.\n", !IO)
|
|
;
|
|
true
|
|
)
|
|
;
|
|
MaybeIoActions = no
|
|
).
|
|
|
|
:- pred count_tabled_io_actions(io_action_range::in, int::out, int::out,
|
|
io::di, io::uo) is det.
|
|
|
|
count_tabled_io_actions(io_action_range(Start, End), NumTabled,
|
|
NumUntabled, !IO) :-
|
|
count_tabled_io_actions_2(Start, End, 0, NumTabled, 0,
|
|
NumUntabled, !IO).
|
|
|
|
:- pred count_tabled_io_actions_2(io_seq_num::in,
|
|
io_seq_num::in, int::in, int::out, int::in, int::out, io::di, io::uo)
|
|
is det.
|
|
|
|
count_tabled_io_actions_2(Cur, End, PrevTabled, Tabled,
|
|
PrevUntabled, Untabled, !IO) :-
|
|
(
|
|
Cur = End
|
|
->
|
|
Untabled = PrevUntabled,
|
|
Tabled = PrevTabled
|
|
;
|
|
get_maybe_io_action(Cur, MaybeIoAction, !IO),
|
|
(
|
|
MaybeIoAction = tabled(_),
|
|
NewPrevUntabled = PrevUntabled,
|
|
NewPrevTabled = PrevTabled + 1
|
|
;
|
|
MaybeIoAction = untabled,
|
|
NewPrevUntabled = PrevUntabled + 1,
|
|
NewPrevTabled = PrevTabled
|
|
),
|
|
count_tabled_io_actions_2(Cur + 1, End,
|
|
NewPrevTabled, Tabled, NewPrevUntabled, Untabled, !IO)
|
|
).
|
|
|
|
:- pred trace_atom_arg_to_univ(trace_atom_arg::in, univ::out) is det.
|
|
|
|
trace_atom_arg_to_univ(TraceAtomArg, Univ) :-
|
|
MaybeUniv = TraceAtomArg ^ arg_value,
|
|
(
|
|
MaybeUniv = yes(Rep),
|
|
term_rep.rep_to_univ(Rep, Univ)
|
|
;
|
|
MaybeUniv = no,
|
|
Univ = univ('_' `with_type` unbound)
|
|
).
|
|
|
|
:- pred write_io_actions(user_state::in, int::in, io_action_range::in,
|
|
io::di, io::uo) is cc_multi.
|
|
|
|
write_io_actions(User, NumTabled, IoActions, !IO) :-
|
|
( NumTabled = 0 ->
|
|
true
|
|
;
|
|
( NumTabled = 1 ->
|
|
io.write_string(User ^ outstr, "1 tabled IO action:",
|
|
!IO)
|
|
;
|
|
io.write_int(User ^ outstr, NumTabled, !IO),
|
|
io.write_string(User ^ outstr, " tabled IO actions:",
|
|
!IO)
|
|
),
|
|
NumPrinted = get_num_printed_io_actions(User ^ browser),
|
|
( NumTabled =< NumPrinted ->
|
|
io.nl(User ^ outstr, !IO),
|
|
print_tabled_io_actions(User, IoActions, !IO)
|
|
;
|
|
io.write_string(User ^ outstr, " too many to show",
|
|
!IO),
|
|
io.nl(User ^ outstr, !IO)
|
|
)
|
|
).
|
|
|
|
:- pred print_tabled_io_actions(user_state::in, io_action_range::in,
|
|
io::di, io::uo) is cc_multi.
|
|
|
|
print_tabled_io_actions(User, IoActions, !IO) :-
|
|
IoActions = io_action_range(Start, End),
|
|
print_tabled_io_actions_2(User, Start, End, !IO).
|
|
|
|
:- pred print_tabled_io_actions_2(user_state::in,
|
|
io_seq_num::in, io_seq_num::in, io::di, io::uo) is cc_multi.
|
|
|
|
print_tabled_io_actions_2(User, Cur, End, !IO) :-
|
|
( Cur = End ->
|
|
true
|
|
;
|
|
get_maybe_io_action(Cur, MaybeIoAction, !IO),
|
|
print_tabled_io_action(User, MaybeIoAction, !IO),
|
|
print_tabled_io_actions_2(User, Cur + 1, End, !IO)
|
|
).
|
|
|
|
:- pred print_tabled_io_action(user_state::in, maybe_tabled_io_action::in,
|
|
io::di, io::uo) is cc_multi.
|
|
|
|
print_tabled_io_action(_, untabled, !IO).
|
|
print_tabled_io_action(User, tabled(IoAction), !IO) :-
|
|
Term = io_action_to_browser_term(IoAction),
|
|
browse.print_browser_term(Term, User ^ outstr, print_all,
|
|
User ^ browser, !IO).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
get_browser_state(User) = User ^ browser.
|
|
|
|
set_browser_state(Browser, !User) :-
|
|
!:User = !.User ^ browser := Browser.
|
|
|
|
get_user_output_stream(User) = User ^ outstr.
|
|
|
|
set_user_testing_flag(Testing, User, User ^ testing := Testing).
|
|
|
|
%-----------------------------------------------------------------------------%
|