/* Set flags to control Prolog's verbosity */
:- set_prolog_flag(verbose_autoload, false). % Disables verbose messages during autoload
:- set_prolog_flag(verbose, silent). % Sets verbose mode to silent globally
:- set_prolog_flag(verbose_load, silent). % Suppresses load messages
/* Ensure the 'logicmoo_utils' library is loaded */
:- ensure_loaded(library(logicmoo_utils)).
/* Assert that no '$exported_op' is exported for 'user' */
:- assert((user:'$exported_op'(_,_,_):- fail)).
/* Abolish any existing definitions of 'system:$exported_op/3' */
:- abolish((system:'$exported_op'/3)).
/* Assert that no '$exported_op' is exported for 'system' */
:- assert((system:'$exported_op'(_,_,_):- fail)).
/* Conditional loading of 'logicmoo_utils' if it exists */
:- if(exists_source(library(logicmoo_utils))).
:- ensure_loaded(library(logicmoo_utils)).
:- endif.
/* Check if 'dictoo' library exists, but the loading is skipped for now */
/* previously: the dictoo library was loaded here, but it's commented out */
:- if(exists_source(library(dictoo))).
%:- ensure_loaded(library(dictoo)).
:- endif.
/* The predicate 'done_once/1' is declared dynamic so it can be modified at runtime */
:- dynamic(done_once/1).
/**
* do_once(+G)
* Executes goal G only once. If it has been done before, it does not re-execute.
* It uses the dynamic predicate 'done_once/1' to keep track of goals that have been executed.
*
* @param G The goal to execute only once.
*
* @example
* ?- do_once(writeln('Hello')).
* Hello
*/
do_once(G):-
((done_once(GG), GG=@=G) -> true % If G has already been executed, do nothing
;(assert(done_once(G)), % Otherwise, assert that G has been executed
(once(@(G,user))->true; % Try to execute G in the user module, and succeed if possible
retract(done_once(G))))). % If execution fails, retract the assertion
/**
* cleanup_debug
* This predicate removes redundant debug clauses.
* It searches for duplicate clauses in the 'prolog_debug' module and erases redundant ones.
*/
cleanup_debug:-
forall(
(clause(prolog_debug:debugging(A1,B,C),Body,Cl1), % Find a clause in prolog_debug with head A1
clause(prolog_debug:debugging(A2,B,C),Body,Cl2), % Find another clause with same head A2 and body
A1=@=A2, Cl1\==Cl2), % Check if the clauses are different
erase(Cl2)). % Erase the second clause
/* Export the 'plain_var/1' predicate for external use */
:- export(plain_var/1).
/**
* plain_var(+V)
* Checks if V is a "plain" variable, meaning it is not an attributed variable
* and doesn't have any attributes related to the 'ci' attribute.
*
* @param V The variable to check.
*
* @example
* ?- plain_var(X).
* true.
*/
plain_var(V):-
notrace((var(V), \+ attvar(V), \+ get_attr(V,ci,_))). % Check if V is a plain Prolog variable without attributes
/**
* catch_nolog(+G)
* Safely executes a goal G, ignoring any errors that may occur.
* Any error messages are suppressed using 'notrace' to avoid logging.
*
* @param G The goal to execute.
*/
catch_nolog(G):-
ignore(catch(notrace(G), E, once(true; nop(u_dmsg(E=G))))). % Catch exceptions but suppress them
/**
* catch_log(+G)
* Executes a goal G and logs any errors that occur.
*
* @param G The goal to execute.
*/
catch_log(G):-
ignore(catch((G), E, ((u_dmsg(E=G), ugtrace(G))))). % Catch exceptions and log them
/* previously:
- catch_log was catching errors, writing them, and calling 'catch_nolog'.
- This was modified to call 'u_dmsg' instead.
*/
/**
* get_user_error(-UE)
* Retrieves the stream associated with the user error output.
*
* @param UE The user error stream.
*/
get_user_error(UE):-
stream_property(UE, file_no(2)),!. % Check if the stream refers to file descriptor 2 (stderr)
get_user_error(UE):-
stream_property(UE, alias(user_error)),!. % Alternatively, check if it has the alias 'user_error'
/* previously:
- Different mechanisms to identify the user error stream, such as file_no and alias, were tried.
*/
/**
* ufmt(+G)
* Formats and prints a goal G using 'ufmt0' or falls back to printing it with 'writeln'.
*
* @param G The goal to format and print.
*/
ufmt(G):-
notrace((fbug(G) -> true ; ufmt0(G))). % First, check if fbug can handle G; if not, call ufmt0
ufmt0(G):-
fmt(G) -> true ; writeln(G). % Attempt to format G, otherwise fall back to writeln
/**
* u_dmsg(+M)
* Sends a debug message to the appropriate output.
* This predicate ensures that messages are routed properly to 'user_error' or the default output.
*
* @param M The message to send.
*/
u_dmsg(M):-
is_list(M), !, my_maplist(u_dmsg, M). % If M is a list, recursively call u_dmsg on each element
u_dmsg(M):-
get_user_error(UE), \+ current_predicate(with_toplevel_pp/2), !,
with_output_to(UE, ufmt(M)). % If 'with_toplevel_pp/2' is not available, output to user error
/* previously: The method of printing debug messages has been extended to handle lists and direct output. */
/* Declaring multifile and dynamic predicates */
:- multifile(is_cgi/0).
:- dynamic(is_cgi/0).
:- multifile(arc_html/0).
:- dynamic(arc_html/0).
/**
* logicmoo_use_swish
* Initializes the SWISH web interface for LogicMoo and starts necessary services.
*/
logicmoo_use_swish:-
set_prolog_flag(use_arc_swish, true), % Enable SWISH usage
ld_logicmoo_webui, % Load the LogicMoo web UI
call(call, webui_start_swish_and_clio), % Start the SWISH and Clio services
http_handler('/swish', http_redirect(moved, '/swish/'), []). % Redirect SWISH handler
/* arc_user is used to determine the current user in various contexts */
/**
* arc_user(+Nonvar)
* Retrieves the current user or binds a variable to it.
*
* @param Nonvar The variable to bind to the user ID.
*/
arc_user(Nonvar):-
nonvar(Nonvar), !, arc_user(Var), !, Nonvar=Var. % If Nonvar is already bound, resolve it
arc_user(main):-
main_thread, !. % If in the main thread, identify the user as 'main'
arc_user(ID):-
catch((pengine:pengine_user(ID)), _, fail), !. % Retrieve user ID via pengine if possible
arc_user(ID):-
catch((xlisting_web:is_cgi_stream,
xlisting_web:find_http_session(User),
http_session:session_data(User, username(ID))), _, fail), !. % Try finding user from an HTTP session
arc_user(ID):-
catch((is_cgi, (xlisting_web:find_http_session(ID))), _, fail), !. % Fallback to another session method
arc_user(ID):-
is_cgi, !, ID=web_user. % If running as CGI, user is 'web_user'
arc_user(ID):-
thread_self(ID). % Otherwise, default to the current thread ID as the user
/* Define the dynamic predicate 'arc_user_prop/3' */
:- dynamic(arc_user_prop/3).
/* previously: luser_setval used nb_setval unconditionally, but now uses arc_user context */
luser_setval(N, V):-
arc_user(ID), luser_setval(ID, N, V), !.
/**
* luser_setval(+ID, +N, +V)
* Sets a user property for a specific user.
* It first checks if N and V are valid "arc sensical" terms, then sets the value.
*
* @param ID The user ID.
* @param N The property name.
* @param V The property value.
*/
luser_setval(ID, N, V):-
\+ (arc_sensical_term(N), arc_sensical_term(V)),
warn_skip(not_arc_sensical_term(luser_setval(ID, N, V))).
luser_setval(ID, N, V):-
(atom(N) -> nb_setval(N, V); true), % Set the property value for atom keys
retractall(arc_user_prop(ID, N, _)), % Remove any old properties for this user
asserta(arc_user_prop(ID, N, V)). % Assert the new property
% Predicate to unset a value for a user by its name `N`.
% @param N The name of the value to be unset.
luser_unsetval(N):-
% Ignore if there is no such value in the non-backtrackable storage
ignore(nb_delete(N)),
% Get the current user ID and unset the value for the user
arc_user(ID),
% Call `luser_unsetval/2` to unset the value for the specific user and name
luser_unsetval(ID,N),
% Cut to stop backtracking once successful
!.
% Predicate to unset a value for a specific user ID and name `N`.
% @param ID The user ID.
% @param N The name of the value to be unset.
luser_unsetval(ID,N):-
% Retract all properties associated with the user and the specific value
retractall(arc_user_prop(ID,N,_)).
% Predicate to set a default value for a user globally.
% @param N The name of the value.
% @param V The value to be set.
set_luser_default(N,V):-
% Set the value globally for the user
luser_setval(global,N,V).
% Predicate to get or set a default value for a user.
% @param N The name of the value.
% @param V The value to be retrieved or set.
luser_default(N,V):-
% If the value `V` is unbound (variable), get the value.
var(V),!,
luser_getval(N,V).
luser_default(N,V):-
% Otherwise, set the default value.
set_luser_default(N,V).
% Predicate to link a value to the current user or a specific ID.
% @param N The name of the value.
% @param V The value to be linked.
luser_linkval(N,V):-
% Get the current user ID and link the value
arc_user(ID),
luser_linkval(ID,N,V),
% Cut to stop backtracking
!.
% Predicate to link a value for a specific user ID.
% Skips linking if `V` is a sensical term and traces a warning if not.
% @param ID The user ID.
% @param N The name of the value.
% @param V The value to be linked.
luser_linkval(ID,N,V):-
% Ensure the value `V` is not a variable and both `N` and `V` are not "sensical terms"
\+ var(V), \+ (arc_sensical_term(N), arc_sensical_term(V)),
% Trace and skip execution if terms are not sensical
trace,
warn_skip(not_arc_sensical_term(luser_linkval(ID,N,V))).
% Predicate to link a value for a user and store it persistently.
% @param ID The user ID.
% @param N The name of the value.
% @param V The value to be linked.
luser_linkval(ID,N,V):-
% If the name `N` is an atom, use `nb_linkval` to store the value persistently
(atom(N) -> nb_linkval(N,V); true),
% Remove any existing property associated with the user and the value `N`
retractall(arc_user_prop(ID,N,_)),
% Assert the new property for the user and value `N`
asserta(arc_user_prop(ID,N,V)).
% Predicate to check if a term is "sensical", meaning it is neither empty nor a special case.
% @param O The term to be checked.
arc_sensical_term(O):-
% Ensure the term `O` is not a variable and is not an empty list, string, or specific ignored structures
nonvar(O), O \== [], O \== '', O \= (_ - _), O \== end_of_file.
% Predicate to check if a term is sensical and return it in the second argument.
% @param V The term to check.
% @param O The output term.
arc_sensical_term(V,O):-
% Reuse the `arc_sensical_term/1` check and unify the output
arc_sensical_term(V), !, O = V.
% Option predicate, used for configuration.
% Currently disabled and returns failure.
% @example arc_option(grid_size_only) fails.
%arc_option(grid_size_only):- !,fail.
% Option predicate to check if an option is set.
% @param O The option name.
arc_option(O):-
% Get the value associated with the option `O`
luser_getval(O,t).
% Conditional execution if an arc option is set.
% @param O The option to check.
% @param G The goal to execute if the option is set.
if_arc_option(O,G):-
% If the option `O` is set, execute `G`; otherwise, do nothing
(arc_option(O) -> must_det_ll(G); true).
% Execute a goal while temporarily changing a user's value.
% @param N The name of the value to be changed.
% @param V The new value to be set.
% @param Goal The goal to execute with the changed value.
with_luser(N,V,Goal):-
% Get the current value of `N` for the user or default to an empty list
(luser_getval(N,OV); OV = []),
% Set up the environment, execute the goal, and clean up by restoring the original value
setup_call_cleanup(
luser_setval(N,V),
once(Goal),
luser_setval(N,OV)
).
% Old predicate for getting a value. Commented out in favor of a newer version.
%luser_getval(N,V):- nb_current(N,VVV),arc_sensical_term(VVV,VV),!,V=VV.
% Predicate to get a user's value. Uses caching.
% @param N The name of the value.
% @param V The retrieved value.
luser_getval(N,V):-
% Get the initial value and ensure it is sensical
luser_getval_0(N,VV),
VV = V,
arc_sensical_term(V),
!.
% Predicate to get the value of `arc_user`.
% @param arc_user The name of the value to get.
luser_getval_0(arc_user,V):-
% Get the current user ID
arc_user(V).
% Predicate to retrieve a value using the first available method.
% @param N The name of the value.
% @param V The retrieved value.
luser_getval_0(N,V):-
luser_getval_1(N,V).
% First attempt to retrieve a value using method 1.
luser_getval_1(N,V):-
luser_getval_2(N,V).
% Fall back to method 2 if method 1 fails.
luser_getval_1(N,V):-
luser_getval_3(N,V),
\+ (luser_getval_2(N,VV), nop(VV \= V)).
% Continue to attempt to retrieve the value from defaults or other methods.
luser_getval_1(N,V):-
get_luser_default(N,V),
\+ (luser_getval_3(N,VV), nop(VV \= V)),
\+ (luser_getval_2(N,VV), nop(VV \= V)).
% Older predicates commented out for now.
% previously: luser_getval_0(N,V):- luser_getval_2(N,V), \+ luser_getval_1(N,_).
% previously: luser_getval_0(N,V):- luser_getval_3(N,V), \+ luser_getval_2(N,_), \+ luser_getval_1(N,_).
% Fetch value from HTTP request parameters when not on the main thread.
luser_getval_2(N,V):-
\+ main_thread,
atom(N),
httpd_wrapper:http_current_request(Request),
member(search(List), Request),
member(N = VV, List),
url_decode_term(VV, V),
arc_sensical_term(V),
!.
% Retrieve value from the non-backtrackable storage if available.
luser_getval_2(N,V):-
atom(N),
nb_current(N, ValV),
arc_sensical_term(ValV, Val),
Val = V.
% Retrieve value from user properties.
luser_getval_3(N,V):-
arc_user(ID),
arc_user_prop(ID, N, V).
% Return failure if not in CGI context.
luser_getval_3(_,_):-
\+ is_cgi,
!,
fail.
% Retrieve value from session parameters in a non-main thread.
luser_getval_3(N,V):-
\+ main_thread,
atom(N),
current_predicate(get_param_sess/2),
get_param_sess(N, M),
url_decode_term(M, V),
arc_sensical_term(V).
% Fetch user defaults if available.
get_luser_default(N,V):-
arc_user_prop(global, N, VV),
VV = V,
arc_sensical_term(V),
!.
% Fetch defaults from Prolog flags if necessary.
get_luser_default(N,V):-
atom(N),
current_prolog_flag(N, VV),
VV = V,
arc_sensical_term(V),
!.
% Previously skipped older method of fetching values.
% previously: luser_getval(ID,N,V):- thread_self(ID),nb_current(N,V),!.
% Skip previously used fallback mechanism.
% previously: luser_getval(ID,N,V):- !, ((arc_user_prop(ID,N,V);nb_current(N,V))*->true;arc_user_prop(global,N,V)).
% Main predicate that checks if the current thread is the main thread.
ansi_main:-
thread_self(main),
nop(is_cgi),
!.
% Predicate to check if the current thread is the main thread.
main_thread:-
thread_self(main),
!.
% Conditionally execute a goal if in the main thread.
if_thread_main(G):-
main_thread -> call(G); true.
% File directive to ensure `fbug/1` is only defined once.
:- if(\+ current_predicate(fbug/1)).
%fbug(P):- format(user_error,'~N~p~n',[P]).
:- endif.
substM(T, F, R, R) :- T==F, !. % If the term is exactly the one to replace, return the replacement.
substM(T, _, _, R) :- \+ compound(T), !, R = T. % If it's not a compound term, no replacement is needed.
substM([H1|T1], F, R, [H2|T2]) :- % If it's a list, recursively substitute in the head and tail.
!, substM(H1, F, R, H2), substM(T1, F, R, T2).
substM(C1, F, R, C2) :- % If it's a compound term, decompose and substitute in the arguments.
C1 =.. [Fn|A1], substM_l(A1, F, R, A2), !, C2 =.. [Fn|A2].
/* Helper predicate: Substitute within a list of arguments */
substM_l([], _, _, []). % Base case: empty list.
substM_l([H1|T1], F, R, [H2|T2]) :- % Recursive case: substitute in head and tail of the list.
substM(H1, F, R, H2), substM_l(T1, F, R, T2).
/**
* @predicate pp_m(+Clause)
* Pretty-print the clause.
* This predicate writes the clause `Cl` to the source in a formatted way.
*
* @param Clause The Prolog clause to pretty-print.
*/
pp_m(Cl) :- write_src(Cl), !. % Calls a helper to write the clause to the source.
pp_m(C, Cl) :- color_g_mesg(C, write_src(Cl)), !. % Writes the clause with some colored messages.
/* previously: unused code for tracing, now commented out for clarity */
% notrace((format('~N'), ignore(( \+ ((numbervars(Cl,0,_,[singletons(true)]), print_tree_with_final(Cl,"."))))))).
/**
* @predicate pp_q(+Clause)
* Pretty-print the clause for queries.
* Outputs the clause with query-specific formatting and ensures no singleton variables.
*
* @param Clause The Prolog clause to pretty-print.
*/
pp_q(Cl) :-
notrace((format('~N'), ignore(( \+ ((numbervars(Cl,0,_,[singletons(true)]), print_tree_with_final(Cl,"."))))))).
/**
* @predicate ncatch(+Goal, +Error, +Handler)
* Wrapper around catch/3 for error handling.
* This predicate catches exceptions and handles them with `Handler`.
*
* @param Goal The goal to execute.
* @param Error The error to catch.
* @param Handler The handler for the caught error.
*/
ncatch(G, E, F) :- catch(G, E, F).
/**
* @predicate mcatch(+Goal, +Error, +Handler)
* Another wrapper for catch/3, used for modular error handling.
*
* @param Goal The goal to execute.
* @param Error The error to catch.
* @param Handler The handler for the caught error.
*/
mcatch(G, E, F) :- catch(G, E, F).
/* previously: error handling was extended with debugging hooks, removed for clarity */
%mcatch(G,E,F):- catch(G,E,(fbug(G=E),catch(bt,_,fail),fbug(G=E),ignore(call(F)),throw(E))).
/* Directive: Conditional inclusion if predicate `if_t/2` does not exist */
:- if(\+ current_predicate(if_t/2)).
:- meta_predicate(if_t(0, 0)).
/**
* @predicate if_t(+IfGoal, +ThenGoal)
* Conditional execution of Prolog goals.
* If `IfGoal` succeeds, `ThenGoal` is executed.
*
* @param IfGoal The condition goal.
* @param ThenGoal The goal to execute if `IfGoal` succeeds.
*/
if_t(IF, THEN) :- call(call, ignore((IF, THEN))).
:- endif.
/* Directive: Conditional inclusion if predicate `must_ll/1` does not exist */
:- if(\+ current_predicate(must_ll/1)).
:- meta_predicate(must_ll(0)).
/**
* @predicate must_ll(+Goal)
* Ensures that the goal succeeds at least once.
* If the goal fails, it throws an error.
*
* @param Goal The goal to execute.
*/
must_ll(G) :- md(call, G) *-> true ; throw(not_at_least_once(G)).
:- endif.
/* Directive: Conditional inclusion if predicate `at_least_once/1` does not exist */
:- if(\+ current_predicate(at_least_once/1)).
:- meta_predicate(at_least_once(0)).
/**
* @predicate at_least_once(+Goal)
* Ensures that the goal succeeds at least once, throwing an error if it fails.
*
* @param Goal The goal to execute.
*/
at_least_once(G) :- call(G) *-> true ; throw(not_at_least_once(G)).
:- endif.
/* previously: wrapping mechanisms were used, preserved for future refactoring */
%wraps_each(must_ll,call).
/* Skipping `remove_must_det/1` because its functionality is now disabled */
remove_must_det(_) :- !, fail. % Always fails as it's not intended for use anymore.
/* Skipped code for conditionally removing `must_det_ll` calls, commented for clarity */
%remove_must_det(MD):- !.
%remove_must_det(MD):- nb_current(remove_must_det(MD),TF),!,TF==true.
/* This block handles removal of specific terms in compound structures.
However, it's not currently in use, but preserved for future needs */
remove_mds(MD, GG, GO) :-
sub_term(G, GG), compound(G), compound_name_arg(G, MD, GGGG),
subst001(GG, G, GGGG, GGG), remove_mds(MD, GGG, GO).
remove_mds(_, GG, GG).
/**
* @predicate never_rrtrace/0
* Prevent tracing if in CGI environment.
* Ensures that tracing doesn't interfere when in CGI mode.
*/
never_rrtrace :- nb_current(cant_rrtrace, t), !, notrace.
never_rrtrace :- is_cgi, notrace.
/* Skipped tracing directives, preserved for potential debugging reactivation */
%itrace:- !.
%itrace:- \+ current_prolog_flag(debug,true),!.
/* Trace the main thread, used for debugging */
itrace :- if_thread_main(trace), !.
ibreak :- if_thread_main(((trace, break))).
/* Catch an argument by position, with error handling */
tc_arg(N, C, E) :-
catch(arg(N, C, E), Err,
/* previously: detailed error reporting for debugging, now simplified */
(bt, fbug(tc_arg(N, C, E) = Err),
((tracing -> true ; trace), break, arg(N, C, E)))).
/**
* @predicate compound_name_arg(+Compound, +Name, -Arg)
* Extract the argument of a compound term.
* If the term is not instantiated, it unifies `Compound` with the functor `Name` and argument `Arg`.
*
* @param Compound A compound term or variable.
* @param Name The functor name.
* @param Arg The argument to extract.
*/
compound_name_arg(G, MD, Goal) :- var(G), !, atom(MD), G =.. [MD, Goal]. % Case: G is uninstantiated.
compound_name_arg(G, MD, Goal) :- compound(G), !, compound_name_arguments(G, MD, [Goal]). % Case: G is a compound term.
/* File directives for handling user-defined message hooks */
:- multifile(user:message_hook/3).
:- dynamic(user:message_hook/3).
/* Disabled error hook for debugging, can be re-enabled for more granular error handling */
%user:message_hook(Term, Kind, Lines):- error==Kind, itrace,fbug(user:message_hook(Term, Kind, Lines)),trace,fail.
/**
* @predicate user:message_hook(+Term, +Kind, +Lines)
* Custom handler for messages.
* Can be used to intercept and process specific message types, but currently fails on all messages.
*
* @param Term The message term.
* @param Kind The kind of message (e.g., error, warning).
* @param Lines The message content.
*/
user:message_hook(Term, Kind, Lines) :-
fail, error == Kind. % Always fails, not currently used.
fbug(message_hook(Term, Kind, Lines)),fail.
% PLDoc header for meta_predicates
/**
* must_det_ll(:Goal) is det.
*
* This predicate ensures that Goal is deterministic and that no choice points
* are left after it succeeds. It is a meta-predicate as it operates on other goals.
*
* @param Goal The goal to be executed deterministically.
*/
:- meta_predicate(must_det_ll(0)).
/**
* must_det_ll1(:P1, :Goal) is det.
*
* This predicate is similar to `must_det_ll/1` but uses an additional predicate P1
* during its execution.
*
* @param P1 A predicate used as a helper in execution.
* @param Goal The goal to be executed.
*/
:- meta_predicate(must_det_ll1(1, 0)).
/**
* md_failed(:P1, :Goal) is det.
*
* This predicate handles the failure of a goal within a meta-predicate context.
* It executes P1 and then Goal, managing failure appropriately.
*
* @param P1 The first predicate to be executed.
* @param Goal The goal that might fail.
*/
:- meta_predicate(md_failed(1, 0)).
/**
* must_not_error(:Goal) is det.
*
* Ensures that the given Goal does not raise an error. It catches errors and
* prevents them from propagating.
*
* @param Goal The goal that should not result in an error.
*/
:- meta_predicate(must_not_error(0)).
% This line is commented out because the predicate was likely removed or deprecated.
% % :- meta_predicate(must_det_l(0)).
/* previously:
% This flag was disabled. Uncommenting this line would prevent certain debugging features.
% :- no_xdbg_flags.
*/
/**
* wno_must(:Goal) is det.
*
* Runs the given goal but with certain debugging and tracing flags disabled.
*
* @param Goal The goal to execute with modified local settings.
*/
:- meta_predicate(wno_must(0)).
% Set no_must_det_ll and cant_rrtrace flags locally, then call the goal G.
wno_must(G) :-
locally(nb_setval(no_must_det_ll, t), % Disable must_det_ll checks locally
locally(nb_setval(cant_rrtrace, t), % Disable rrtrace locally
call(G))). % Call the goal
% PLDoc header for md_maplist/3
/**
* md_maplist(:MD, :P1, +List) is det.
*
* This predicate maps the meta-predicate MD over a list, applying P1 to each element.
*
* @param MD The meta-predicate to apply to each element of the list.
* @param P1 A predicate to be applied to each element of the list.
* @param List The list of elements to process.
*/
md_maplist(_MD, _, []) :-
% Base case: empty list, do nothing.
!.
% Recursive case: Apply MD and P1 to the head of the list, then recurse on the tail.
md_maplist(MD, P1, [H | T]) :-
call(MD, call(P1, H)), % Apply MD and P1 to the head element
md_maplist(MD, P1, T). % Recur on the tail
% PLDoc header for md_maplist/4
/**
* md_maplist(:MD, :P2, +ListA, +ListB) is det.
*
* Maps a predicate over two lists element-wise.
*
* @param MD The meta-predicate to apply to each pair of elements.
* @param P2 A predicate to be applied to each pair of elements.
* @param ListA The first list.
* @param ListB The second list.
*/
md_maplist(_MD, _, [], []) :-
% Base case: both lists are empty.
!.
% Recursive case: Apply MD and P2 to the heads of the lists, then recurse.
md_maplist(MD, P2, [HA | TA], [HB | TB]) :-
call(MD, call(P2, HA, HB)), % Apply MD and P2 to the head elements of both lists
md_maplist(MD, P2, TA, TB). % Recur on the tails
% PLDoc header for md_maplist/5
/**
* md_maplist(:MD, :P3, +ListA, +ListB, +ListC) is det.
*
* Maps a predicate over three lists element-wise.
*
* @param MD The meta-predicate to apply to each triplet of elements.
* @param P3 A predicate to be applied to each triplet of elements.
* @param ListA The first list.
* @param ListB The second list.
* @param ListC The third list.
*/
md_maplist(_MD, _, [], [], []) :-
% Base case: all three lists are empty.
!.
% Recursive case: Apply MD and P3 to the heads of the lists, then recurse.
md_maplist(MD, P3, [HA | TA], [HB | TB], [HC | TC]) :-
call(MD, call(P3, HA, HB, HC)), % Apply MD and P3 to the head elements of all three lists
md_maplist(MD, P3, TA, TB, TC). % Recur on the tails of the lists
% The following code is commented out as it was likely part of a debugging process that is no longer needed.
% % must_det_ll(G):- !, once((/*notrace*/(G)*->true;md_failed(P1,G))).
/* previously:
% This was an old directive to check if the must_det_ll/1 predicate exists before defining it.
% It was removed because the condition is either unnecessary or handled elsewhere.
% :- if( \+ current_predicate(must_det_ll/1)).
*/
% If tracing is active, run X once and ensure it's deterministic.
must_det_ll(X) :-
tracing,
!,
once(X).
% Otherwise, call md/2 to execute X within the context of the meta-predicate.
must_det_ll(X) :-
md(once, X).
/* previously:
% This directive ensured that the predicate definition only occurred once.
% It has been removed as the condition is no longer relevant.
% :- endif.
*/
md(P1,G):- tracing,!, call(P1,G). % once((call(G)*->true;md_failed(P1,G))).
md(P1,G):- remove_must_det(MD), wraps_each(MD,P1),!,call(G).
md(P1,G):- never_rrtrace,!, call(P1,G).
md(P1,G):- /*notrace*/(arc_html),!, ignore(/*notrace*/(call(P1,G))),!.
%md(P1,X):- !,must_not_error(X).
md(P1,(X,Goal)):- is_trace_call(X),!,call((itrace,call(P1,Goal))).
md(_, X):- is_trace_call(X),!,itrace.
md(P1, X):- nb_current(no_must_det_ll,t),!,call(P1,X).
md(P1,X):- \+ callable(X), !, throw(md_not_callable(P1,X)).
md(P1,(A*->X;Y)):- !,(must_not_error(A)*->md(P1,X);md(P1,Y)).
md(P1,(A->X;Y)):- !,(must_not_error(A)->md(P1,X);md(P1,Y)).
md(P1,(X,Cut)):- (Cut==(!)),md(P1,X),!.
md(MD,maplist(P1,List)):- !, call(MD,md_maplist(MD,P1,List)).
md(MD,maplist(P2,ListA,ListB)):- !, call(MD,md_maplist(MD,P2,ListA,ListB)).
md(MD,maplist(P3,ListA,ListB,ListC)):- !, call(MD,md_maplist(MD,P3,ListA,ListB,ListC)).
md(P1,(X,Cut,Y)):- (Cut==(!)), !, (md(P1,X),!,md(P1,Y)).
md(P1,(X,Y)):- !, (md(P1,X),md(P1,Y)).
%md(P1,X):- /*notrace*/(ncatch(X,_,fail)),!.
%md(P1,X):- conjuncts_to_list(X,List),List\=[_],!,maplist(must_det_ll,List).
md(_,must_det_ll(X)):- !, must_det_ll(X).
md(_,grid_call(P2,I,O)):- !, must_grid_call(P2,I,O).
%md(P1,call(P2,I,O)):- !, must_grid_call(P2,I,O).
%md(P1,(X,Y,Z)):- !, (md(P1,X)->md(P1,Y)->md(P1,Z)).
%md(P1,(X,Y)):- !, (md(P1,X)->md(P1,Y)).
%md(P1,if_t(X,Y)):- !, if_t(must_not_error(X),md(P1,Y)).
md(P1,forall(X,Y)):- !, md(P1,forall(must_not_error(X),must_not_error(Y))).
md(P1,\+ (X, \+ Y)):- !, md(P1,forall(must_not_error(X),must_not_error(Y))).
md(P1,(X;Y)):- !, ((must_not_error(X);must_not_error(Y))->true;md_failed(P1,X;Y)).
md(P1,\+ (X)):- !, (\+ must_not_error(X) -> true ; md_failed(P1,\+ X)).
%md(P1,(M:Y)):- nonvar(M), !, M:md(P1,Y).
md(P1,X):-
ncatch(must_det_ll1(P1,X),
md_failed(P1,G,N), % <- ExceptionTerm
% bubble up and start running
((M is N -1, M>0)->throw(md_failed(P1,G,M));(ugtrace(md_failed(P1,G,M),X),throw('$aborted')))),!.
%must_det_ll(X):- must_det_ll1(P1,X),!.
/*
This directive enables the 'tracing' mode for debugging,
ensuring that errors are caught and handled appropriately.
*/
/**
* must_det_ll1/2
* This predicate ensures the given goal P1 is executed deterministically with trace handling.
* It handles different forms of calls, ensuring no errors during execution.
*
* @param P1 The goal to execute.
* @param X The argument that can be passed directly or wrapped in 'once' or other calls.
* @example
* ?- must_det_ll1(my_goal, my_arg).
*/
must_det_ll1(P1, X) :-
% Check if tracing is active, then execute the call with error handling
tracing, !, must_not_error(call(P1, X)), !.
must_det_ll1(P1, once(A)) :-
% When the second argument is wrapped in 'once', ensure it runs deterministically
!, once(md(P1, A)).
must_det_ll1(P1, X) :-
% Extract the module, functor, and arity from the second argument
strip_module(X, M, P), functor(P, F, A),
% Set up cleanup for tracing, ensuring no errors occur, or handling failure
setup_call_cleanup(nop(trace(M:F/A, +fail)),
(must_not_error(call(P1, X)) *-> true; md_failed(P1, X)),
nop(trace(M:F/A, -fail))), !.
/* previously: must_not_error(G) was using must(once(G)) but this was changed to allow for more flexible error handling. */
/**
* must_not_error/1
* Executes the given goal and ensures errors are handled correctly.
* Different cases handle tracing, CGI execution, and custom error logic.
*
* @param G The goal to execute.
*/
must_not_error(G) :-
% If tracing is active or rrtrace is not used, execute the goal directly
(tracing; never_rrtrace), !, call(G).
must_not_error(G) :-
% If running in a CGI environment, handle errors with custom messaging
notrace(is_cgi), !, ncatch((G), E, ((u_dmsg(E = G)))).
% Dead code: GUI tracer error handling skipped; the system may not need special GUI tracing behavior
% must_not_error(X):- is_guitracer, !, call(X).
must_not_error(X) :-
% Catch and handle any exceptions, tracing them for further debugging if needed
!, ncatch(X, E, (fbug(E = X), ugtrace(error(E), X))).
must_not_error(X) :-
% Trace exceptions and retry with visible trace enabled
ncatch(X, E, (rethrow_abort(E);
(writeq(E = X), pp(etrace = X), trace, rrtrace(visible_rtrace([-all, +exception]), X)))).
/**
* always_rethrow/1
* This predicate determines which exceptions should always be rethrown during execution.
*
* @param E The exception to check.
*/
always_rethrow('$aborted'). % Always rethrow when the process is aborted.
always_rethrow(md_failed(_, _, _)). % Rethrow if an internal failure occurs.
always_rethrow(return(_)). % Handle custom return exceptions.
always_rethrow(metta_return(_)). % Handle metta return exceptions.
always_rethrow(give_up(_)). % Give up on certain operations.
always_rethrow(time_limit_exceeded(_)). % Rethrow if time limit is exceeded.
always_rethrow(depth_limit_exceeded). % Handle depth limit exceptions.
always_rethrow(restart_reading). % Handle restart reading exceptions.
always_rethrow(E) :-
% If rrtrace is not used, throw the exception
never_rrtrace, !, throw(E).
/**
* catch_non_abort/1
* This predicate catches all exceptions except those that involve process aborts.
*
* @param Goal The goal to execute while catching non-abort exceptions.
*/
catch_non_abort(Goal) :-
% Catch non-abort exceptions, rethrow them if necessary
catch(cant_rrtrace(Goal), E, rethrow_abort(E)), !.
/**
* rethrow_abort/1
* This predicate handles rethrowing of certain exceptions.
*
* @param E The exception to rethrow.
*/
rethrow_abort(E) :-
% Log the exception and fail
format(user_error, '~N~q~n', [catch_non_abort_or_abort(E)]), fail.
% Skipped: Special handling for time limits, as it is not always necessary
% rethrow_abort(time_limit_exceeded) :- !.
rethrow_abort('$aborted') :-
% When '$aborted' is encountered, abort and log a timeout
!, throw('$aborted'), !, forall(between(1, 700, _), sleep(0.01)), writeln(timeout), !, fail.
rethrow_abort(E) :-
% Handle other exceptions, outputting an error message
ds, !, format(user_error, '~N~q~n', [catch_non_abort(E)]), !.
/**
* cant_rrtrace/1
* Executes the goal without rrtrace, or with rrtrace based on flags.
*
* @param Goal The goal to execute.
*/
cant_rrtrace(Goal) :-
% If rrtrace is disabled, just call the goal
never_rrtrace, !, call(Goal).
cant_rrtrace(Goal) :-
% Otherwise, execute with rrtrace cleanup
setup_call_cleanup(cant_rrtrace, Goal, can_rrtrace).
/**
* md_failed/2
* Handles failures during the execution of P1 with argument X.
* Logs the failure and determines whether to retry or handle it differently.
*
* @param P1 The predicate that failed.
* @param X The argument that failed.
*/
md_failed(P1, X) :-
% Log the failure without trace and fail
notrace((write_src_uo(failed(P1, X)), fail)).
md_failed(P1, X) :-
% If tracing is active, run with visible tracing
tracing, visible_rtrace([-all, +fail, +call, +exception], call(P1, X)).
md_failed(P1, X) :-
% Without tracing, log and run with visible trace
\+ tracing, !, visible_rtrace([-all, +fail, +exit, +call, +exception], call(P1, X)).
md_failed(P1, G) :-
% Handle failures in a CGI environment
is_cgi, \+ main_debug, !, u_dmsg(arc_html(md_failed(P1, G))), fail.
md_failed(_P1, G) :-
% If testing is enabled, output the failure and give up
option_value(testing, true), !,
T = 'FAILEDDDDDDDDDDDDDDDDDDDDDDDDDD!!!!!!!!!!!!!'(G),
write_src_uo(T), give_up(T, G).
md_failed(P1, G) :-
% If rrtrace is disabled, log and throw the failure
never_rrtrace, !, notrace, (u_dmsg(md_failed(P1, G))), !, throw(md_failed(P1, G, 2)).
md_failed(P1, X) :-
% Handle failure with GUI tracer
notrace, is_guitracer, u_dmsg(failed(X)), nortrace, atrace, call(P1, X).
md_failed(P1, G) :-
% Log failure in debugging mode and throw the failure
main_debug, (write_src_uo(md_failed(P1, G))), !, throw(md_failed(P1, G, 2)).
/**
* write_src_uo/1
* Outputs the source of a given goal to the appropriate stream.
*
* @param G The goal to output.
*/
write_src_uo(G):-
stream_property(S,file_no(1)),
with_output_to(S,
(format('~N~n~n',[]),
write_src(G),
format('~N~n~n'))),!,
%stack_dump,
stream_property(S2,file_no(2)),
with_output_to(S2,
(format('~N~n~n',[]),
write_src(G),
format('~N~n~n'))),!.
:- meta_predicate(rrtrace(0)).
rrtrace(X) :- rrtrace(etrace, X).
/* This predicate attempts to dump the stack if an error occurs.
The use of `ignore` ensures that if `bt` fails, no exception will be thrown,
and execution continues.
*/
stack_dump :- ignore(catch(bt, _, true)).
/* previously:
stack_dump attempted to also dump additional debugging information via `dumpST`
and `bts`, but these were commented out likely to avoid unnecessary output.
*/
/**
* ugtrace(+Why, +G)
* This predicate traces or logs the execution of the goal `G` based on `Why`.
* If an error (`error(Why)`) is encountered, it traces the stack.
*
* @param Why Reason for tracing.
* @param G Goal to be traced.
*/
ugtrace(error(Why), G) :-
!, % Cut to avoid further processing if error case is matched.
notrace, % Disable the tracer.
write_src_uo(Why), % Write the error information.
stack_dump, % Dump the stack to help debugging.
write_src_uo(Why), % Log the error again.
rtrace(G). % Trace the goal G.
ugtrace(Why, G) :-
tracing, !, % If tracing is active, proceed with tracing.
notrace, % Temporarily disable tracing.
write_src(Why), % Log the reason for tracing.
rtrace(G). % Perform a regular trace on the goal G.
/* If tracing is not active, handle the failure and possibly throw an exception. */
ugtrace(Why, _) :-
is_testing, !, % If running in testing mode, handle errors differently.
ignore(give_up(Why, 5)), % Attempt to give up after 5 retries.
throw('$aborted'). % Abort the goal execution.
ugtrace(_Why, G) :-
ggtrace(G), % Call the goal with ggtrace.
throw('$aborted'). % Abort after tracing.
/* previously:
The old implementation used `ggtrace` in cases not covered above,
but the code was commented out, possibly because it wasn't needed in
current use cases.
*/
/**
* give_up(+Why, +N)
* If running in testing mode, abort execution with a given reason.
*
* @param Why Reason for giving up.
* @param N Exit code to halt the process with.
*/
give_up(Why, _) :-
is_testing, !, % If in testing mode, log the reason and halt.
write_src_uo(Why),
throw(give_up(Why)).
give_up(Why, N) :-
is_testing, !, % If in testing mode, halt the process.
write_src_uo(Why),
halt(N).
give_up(Why, _) :-
write_src_uo(Why), % Log the reason for giving up.
throw('$aborted'). % Abort execution.
/**
* is_guitracer
* This predicate checks if the Prolog environment supports the GUI tracer.
* It does so by checking if the 'DISPLAY' environment variable is set
* and the `gui_tracer` flag is enabled.
*/
is_guitracer :-
getenv('DISPLAY', _),
current_prolog_flag(gui_tracer, true).
/* Directive to define `rrtrace/2` as a meta-predicate, where the first argument is
a predicate that will be applied to the second argument. */
:- meta_predicate(rrtrace(1, 0)).
/**
* rrtrace(+P1, +X)
* Handles tracing in various scenarios depending on system state (e.g., CGI mode).
*
* @param P1 Predicate to apply.
* @param X Goal to be traced.
*/
rrtrace(P1, X) :-
never_rrtrace, !, % If tracing should never occur, fail the predicate.
nop((u_dmsg(cant_rrtrace(P1, X)))), % Log the inability to trace.
fail. % Fail the goal.
rrtrace(P1, G) :-
is_cgi, !, % If running in CGI mode, log and call the predicate.
u_dmsg(arc_html(rrtrace(P1, G))),
call(P1, G).
/* If GUI tracing is not enabled, avoid GUI tracing altogether. */
rrtrace(P1, X) :-
notrace, \+ is_guitracer, !,
nortrace,
(notrace(\+ current_prolog_flag(gui_tracer, true))
-> call(P1, X)
; (itrace, call(P1, X))).
/* previously:
Another clause of `rrtrace/2` was removed because it involved GUI tracing,
which might not be supported in the current environment.
*/
rrtrace(P1, X) :-
itrace, !, % If interactive tracing is enabled, apply the trace.
call(P1, X).
/* Directive to define `arc_wote/1` as a meta-predicate. */
:- meta_predicate(arc_wote(0)).
/**
* arc_wote(+G)
* Executes goal `G` within the context of ANSI formatting (pretty printing).
*
* @param G Goal to be executed.
*/
arc_wote(G) :- with_pp(ansi, wote(G)).
/*
arcST enables tracing of backtrace stack and related debugging information.
*/
arcST :- itrace, arc_wote(bts), itrace.
/*
atrace is an alias to trigger arc_wote with backtrace stack functionality.
*/
atrace :- arc_wote(bts).
/* previously:
There was an alternative version of atrace that dumped debugging information
to file descriptor 2, but it was skipped, likely to avoid unnecessary output.
*/
/* Directive to define `odd_failure/1` as a meta-predicate. */
:- meta_predicate(odd_failure(0)).
/**
* odd_failure(+G)
* Executes the goal `G` and handles failure in a special manner.
*
* @param G Goal to be executed.
*/
odd_failure(G) :-
never_rrtrace, !,
call(G). % Simply call the goal if tracing is disabled.
odd_failure(G) :-
wno_must(G) *-> true ; fail_odd_failure(G). % Handle failure in a specific way if goal fails.
/* Directive to define `fail_odd_failure/1` as a meta-predicate. */
:- meta_predicate(fail_odd_failure(0)).
/**
* fail_odd_failure(+G)
* Logs and traces goal failures.
*
* @param G Goal that has failed.
*/
fail_odd_failure(G) :-
u_dmsg(odd_failure(G)),
rtrace(G),
fail. % Always fail after tracing.
/* previously:
A more complex failure handler existed, but it was simplified here.
*/
/**
* bts
* Loads the `prolog_stack` library and prints a detailed Prolog backtrace.
*/
bts :-
ensure_loaded(library(prolog_stack)), % Load the backtrace library.
prolog_stack:export(prolog_stack:get_prolog_backtrace_lc/3), % Export necessary predicates.
use_module(library(prolog_stack), [print_prolog_backtrace/2, get_prolog_backtrace_lc/3]), % Use stack-related predicates.
prolog_stack:call(call, get_prolog_backtrace_lc, 8000, Stack, [goal_depth(600)]), % Get the stack trace.
stream_property(S, file_no(1)), % Get the current stream properties.
prolog_stack:print_prolog_backtrace(S, Stack), % Print the stack trace to the stream.
ignore((fail, current_output(Out), \+ stream_property(Out, file_no(1)), print_prolog_backtrace(Out, Stack))), !. % Print to other output streams if available.
/**
* my_assertion(+G)
* Asserts that the goal `G` succeeds.
*
* @param G Goal to be asserted.
*/
my_assertion(G) :-
my_assertion(call(G), G).
my_assertion(_, G) :-
call(G), !. % Call the goal and succeed.
my_assertion(Why, G) :-
u_dmsg(my_assertion(Why, G)),
writeq(Why = goal(G)),
nl,
ibreak. % Break execution for debugging.
/**
* must_be_free(+Free)
* Ensures that the variable `Free` is free (unbound).
*
* @param Free Variable to check.
*/
must_be_free(Free) :-
plain_var(Free), !. % Check if the variable is unbound.
must_be_free(Free) :-
\+ nonvar_or_ci(Free), !. % Ensure it's not a concrete instance.
/* If the variable is bound, log the error and fail. */
must_be_free(Nonfree) :-
arcST,
u_dmsg(must_be_free(Nonfree)),
ignore((attvar(Nonfree), get_attrs(Nonfree, ATTS), pp(ATTS))),
ibreak,
fail.
/**
* must_be_nonvar(+Nonvar)
* Ensures that the variable `Nonvar` is bound (non-variable).
*
* @param Nonvar Variable to check.
*/
must_be_nonvar(Nonvar) :-
nonvar_or_ci(Nonvar), !. % Check if the variable is nonvar.
/* If the variable is free, log the error and fail. */
must_be_nonvar(IsVar) :-
arcST,
u_dmsg(must_be_nonvar(IsVar)),
ibreak,
fail.
/* previously: goal_expansion for handling must_det_l and must_det_ll was skipped
Explanation: The commented-out goal_expansion clauses were likely designed for transforming specific goals during compilation.
These clauses might have been skipped to avoid conflicts or unnecessary processing in the current context. */
/**
* get_setarg_p1(+P3, +E, +Cmpd, -SA) is det.
*
* Retrieves the argument from the compound term Cmpd and sets the argument using a predicate P3.
*
* @param P3 Predicate to set the argument.
* @param E The element to find within the compound term.
* @param Cmpd The compound term being processed.
* @param SA The result after applying the predicate P3 to the argument in Cmpd.
*
* @example
* ?- get_setarg_p1(setarg, member, some_term(member, data), SA).
*/
get_setarg_p1(P3, E, Cmpd, SA) :-
% Check if Cmpd is a compound term and call get_setarg_p2 to find and set the argument.
compound(Cmpd),
get_setarg_p2(P3, E, Cmpd, SA).
/**
* get_setarg_p2(+P3, +E, +Cmpd, -SA) is det.
*
* Retrieves the argument in Cmpd at position N1 and sets it using the predicate P3.
*
* @param P3 Predicate to set the argument.
* @param E The element to match within the compound term.
* @param Cmpd The compound term being processed.
* @param SA The result after applying the predicate P3 to the argument in Cmpd.
*/
get_setarg_p2(P3, E, Cmpd, SA) :-
% Find the argument position in the compound term and apply P3 to set the argument.
arg(N1, Cmpd, E),
SA = call(P3, N1, Cmpd).
get_setarg_p2(P3, E, Cmpd, SA) :-
% Recursively search for the argument in the compound term's arguments if the first attempt fails.
arg(_, Cmpd, Arg),
get_setarg_p1(P3, E, Arg, SA).
/**
* my_b_set_dict(+Member, +Obj, +Var) is det.
*
* Set the value of a member in an object using b_set_dict.
*
* @param Member The member key to set.
* @param Obj The object (e.g., dictionary) where the member resides.
* @param Var The value to set for the member.
*/
my_b_set_dict(Member, Obj, Var) :-
% Set the member's value in the object using the auxiliary set_omemberh predicate.
set_omemberh(b, Member, Obj, Var).
/**
* set_omemberh(+How, +Member, +Obj, +Var) is det.
*
* Handles setting member values in objects based on different modes (b, nb, link).
*
* @param How The method used for setting (e.g., 'b', 'nb').
* @param Member The member key to set.
* @param Obj The object where the member resides.
* @param Var The value to set for the member.
*/
set_omemberh(_, Member, Obj, Var) :-
% Use arc_setval to set the member's value in the object.
!, arc_setval(Obj, Member, Var).
/* previously: Alternative set_omemberh methods were commented out.
Explanation: Other methods such as nb_set_dict and nb_link_dict are specialized ways to set dictionary values without backtracking.
These might have been disabled to maintain consistency or because they were not needed in the current context. */
/**
* set_omember(+Member, +Obj, +Var) is det.
*
* Sets the value of a member in an object using a default mode.
*
* @param Member The member key to set.
* @param Obj The object where the member resides.
* @param Var The value to set for the member.
*/
set_omember(Member, Obj, Var) :-
% Use the default method 'b' to set the member value.
set_omember(b, Member, Obj, Var).
/**
* set_omember(+How, +Member, +Obj, +Var) is det.
*
* Sets the value of a member in an object with a specified method.
*
* @param How The method used for setting (e.g., 'b', 'nb').
* @param Member The member key to set.
* @param Obj The object where the member resides.
* @param Var The value to set for the member.
*/
set_omember(How, Member, Obj, Var) :-
% Ensure that the necessary arguments are nonvar (i.e., instantiated) before proceeding.
must_be_nonvar(Member),
must_be_nonvar(Obj),
must_be_nonvar(How),
!,
set_omemberh(How, Member, Obj, Var),
!.
/**
* get_kov(+K, +O, -V) is semidet.
*
* Retrieves the value associated with key K in object O, with a fallback to handle nested structures.
*
* @param K The key to look up.
* @param O The object in which to search for the key.
* @param V The value associated with the key.
*/
get_kov(K, O, V) :-
% Check if the object is a dot-hook, and retrieve the associated value if so.
dictoo:is_dot_hook(user, O, K, V),
!,
o_m_v(O, K, V).
get_kov(K, O, V) :-
% Fallback to retrieve values from nested objects or properties if direct retrieval fails.
(get_kov1(K, O, V) *-> true ; (get_kov1(props, O, VV), get_kov1(K, VV, V))).
/* Explanation: The commented-out clause that directly accesses rbtree nodes
was likely skipped to avoid direct manipulation of red-black tree nodes. This approach can be risky or unnecessary. */
% Directives to export and import the term_expansion_setter/2 predicate.
:- export(term_expansion_setter/2).
:- system:import(term_expansion_setter/2).
%goal_expansion(Goal,'.'(Training, Objs, Obj)):- Goal = ('.'(Training, Objs, A), Obj = V), var(Obj).
/** and other HTML tags with spaces or line breaks.
replace_in_string([' '='
'). % Write an HTML line break.
ptc(Color, Call) :-
pp(Color, call(Call)). % Print a call with color.
% Meta-predicates for pretty printing
:- meta_predicate(ppnl(+)).
ppnl(Term) :-
is_list(Term),
!, g_out(wqs(Term)). % Print a list using wqs/1.
ppnl(Term) :-
nl_if_needed, format('~q', [Term]), % Print the term with formatting.
nl_if_needed_ansi.
:- meta_predicate(pp(+)).
pp(Color, P) :-
\+ ansi_main, wants_html, % Handle pretty printing with color and HTML.
!, with_color_span(Color, pp(P)), write_br.
pp(Color, P) :-
ignore((quietlyd((wots_hs(S, pp(P)), !, color_print(Color, S))))). % Print quietly with color.
pp(_):-
is_print_collapsed, !. % If print is collapsed, skip.
pp(_Term):-
nl_if_needed, fail. % Fail and print a new line if necessary.
pp(Term) :-
\+ ansi_main, wants_html, % Handle pretty printing with HTML.
!, wots_vs(SS, ptcol_html_scrollable(Term)), write(SS), write_br.
/*
pp/1
Prints a term, but ensures some setup with nb_setval to maintain state.
Uses nb_current to check if arc_can_portray exists before printing.
@param Term The term to print
@example pp(my_term).
*/
pp(Term) :-
% Check if the nb_current 'arc_can_portray' flag is unset, if so print the term with specific settings
\+ nb_current(arc_can_portray, _),
!,
locally(nb_setval(arc_can_portray, t), print(Term)).
pp(Term) :-
% If the previous condition fails, use az_ansi to print the term in ANSI format and handle newline
az_ansi(pp_no_nl(Term)),
!,
nl_if_needed_ansi.
/*
Previously: ptcol(P) was a general printing routine, but this code was commented out
It seems that ptcol_html is now preferred, and the original code is skipped.
It's left in case ptcol is used in a future revision or the application context changes.
This dead code section may be skipped because ptcol_html provides better formatting for HTML output.
*/
/*
ptcol(P) :-
wants_html, !,
ptcol_html(P).
ptcol(call(P)) :-
callable(P), !,
call(P).
ptcol(P) :-
pp(P).
*/
% Main entry point to handle HTML output using ptcol_html_scrollable
ptcol_html(P) :-
ptcol_html_scrollable_0(P).
% Handle HTML scrolling output with a div tag and the scrollable attribute
ptcol_html_scrollable(P) :-
with_tag_ats(div, scrollable, ptcol_html_scrollable_0(P)).
% Basic HTML output handler for pretty printing within a preformatted block
ptcol_html_0(P) :-
with_tag(pre, ptcol_html_wo_pre(P)).
% Calls P if it's callable; otherwise, pretty prints without newlines
ptcol_html_wo_pre(call(P)) :-
callable(P), !,
in_pp_html(call(P)).
ptcol_html_wo_pre(P) :-
in_pp_html(print_tree_no_nl(P)).
% Scrollable HTML pretty print routine that wraps around the non-scrollable version
ptcol_html_scrollable_0(P) :-
ptcol_html_wo_pre(P).
/*
pp_wcg/1
Wrapper for pretty printing with an option for HTML output or a safe method to print terms.
@param G The term or goal to print
@example pp_wcg(my_term).
*/
pp_wcg(G) :-
% Check if HTML is preferred, if so, use the scrollable HTML version
wants_html, !,
ptcol_html_scrollable(G).
pp_wcg(G) :-
% Otherwise, use a safe method to print the term, with special flags
pp_safe(call((locally(nb_setval(arc_can_portray, t), print(G))))), !.
/*
wqln and wqnl are simple wrappers for pretty printing with or without newlines.
The naming convention here is somewhat arbitrary, but wqln seems to ensure newlines after printing.
*/
/*
wqln/1
Wrapper for ppnl (pretty print with newline).
@param Term The term to print
*/
wqln(Term) :-
ppnl(Term).
/*
wqnl/1
Similar to wqln, but may prefer safer printing.
@param G The term to print
*/
wqnl(G) :-
pp_safe(call((locally(nb_setval(arc_can_portray, nil), print(G))))), !.
/*
pp_safe/1
Ensures safe printing by checking if printing should be hidden.
If not hidden, it will safely call or print the term.
@param W The term to print
*/
pp_safe(_) :-
% Check if pp_hide is set, if so, do nothing (skip printing)
nb_current(pp_hide, t), !.
pp_safe(call(W)) :-
% If W is a callable term, call it and ensure newlines around the output
!,
nl_if_needed, nl_now,
call(W),
nl_now.
pp_safe(W) :-
% Otherwise, write the term in a quoted format, ensuring newlines around the output
nl_if_needed, nl_now,
writeq(W),
nl_now.
/*
pp_safe/2
A version of pp_safe that allows colored printing.
@param C The color to use
@param W The term to print
*/
pp_safe(C, W) :-
color_print(C, call(pp_safe(W))).
/*
p_p_t_no_nl/1
Handles printing based on whether the system wants HTML or ANSI printing.
@param Term The term to print
*/
p_p_t_no_nl(P) :-
% Check if ANSI printing is disabled and HTML is wanted, use HTML print
\+ ansi_main, wants_html, !,
ptcol_html(P).
p_p_t_no_nl(Term) :-
% Otherwise, use ANSI printing without newlines
az_ansi(print_tree_no_nl(Term)).
/*
ppt_no_nl/1
Another variation of pretty printing without newlines, with checks for HTML and ANSI.
@param P The term to print
*/
ppt_no_nl(P) :-
% Use HTML printing if applicable
\+ ansi_main, wants_html, !,
ptcol_html(P).
ppt_no_nl(P) :-
% Otherwise, try to tersify the term and then print it without newlines
tersify(P, Q), !,
pp_no_nl(Q).
/*
is_toplevel_printing/1
Determines if the term is being printed at the top level (for pretty-printing optimizations).
@param _ Any term (ignored)
*/
is_toplevel_printing(_) :-
% Check if the output is a string or the cursor is near the start of the line
\+ is_string_output,
line_position(current_output, N),
N < 2,
fail.
/*
pp_no_nl/1
Pretty print a term without newlines, handling variables, ANSI terms, and special cases.
@param P The term to print
*/
pp_no_nl(P) :-
% If P is a variable, print it as a variable term and perform optional debug actions
var(P), !,
pp(var_pt(P)),
nop((dumpST, ibreak)).
pp_no_nl(S) :-
% If S is an ANSI term, print it with ANSI support
term_is_ansi(S), !,
write_keeping_ansi_mb(S).
pp_no_nl(P) :-
% If P is an atom containing '~', use format to handle special formatting
atom(P), atom_contains(P, '~'), !,
format(P).
pp_no_nl(G) :-
% If G is a VM map, write it using write_map
is_vm_map(G), !,
write_map(G, 'pp').
pp_no_nl(P) :-
% Otherwise, use a general pretty-printing mechanism with guessed formatting
\+ \+ ((pt_guess_pretty(P, GP), ptw(GP))).
/*
ptw/1
Main pretty print wrapper that falls back to various printing techniques depending on the type of term.
@param P The term to print
*/
ptw(P) :-
% If P is a variable, print it with specific settings
var(P), !,
ptw(var_ptw(P)),
nop((dumpST, ibreak)).
ptw(G) :-
% If G is a VM map, write it with specific settings
is_vm_map(G), !,
write_map(G, 'ptw').
ptw(S) :-
% If S is an ANSI term, write it with ANSI support
term_is_ansi(S), !,
write_keeping_ansi_mb(S).
ptw(P) :-
% Otherwise, use the no-newline version of pretty printing
p_p_t_no_nl(P), !.
/*
pt_guess_pretty/2
Attempts to pretty-print a term by guessing the format.
@param P The original term
@param O The guessed pretty-printed version
*/
pt_guess_pretty(P, O) :-
\+ nb_current(in_pt_guess_pretty, t),
locally(nb_setval(in_pt_guess_pretty, t), pt_guess_pretty_1(P, O)).
pt_guess_pretty(O, O).
/*
upcase_atom_var_l/2
Converts a list of atoms to uppercase, with special handling for lists of atoms.
@param IntL Input list
@param NameL Resulting list with uppercase atoms
*/
upcase_atom_var_l(IntL, NameL) :-
upcase_atom_var(IntL, NameL).
upcase_atom_var_l(IntL, NameL) :-
% If the input is a list, recursively apply upcase_atom_var_l
is_list(IntL), !,
my_maplist(upcase_atom_var_l, IntL, NameL).
/*
pt_guess_pretty_1/2
A helper predicate for pt_guess_pretty that attempts to transform a term for pretty printing.
@param P Input term
@param O Transformed term
*/
pt_guess_pretty_1(P, O) :-
copy_term(P, O, _),
ignore((sub_term(Body, O), compound(Body), Body = was_once(InSet, InVars), upcase_atom_var_l(InSet, InVars))).
ignore(pretty1(O)),ignore(pretty_two(O)),ignore(pretty_three(O)),ignore(pretty_final(O)),!,
((term_singletons(O,SS),numbervars(SS,999999999999,_,[attvar(skip),singletons(true)]))).
:- dynamic(pretty_clauses:pp_hook/3). % Declare pp_hook/3 as a dynamic predicate, allowing runtime modifications
:- multifile(pretty_clauses:pp_hook/3). % Declare pp_hook/3 as a multifile predicate, allowing it to be defined across multiple files
:- module_transparent(pretty_clauses:pp_hook/3). % Ensure that pp_hook/3 is transparent, maintaining module encapsulation
pretty_clauses:pp_hook(FS,Tab,S):- \+ current_prolog_flag(never_pp_hook, true), nb_current(arc_can_portray,t), notrace(catch(arc_pp_hook(FS,Tab,S),_,fail)).
/**
* arc_pp_hook(+FS, +Tab, +S) is semidet.
*
* Pretty printing hook for terms. This predicate handles various types of terms,
* including ANSI-formatted and grouped terms.
*
* @param FS The term being processed.
* @param Tab The current indentation level.
* @param S The stream or term to print.
*/
arc_pp_hook(_, Tab, S) :- term_is_ansi(S), !, prefix_spaces(Tab), write_keeping_ansi_mb(S). % Handle ANSI-formatted terms
/* Previously: arc_pp_hook(_,Tab,S):- is_vm(S),!,prefix_spaces(Tab),!,write('..VM..').
This code was skipped as it seems to be a specific condition related to a VM term, but is no longer relevant. */
/* Previously: arc_pp_hook(_, _,_):- \+ \+ current_prolog_flag(never_pp_hook, true), nb_current(arc_can_portray,t),!,fail.
Skipped because it explicitly checks for conditions that are handled more efficiently in other parts of the code. */
arc_pp_hook(FS, _, G) :-
\+ current_prolog_flag(never_pp_hook, true), % Check if the pp_hook is enabled
nb_current(arc_can_portray, t), % Ensure arc_can_portray is active
current_predicate(is_group/1), % Check if is_group/1 predicate is defined
locally(b_setval(pp_parent, FS), print_with_pad(pp_hook_g(G))), !. % Locally set pp_parent and print the group
/** pp_parent(-PP) is semidet.
*
* Retrieves the current parent of a pretty printing process.
*
* @param PP The parent term, or an empty list if none is set.
*/
pp_parent(PP) :- nb_current(pp_parent, PP), !. % Retrieve the current parent
pp_parent([]) :- !. % Default to an empty list if no parent is set
/* Previously: :- meta_predicate(lock_doing(+,+,0)).
Modified to support a module-based predicate specifier. */
/**
* lock_doing(+Lock, +G, :Goal) is det.
*
* Prevents recursive execution of the same goal within a locked section.
* If the goal has already been executed, it won't be re-executed in the same context.
*
* @param Lock The lock variable, used to track goals.
* @param G The goal to lock.
* @param Goal The actual goal to execute.
*/
:- meta_predicate(lock_doing(+,+,:)). % Declare lock_doing as a meta-predicate
lock_doing(Lock, G, Goal) :-
(nb_current(Lock, Was); Was = []), !, % Check if the lock already has a value
\+ ((member(E, Was), E == G)), % Ensure G hasn't been executed before
locally(nb_setval(Lock, [G | Was]), Goal). % Execute the goal in a locked section
/** never_let_arc_portray_again is det.
*
* Sets a Prolog flag to prevent further portrayal by the arc system.
*/
never_let_arc_portray_again :- set_prolog_flag(never_pp_hook, true), !. % Disable the pp_hook globally
/** arc_can_portray is semidet.
*
* Checks if the arc portrayal system is allowed to portray terms.
*/
arc_can_portray :- \+ current_prolog_flag(never_pp_hook, true), nb_current(arc_can_portray, t). % Check if portrayal is allowed
/** arcp:will_arc_portray is semidet.
*
* Determines whether the arc system should portray a term.
* Conditions include ensuring debug mode is off and tracing is disabled.
*/
arcp:will_arc_portray :-
\+ current_prolog_flag(never_pp_hook, true),
\+ nb_current(arc_can_portray, f),
current_prolog_flag(debug, false), % Ensure debugging is off
\+ tracing, % Ensure tracing is off
flag(arc_portray_current_depth, X, X), X < 3, % Ensure portrayal depth is within limits
current_predicate(bfly_startup/0). % Check if the predicate bfly_startup/0 exists
/** user:portray(+Grid) is semidet.
*
* Custom portrayal for grid structures.
*
* @param Grid The grid to portray.
*/
user:portray(Grid) :-
arcp:will_arc_portray, % Check if portrayal is allowed
\+ \+ catch(quietly(arc_portray(Grid)), _, fail), !, % Attempt to portray the grid quietly, catching any errors
flush_output.
/** pp_hook_g(+S) is semidet.
*
* Pretty print hook for a given term. It supports various term types and
* ensures that ANSI formatting and other conditions are handled appropriately.
*
* @param S The term to be pretty printed.
*/
pp_hook_g(S) :- term_is_ansi(S), !, write_keeping_ansi_mb(S). % Handle ANSI-formatted terms
pp_hook_g(_) :- \+ \+ current_prolog_flag(never_pp_hook, true), nb_current(arc_can_portray, t), !, fail. % Fail if portrayal is disabled
pp_hook_g(S) :- term_contains_ansi(S), !, write_nbsp, pp_hook_g0(S). % Handle terms containing ANSI sequences
pp_hook_g(G) :- \+ plain_var(G), lock_doing(in_pp_hook_g, G, pp_hook_g0(G)). % Lock and execute pp_hook_g0
/** pp_hook_g0(+S) is semidet.
*
* Secondary pretty print hook for ANSI-formatted terms.
*
* @param S The term to print.
*/
pp_hook_g0(S) :- term_is_ansi(S), !, write_nbsp, write_keeping_ansi_mb(S). % Handle ANSI-formatted terms
pp_hook_g0(_) :- \+ \+ current_prolog_flag(never_pp_hook, true), nb_current(arc_can_portray, t), !, fail. % Fail if portrayal is disabled
pp_hook_g0(_) :- in_pp(bfly), !, fail. % Skip if in the context of 'bfly'
pp_hook_g0(G) :- wots_hs(S, in_bfly(f, pp_hook_g10(G))), write(S). % Execute further pretty printing in a 'butterfly' context
/** mass_gt1(+O1) is semidet.
*
* Check if the mass of an object is greater than 1.
*
* @param O1 The object to check.
*/
mass_gt1(O1) :- into_obj(O1, O2), mass(O2, M), !, M > 1. % Convert O1 into O2 and check if its mass is greater than 1
/** pp_hook_g10(+G) is semidet.
*
* Tertiary pretty print hook that handles more complex structures.
*
* @param G The term to print.
*/
pp_hook_g10(G) :- \+ plain_var(G), current_predicate(pp_hook_g1/1), lock_doing(in_pp_hook_g10, G, pp_hook_g1(G)). % Lock and execute pp_hook_g1
/* Previously: as_grid_string(O,SSS):- is_grid(O),wots_vs(S,print_grid(O)), sformat(SSS,'{ ~w}',[S]).
Commented out as it may be an alternative approach for grid formatting. */
/** as_grid_string(+O, -SSS) is semidet.
*
* Convert an object to a grid-formatted string.
*
* @param O The object to convert.
* @param SSS The resulting string.
*/
as_grid_string(O, SSS) :- wots_vs(S, show_indiv(O)), sformat(SSS, '{ ~w}', [S]). % Convert object O into a formatted string
/** as_pre_string(+O, -SS) is semidet.
*
* Convert an object to a pretty-printed string.
*
* @param O The object to convert.
* @param SS The resulting string.
*/
as_pre_string(O, SS) :- wots_hs(S, show_indiv(O)), strip_vspace(S, SS). % Convert object O into a pretty-printed string
/** pretty_grid(+O) is semidet.
*
* Pretty print a grid structure.
*
* @param O The grid to print.
*/
pretty_grid(O) :-
catch(
(wots_hs(S, print_grid(O)), strip_vspace(S, SS), ptc(orange, (format('" ~w "', [SS])))),
_, fail), !. % Try to pretty-print the grid, catching any errors
/* previously: pretty_grid(O) was commented out because its functionality was replaced or optimized elsewhere.
The use of the `catch/3` in this code suggests that it was handling errors when displaying grids,
which might have been deemed unnecessary or handled in a more centralized way.
*/
/**
* pp_hook_g1(+O)
*
* A hook predicate that prints various types of terms (e.g., grids, objects, colors).
* This is used in contexts where specific term types need to be visualized in particular ways.
*
* @param O The term to be printed.
*
* @example
* ?- pp_hook_g1(rhs(x)).
* Prints "rhs(x)" formatted in bold.
*/
pp_hook_g1(O) :-
plain_var(O), % Check if O is an uninstantiated variable.
!, % If true, cut to prevent backtracking and fail since we don't print plain variables.
fail.
pp_hook_g1(O) :-
attvar(O), % Check if O is an attributed variable.
!, % If true, handle the attributed variable.
is_colorish(O), % Check if O has color properties.
data_type(O, DT), % Retrieve the data type of O.
writeq('...'(DT)), % Write the data type in a quoted form.
!. % Cut to prevent further rules from being executed.
pp_hook_g1(S) :-
term_is_ansi(S), % Check if the term S is an ANSI-compatible term (for formatting purposes).
!, % If true, handle it accordingly.
write_nbsp, % Write a non-breaking space.
write_keeping_ansi_mb(S). % Write S while preserving ANSI formatting.
% previously: term_contains_ansi was commented out because the ANSI formatting for terms
% containing special sequences may have been replaced with other logic.
% pp_hook_g1(S) :- term_contains_ansi(S), !, fail, write_nbsp, write_keeping_ansi_mb(S).
pp_hook_g1(rhs(O)) :-
write_nbsp, % Write a non-breaking space.
nl, % Print a newline.
bold_print(print(r_h_s(O))), % Print the right-hand side (r_h_s) of the term O in bold.
!.
pp_hook_g1(iz(O)) :-
compound(O), % Check if O is a compound term.
O = info(_), % Further check if O has the form info(_).
underline_print(print(izz(O))), % Print O with underlined text.
!.
pp_hook_g1(O) :-
is_grid(O), % Check if O is a grid (specific term type).
/* previously: This line involving sub_term was commented out because it was deemed redundant.
The sub_term check for '$VAR'(_) was unnecessary in the current context.
*/
pretty_grid(O). % Call the pretty_grid predicate to print the grid.
pp_hook_g1(O) :-
is_object(O), % Check if O is an object (a complex term).
into_solid_grid(O, G), % Convert O into a solid grid form.
wots(SS, pretty_grid(G)), % Fetch the grid representation and process it.
write(og(SS)), % Write the grid (og stands for "output grid").
!.
pp_hook_g1(shape_rep(grav,O)) :-
is_points_list(O), % Check if O is a list of points.
as_grid_string(O, S), % Convert the list of points into a grid string.
wotsq(O, Q), % Perform some query/processing on O (wotsq likely prints or returns a result).
print(shape_rep(grav, S, Q)), % Print the shape representation with gravity.
!.
pp_hook_g1(vals(O)) :-
!,
writeq(vals(O)), % Print the value of O quoted.
!.
% previously: l2r(O) code block was commented out, possibly because it duplicates grid representation functionality.
% pp_hook_g1(l2r(O)) :- into_solid_grid_strings(l2r(O), Str), Str \=@= l2r(O), print_term_no_nl(Str), !.
pp_hook_g1(localpoints(O)) :-
is_points_list(O), % Check if O is a list of points.
as_grid_string(O, S), % Convert the list of points to a grid string.
wotsq(O, Q), % Perform query/processing on O.
print(localpoints(S, Q)), % Print the local points.
!.
pp_hook_g1(C) :-
compound(C), % Check if C is a compound term.
compound_name_arguments(C, F, [O]), % Extract the functor and arguments of the compound term.
is_points_list(O), % Check if O is a list of points.
length(O, N), N > 2, % Ensure O contains more than two points.
as_grid_string(O, S), % Convert O to a grid string.
compound_name_arguments(CO, F, [S]), % Reconstruct the compound term with the grid string.
print(CO), % Print the new compound term.
!.
pp_hook_g1(O) :-
is_points_list(O), % Check if O is a list of points.
as_grid_string(O, S), % Convert the list to a grid string.
write(S), % Write the grid string.
!.
pp_hook_g1(O) :-
is_real_color(O), % Check if O represents a real color.
color_print(O, call(writeq(O))), % Print the color in a special colored format.
!.
pp_hook_g1(O) :-
is_colorish(O), % Check if O has color properties.
data_type(O, DT), % Get the data type of O.
writeq('...'(DT)), % Print the data type of O.
!.
pp_hook_g1(_) :-
\+ in_pp(ansi), % If we are not in ANSI mode, fail.
!,
fail.
pp_hook_g1(Grp) :-
current_predicate(pp_ilp/1), % Check if the predicate pp_ilp/1 exists.
is_rule_mapping(Grp), % Check if Grp is a rule mapping.
pp_ilp(Grp), % Call pp_ilp to print the rule mapping.
!.
pp_hook_g1(O) :-
atom(O), % Check if O is an atom.
atom_contains(O, 'o_'), % Ensure O contains the substring 'o_'.
pp_parent([LF|_]), % Get the parent of the current term.
\+ (LF == lf; LF == objFn), % Ensure LF is not 'lf' or 'objFn'.
resolve_reference(O, Var), % Resolve O to its reference Var.
O \== Var, % Ensure O is different from Var.
\+ plain_var(Var), % Ensure Var is not a plain variable.
!,
write_nbsp, % Write a non-breaking space.
writeq(O), % Print O quoted.
write(' /* '), % Write a comment opening.
show_indiv(Var), % Show the individual Var.
write(' */ '). % Close the comment.
pp_hook_g1(O) :-
is_object(O), % Check if O is an object.
pp_no_nl(O), % Print O without a newline.
!.
pp_hook_g1(O) :-
is_group(O), % Check if O is a group.
pp_no_nl(O), % Print O without a newline.
!.
% previously: change_obj was commented out, possibly due to redundancy in object comparison and presentation.
% pp_hook_g1(change_obj(N, O1, O2, Sames, Diffs)) :- showdiff_objects5(N, O1, O2, Sames, Diffs), !.
pp_hook_g1(O) :-
is_vm_map(O), % Check if O is a VM map.
data_type(O, DT), % Get the data type of O.
writeq('..map.'(DT)), % Print the data type of the VM map.
!.
pp_hook_g1(O) :-
is_gridoid(O), % Check if O is a grid-like object.
show_indiv(O), % Show the individual properties of the grid-like object.
!.
% previously: change_obj and diff were commented out, possibly due to updates in the diff and object change visualization mechanism.
% pp_hook_g1(O) :- O = change_obj(O1, O2, _Same, _Diff), w_section(showdiff_objects(O1, O2)), !.
% pp_hook_g1(O) :- O = change_obj(O1, O2, _Same, _Diff), w_section(object, [O1, O2], with_tagged('h5', pp(O))).
pp_hook_g1(O) :-
O = showdiff(O1, O2), % If O represents a difference between O1 and O2.
!,
showdiff(O1, O2). % Show the differences between O1 and O2.
% previously: compound(O), wqs1(O) was commented out as part of an optimization or replacement.
% pp_hook_g1(O) :- compound(O), wqs1(O), !.
pp_hook_g1(O) :-
\+ compound(O), % If O is not a compound term, fail.
fail.
pp_hook_g1(G) :-
'@'(pp_hook_g1a(G), user). % Call pp_hook_g1a in the context of the user.
pp_hook_g1a(G) :-
\+ current_prolog_flag(debug, true), % If debug mode is off.
current_predicate(pp_hook_g2/1), % Check if pp_hook_g2/1 exists.
lock_doing(in_pp_hook_g3, any, pp_hook_g2(G)), % Lock and execute pp_hook_g2 for G.
!.
/* PLDoc header for pp_hook_g1a/1
This predicate processes the term `G` and applies the `fch/1` predicate to it.
It uses cut (!) to ensure the processing stops after the first match.
@param G The term to be processed.
@example pp_hook_g1a(example_term). */
pp_hook_g1a(G) :-
% Applies the fch/1 predicate to the input term G
fch(G),
% Cut (!) ensures that no further rules are considered
!.
/* previously: pp_hook_g2/1 was intended to process output terms using colorization,
but it's commented out, possibly due to dependency on a predicate `colorize_oterms/2`.
Dead code explanation: Skipped due to reliance on a condition `current_predicate(colorize_oterms/2)`
which may not always be true or may no longer be needed. */
%pp_hook_g2(O):- current_predicate(colorize_oterms/2),colorize_oterms(O,C), notrace(catch(fch(C),_,fail)),! .
/* PLDoc header for fch/1
This predicate is used for formatting or outputting a term `O`. Currently,
it applies the `wqs1/1` predicate to the input term `O`.
@param O The term to be output or formatted.
@example fch(example_term). */
fch(O) :-
% Applies the wqs1/1 predicate to the input term O
wqs1(O).
/* previously: Other variations of `fch/1` were used for different printing mechanisms
such as `pp_no_nl/1` for printing without newline, but they are now commented out
and not in use, possibly for simplifying the output format. */
%fch(O):- pp_no_nl(O).
%fch(O):- print(O).
%fch(O):- p_p_t_no_nl(O).
/* PLDoc header for wotsq/2
This predicate takes a term `O` and a second argument `Q` and processes them
using the `wots_hs/2` and `wqnl/1` predicates.
@param O The term to be processed.
@param Q The second argument used for processing.
@example wotsq(term1, term2). */
wotsq(O, Q) :-
% Calls the wots_hs/2 predicate with the second argument Q
% and the result of wqnl/1 applied to O.
wots_hs(Q, wqnl(O)).
/* PLDoc header for has_goals/1
This predicate checks if a term `G` has goals by examining its attributed
variables (attvars) or if its variables and singletons differ.
@param G The term to be examined.
@example has_goals(example_term). */
has_goals(G) :-
% Check if the term G has attributed variables (attvars).
term_attvars(G, AV),
AV \== [].
has_goals(G) :-
% Check if the term G has variables that are not singletons.
term_variables(G, TV),
term_singletons(G, SV),
TV \== SV.
/* PLDoc header for maybe_term_goals/3
This predicate examines a term and produces its attributed variables and goals,
copying terms and applying numbervars to its variables.
@param Term The original term.
@param TermC The copied term after processing.
@param Goals The list of goals associated with the term.
@example maybe_term_goals(example_term, CopiedTerm, Goals). */
maybe_term_goals(Term, TermC, Goals) :-
% Extract attributed variables from the term
term_attvars(Term, Attvars),
Attvars \== [],
!, % Cut to prevent further backtracking if attributed variables are found
term_variables(Term, Vars),
% Filter out variables that are not in the attributed variables list
include(not_in(Attvars), Vars, PlainVars),
% Copy term along with attributed and plain variables
copy_term((Attvars + PlainVars + Term), (AttvarsC + PlainVarsC + TermC), Goals),
% Number the variables starting from 10, skipping attributed variables
numbervars(PlainVarsC, 10, Ten1, [singletons(true), attvar(skip)]),
% Number the attributed variables and goals
numbervars(AttvarsC + Goals, Ten1, _Ten, [attvar(bind), singletons(false)]).
/* PLDoc header for maybe_replace_vars/5
This predicate replaces variables in goals if necessary, using sub_var and
freeze for goal evaluation.
@param VarsC The list of variables to replace.
@param SGoals The original goals.
@param TermC The copied term.
@param RSGoals The resulting goals after replacement.
@param RTermC The resulting term after replacement.
@example maybe_replace_vars([Var1, Var2], Goals, CopiedTerm, ResultGoals, ResultTerm). */
maybe_replace_vars([], SGoals, TermC, SGoals, TermC) :-
!. % If there are no variables to replace, return the original goals and term.
maybe_replace_vars([V | VarsC], SGoals, TermC, RSGoals, RTermC) :-
% Partition the goals into those containing the variable V and those without
my_partition(sub_var(V), SGoals, Withvar, WithoutVar),
% Ensure that only one goal contains the variable
Withvar = [OneGoal],
% Use freeze to delay evaluation of the goal until it's not null
freeze(OneGoal, (OneGoal \== null, OneGoal \== @(null))),
% Ensure the variable appears only once in the term
findall(_, sub_var(V, TermC), LL),
LL = [_],
!,
% Substitute the variable V with the goal in the term and goals
subst([WithoutVar, TermC], V, {OneGoal}, [SGoalsM, TermCM]),
% Recursively replace remaining variables
maybe_replace_vars(VarsC, SGoalsM, TermCM, RSGoals, RTermC).
maybe_replace_vars([_ | VarsC], SGoals, TermC, RSGoals, RTermC) :-
% If the variable is not found, continue with the next variable
maybe_replace_vars(VarsC, SGoals, TermC, RSGoals, RTermC).
/* PLDoc header for src_sameish/2
This predicate checks if two terms are "sameish", i.e., structurally equivalent.
@param Orig The original term.
@param Find The term to compare with.
@example src_sameish(term1, term2). */
src_sameish(Orig, Find) :-
% Copy the original term to a new variable COrig
copy_term(Orig, COrig),
% Set Find to Orig and check if Orig and COrig are structurally equivalent
Find = Orig,
Orig =@= COrig.
/* PLDoc header for number_vars_calc_goals/3
This predicate calculates goals and assigns numbered variables to a term,
taking into account its singletons and attributed variables.
@param Term The original term.
@param SSRTermC The term after processing with numbered variables.
@param SRSGoals The sorted list of goals.
@example number_vars_calc_goals(example_term, ProcessedTerm, Goals). */
number_vars_calc_goals(Term, SSRTermC, [1 | SRSGoals]) :-
% Extract singletons and attributed variables from the term
term_singletons(Term, Singles),
term_attvars(Term, Vars),
% Copy the term, variables, and singletons
copy_term(Term + Vars + Singles, TermC + VarsC + SinglesC, Goals),
% Number the variables and goals, skipping attributed variables
notrace(catch(numbervars(TermC + Goals, 0, _Ten1, [singletons(false), attvar(skip)]), _, fail)),
% Sort the goals based on variables
sort_goals(Goals, VarsC, SGoals),
% Replace variables in the goals and term if necessary
maybe_replace_vars(VarsC, SGoals, TermC, RSGoals, RTermC),
% Filter out non-substituted singletons
include(not_sub_var(RSGoals), SinglesC, KSingles),
% Create placeholder variables for remaining singletons
length(KSingles, SL),
length(VSingles, SL),
my_maplist(=('$VAR'('__')), VSingles),
% Substitute the singletons and variables in the term and goals
subst_2L(KSingles, VSingles, [RTermC, RSGoals], [SRTermC, SRSGoals]),
% Apply specific substitutions based on matching patterns
subst_1L_p2(src_sameish, [
{dif('$VAR'('__'), RED)} = dif(RED),
{cbg('$VAR'('__'))} = cbg
], SRTermC, SSRTermC),
!.
number_vars_calc_goals(Term,SSRTermC,[3|SRSGoals]):-
term_singletons(Term,Singles),
term_attvars(Term,Vars),
copy_term(Term+Vars+Singles,TermC+VarsC+SinglesC,Goals),
numbervars(TermC+Goals,0,_Ten1,[singletons(false),attvar(bind)]),
sort_goals(Goals,VarsC,SGoals),
maybe_replace_vars(VarsC,SGoals,TermC,RSGoals,RTermC),
include(not_sub_var(RSGoals),SinglesC,KSingles),
length(KSingles,SL),length(VSingles,SL),my_maplist(=('$VAR'('__')),VSingles),
subst_2L(KSingles,VSingles,[RTermC,RSGoals],[SRTermC,SRSGoals]),
subst(SRTermC,{cbg('_')},cbg,SSRTermC),!.
/* number_vars_calc_goals/3 calculates variables and goals for a given term */
% @param Term Input term.
% @param TermC Copy of the term after variable numbering.
% @param [4|SGoals] Numbered goals starting with a 4.
% Uses numbervars with the option singletons(true) to ensure unique variable names.
number_vars_calc_goals(Term, TermC, [4|SGoals]) :-
/* term_variables/2 extracts the free variables in Term */
term_variables(Term, Vars),
/* term_attvars/2 extracts attributed variables in Term */
term_attvars(Term, Attvars),
/* copy_term/3 creates a copy of the term and variables, including goals */
copy_term(Term+Vars+Attvars, TermC+VarsC+AttvarsC, Goals),
/* numbervars/3 numbers variables in TermC and Goals */
notrace(catch(numbervars(TermC+Goals, 0, _Ten1, [singletons(true)]), _, fail)),
/* append/3 appends lists of attributed variables and free variables */
append([AttvarsC, VarsC, AttvarsC, Vars], Sorted),
/* sort_goals/3 sorts the goals based on variable ordering */
sort_goals(Goals, Sorted, SGoals), !.
/* Another variant of number_vars_calc_goals, differing in options */
% @param [5|SGoals] Numbered goals starting with a 5.
number_vars_calc_goals(Term, TermC, [5|SGoals]) :-
term_variables(Term, Vars),
term_attvars(Term, Attvars),
copy_term(Term+Vars+Attvars, TermC+VarsC+AttvarsC, Goals),
/* numbervars with singletons(false) and attvar(skip) options */
numbervars(TermC+Goals, 0, _Ten1, [singletons(false), attvar(skip)]),
append([AttvarsC, VarsC, Attvars, Vars], Sorted),
sort_goals(Goals, Sorted, SGoals), !.
/* writeg/1 tries to write a term with extra handling */
% @param Term The term to write.
% Uses writeg0 or fallback to ppa for error handling.
writeg(Term) :-
ignore(\+ notrace(catch(once(writeg0(Term); ppa(Term)), E, (pp(E), ppa(Term))))), !.
/* writeg0/1 handles writing a term, including attributed variables */
% @param Term The term to write.
% Writes attributed variables and goals if applicable.
writeg0(Term) :-
term_attvars(Term, Attvars),
Attvars \== [], !,
must_det_ll((
number_vars_calc_goals(Term, TermC, Goals),
writeg5(TermC), !,
if_t(Goals \== [], (
nl_if_needed,
write(' goals='),
call_w_pad_prev(3, az_ansi(print_tree_no_nl(Goals)))
))
)), !.
/* Writes ground terms or invokes numbering for variables */
writeg0(Term) :-
\+ ground(Term),
\+ \+ must_det_ll((
numbervars(Term, 0, _Ten1, [singletons(true), attvar(skip)]),
writeg5(Term)
)).
/* If no special handling is needed, just write the term */
writeg0(Term) :- writeg5(Term), !.
/* writeg5 handles specific types of terms for formatted output */
writeg5(X) :-
is_ftVar(X), !, write_nbsp, write_nbsp, print(X), write_nbsp.
/* Special handling for 2x2 grids */
writeg5(N=V) :-
is_simple_2x2(V), !,
print_grid(N, V),
writeln(' = '),
call_w_pad_prev(2, writeg9(V)).
/* Special handling for grid-like structures */
writeg5(N=V) :-
is_gridoid(V), !,
print_grid(N, V),
writeln(' = '),
call_w_pad_prev(2, writeg9(V)).
/* Generic handling of non-variable terms */
writeg5(N=V) :-
nl_if_needed,
nonvar(N),
pp_no_nl(N),
writeln(' = '),
!,
call_w_pad_prev(2, writeg5(V)).
/* Default failure case */
writeg5(_) :- write_nbsp, fail.
/* Recursive term handler */
writeg5(V) :- writeg9(V).
/* writeg8 is a helper function for printing ftVars or variables */
writeg8(X) :- is_ftVar(X), !, print(X).
writeg8(X) :- var(X), !, print(X).
writeg8(X) :- writeq(X).
/* writeg9 handles lists and structured outputs */
writeg9(V) :- is_simple_2x2(V), !, print_simple_2x2(writeg8, V).
writeg9(V) :- is_list(V), nl_if_needed, write('['), !, my_maplist(writeg5, V), write(']').
writeg9(_) :- write_nbsp, write(' \t '), fail.
writeg9(X) :- is_ftVar(X), !, write_nbsp, write_nbsp, print(X).
writeg9(V) :- pp_no_nl(V).
/* previously: alternative writeg5 implementation, now skipped for clarity */
% Kept for legacy reasons, but not used in the current implementation.
/*
writeg5(V):- is_simple_2x2(V),!,print_simple_2x2(writeg8,V).
writeg5(V):- is_gridoid(V),!,call_w_pad_prev(2,writeg9(V)).
writeg5(V):- is_list(V),nl_if_needed,write('['),my_maplist(writeg5,V),write(']').
*/
/* arg1_near checks if the first argument of a goal matches a specific variable */
% @param Vars List of variables.
% @param Goal Goal to match against.
% @param Nth Position of the variable in Vars.
arg1_near(Vars, Goal, Nth) :-
tc_arg(1, Goal, PreSort),
nth1(Nth, Vars, E),
E == PreSort, !.
arg1_near(_VarsC, Goal, PreSort) :-
tc_arg(1, Goal, PreSort), !.
arg1_near(_VarsC, Goal, Goal).
/* sort_goals uses predsort to order goals based on their variables */
% @param Goals The list of goals.
% @param VarsC The variables for sorting.
% @param SGoals Sorted list of goals.
sort_goals(Goals, VarsC, SGoals) :-
predsort(sort_on(arg1_near(VarsC)), Goals, SGoals).
/*
writeg0(Obj):- is_object(Obj),pp(Obj),!.
writeg0(O):- writeg00(O).
writeg00(Term):-
maybe_term_goals(Term,TermC,Goals),
writeg00(TermC), call_w_pad(2,writeg00(Goals)),!.
writeg00(N=V):- nl_if_needed,nonvar(N), pp_no_nl(N),writeln(' = '), !, call_w_pad(2,writeg00(V)).
writeg00(O):- compound(O),compound_name_arguments(O,F,[A]),!,call_w_pad(2,((writeq(F),write('('),writeg3(A),write(')')))).
writeg00(S):- term_contains_ansi(S), !, write_keeping_ansi_mb(S).
writeg00([H|T]):- compound(H),H=(_=_), my_maplist(writeg0,[H|T]).
writeg00([H|T]):- is_list(T),call_w_pad(2,((nl,write('['),writeg2(H),my_maplist(writeg0,T),write(']'),nl))).
%writeg0(Term):- \+ ground(Term),!, \+ \+ (numbervars(Term,99799,_,[singletons(true)]),
% subst(Term,'$VAR'('_'),'$VAR'('_____'),TermO), writeg0(TermO)).
%writeg0(V):- \+ is_list(V),!,writeq(V),nl_now.
writeg00(V):- \+ is_list(V),!,pp(V).
writeg00(X):- call_w_pad(2,pp(X)).
writeg1(N=V):- is_gridoid(V),!,print_grid(N,V),call_w_pad(2,(my_maplist(writeg1,V))).
writeg1(X):- nl_if_needed,writeg2(X),!,write_nbsp,!.
writeg2(S):- term_contains_ansi(S), !, write_keeping_ansi_mb(S).
writeg2(X):- is_ftVar(X),!,print(X).
writeg2(X):- write_term(X,[quoted(true),quote_non_ascii(true),portrayed(false),nl(false),numbervars(true)]),!.
%writeg2(X):- write_term(X,[quoted(true),quote_non_ascii(true),portrayed(false),nl(false),numbervars(false)]),!.
%writeg1(X):- nl_if_needed,writeg(X).
writeg2(S):- term_is_ansi(S), !, write_keeping_ansi_mb(S).
writeg2(X):- writeq(X),!.
writeg3(X):- is_list(X),X\==[],X=[_,_|_],!,writeg(X).
writeg3(X):- writeg2(X).
*/
/* previously: This section contains older code related to printing terms using pp_hook_g1 and pp_hook_g.
These predicates handle specific use cases for pretty-printing terms. Although not in active use,
they have been retained for future debugging or extensions.
*/
% pp_hook_g1(T):-
% nb_current('$portraying',Was)
% -> ((member(E,Was), T==E) -> ptv2(T) ; locally(b_setval('$portraying',[T|Was]),ptv0(T)))
% ; locally(b_setval('$portraying',[T]),ptv0(T)).
/**
* strip_vspace(+S, -Stripped)
*
* Recursively removes leading and trailing whitespace characters from a string S and returns the cleaned string as Stripped.
*
* @param S The input string with potential whitespace
* @param Stripped The cleaned string with unnecessary whitespace removed
*/
strip_vspace(S,Stripped):-
% Remove spaces before recursively stripping the string
string_concat(' ',SS,S),!,
strip_vspace(SS,Stripped).
strip_vspace(S,Stripped):-
% Remove spaces after the string and recursively strip
string_concat(SS,' ',S),!,
strip_vspace(SS,Stripped).
strip_vspace(S,Stripped):-
% Remove newline before the string and recursively strip
string_concat('\n',SS,S),!,
strip_vspace(SS,Stripped).
strip_vspace(S,Stripped):-
% Remove newline after the string and recursively strip
string_concat(SS,'\n',S),!,
strip_vspace(SS,Stripped).
strip_vspace(S,Stripped):-
% Remove tabs before the string and recursively strip
string_concat('\t',SS,S),!,
strip_vspace(SS,Stripped).
strip_vspace(S,Stripped):-
% Remove tabs after the string and recursively strip
string_concat(SS,'\t',S),!,
strip_vspace(SS,Stripped).
strip_vspace(S,Stripped):-
% Replace certain whitespace patterns with more compact representations and recursively strip
replace_in_string([" \n"="\n","( "="( ","(\n"="( "],S,S2),S2\==S,!,
strip_vspace(S2,Stripped).
/* previously: An alternative method for stripping whitespace using split_string was left out,
likely due to performance considerations with handling large strings or frequent operations.
*/
% strip_vspace(S,Stripped):- split_string(S, "", "\t\r\n", [Stripped]).
strip_vspace(S,S).
/**
* print_nl(+P)
*
* Prints the term P with a newline if necessary, optionally applying color formatting.
*
* @param P The term to print
*/
print_nl(P):-
% Print newline if needed, then print the term without an extra newline
nl_if_needed,
wots_hs(SS,pp_no_nl(P)),
maybe_color(SS,P),
nl_if_needed.
/**
* color_write(+S)
*
* Writes a term with ANSI formatting if applicable.
*
* @param S The term to write, which could contain ANSI codes or be a normal term
*/
color_write(S):-
% If the term is ANSI formatted, preserve formatting
term_is_ansi(S), !,
write_keeping_ansi_mb(S).
color_write(P):-
% Otherwise, print the term
wots_hs(SS,write(P)),
maybe_color(SS,P).
/**
* write_keeping_ansi_mb(+P)
*
* Preserves ANSI formatting while writing a term.
*
* @param P The term to write, potentially with ANSI formatting
*/
write_keeping_ansi_mb(P):-
% If the term is bold or potentially bold, preserve the formatting
is_maybe_bold(P,write_keeping_ansi(P)).
/**
* is_maybe_bold(+P)
*
* Checks if a term should be printed in bold by analyzing its content.
*
* @param P The term to check
*/
is_maybe_bold(P):-
% Format the term into a string and check if it contains specific bold-related markers
sformat(S,'~w',[P]),
atom_contains(S,'stOF').
is_maybe_bold(P,G):-
% If the term qualifies for bold, print it with underline and bold
is_maybe_bold(P),!,
underline_print(bold_print(G)).
is_maybe_bold(_P,G):-
% Otherwise, just call the generic printing method
call(G).
/**
* pp_msg_color(+P, +C)
*
* Prints a message P with color C if applicable.
*
* @param P The message term to print
* @param C The color associated with the message
*/
pp_msg_color(P,C):-
% If the term is compound, check if it has color annotations
compound(P),
pc_msg_color(P,C),!.
pp_msg_color(P,C):-
% Otherwise, print the message with default color settings
must_det_ll(mesg_color(P,C)).
/**
* pc_msg_color(+P, +C)
*
* Handles specific message types for colored output.
*
* @param P The message term to print
* @param C The color associated with the message
*/
pc_msg_color(iz(P),C):-
% If the term is wrapped in 'iz', pass it on to pp_msg_color
pp_msg_color(P,C).
pc_msg_color(link(P,_,_),C):-
% If the term is a 'link', pass it on to pp_msg_color
pp_msg_color(P,C).
/**
for HTML or ansi.
% @param S The input string.
% @param SS The output string with line breaks.
p_to_br(S, SS):-
% First, fix line breaks using fix_br_nls/2.
fix_br_nls(S, S0),
% Then, handle
and other replacements using cr_to_br/2.
cr_to_br(S0, SSS),
% Replace
', '
'='
', '
', '
'='
'], SSS, SSSS),
% Apply final line break replacement.
cr_to_br(SSSS, SS).
% Predicate to replace carriage returns for HTML.
% @param S The input string.
% @param SSS The output string with
tags.
cr_to_br_html(S, SSS):-
% Replace carriage return and newlines with
for HTML.
replace_in_string(['\r\n'='
', '\r'='
', '\n'='
'], S, SSS).
% Predicate to replace
for ansi format.
% @param S The input string.
% @param SSS The output string with \n for ansi.
cr_to_br_ansi(S, SSS):-
% Replace
with newlines for ansi formatting.
replace_in_string(['
'='\n', ' '=' '], S, SSS).
% Predicate to fix broken line breaks in HTML strings.
% @param S The input string.
% @param O The fixed output string.
fix_br_nls(S, O):-
% Perform a set of replacements to clean up HTML line breaks.
replace_in_string(['
\n'='
', '
\n'='
', '
\n'='
',
'\n
'='
', '\n
'='
', '\n
'='
'], S, O).
% Predicate to remove excessive spaces from a string.
% @param S The input string.
% @param O The output string with reduced spaces.
remove_huge_spaces(S, O):-
% First, fix line breaks.
notrace((fix_br_nls(S, SS), !,
% Then replace spaces with
using p_to_br/2.
p_to_br(SS, O))),
!.
/*
remove_huge_spaces(S,O):- fix_br_nls(S,S0),
replace_in_string([' '=' ',
' '=' ',
' '=' ',
' '=' ',
' '=' ',
' '=' ',
' '=' ',
'\t'=' ',
' '=' '],S0,SS),p_to_br(SS,O).
*/
wqs_l(H):-
\+ is_list(H), /* If H is not a list, directly process it with wqs/1 */
!, /* Cut to avoid further choices once non-list case is handled */
wqs(H). /* Call wqs/1 on the non-list input */
wqs_l(H):-
wqs(H). /* If H is a list, directly call wqs/1 to handle it */
/**
* @predicate wqs/1
* Processes the input term using wqs0/1, and applies colors if applicable.
*
* @param P Input term that will be processed and potentially colored.
*
* @example
* ?- wqs(foo). % Calls wqs0/1 to process and output foo.
*/
wqs(P):-
wots_hs(SS, wqs0(P)), /* Call wots_hs/2 with the result of wqs0(P) */
maybe_color(SS, P). /* Apply color formatting to the output if appropriate */
/**
* @predicate wqs/2
* Processes the term using wqs0/1 with an additional condition.
*
* @param C A condition to be checked.
* @param P The term to be processed if the condition holds.
*/
wqs(C, P):-
ansicall(C, wqs0(P)), /* Calls wqs0/1 on P if C holds, and applies ANSI formatting */
!. /* Cut to ensure no backtracking */
/**
* @predicate wqs0/1
* Base case for processing different types of input in wqs/1 and wqs/2.
*
* Handles variables, colors, maps, and ANSI terms accordingly.
*
* @param X The input term to process based on its type.
*/
wqs0(X):-
plain_var(X), /* If X is a plain variable */
wqs(plain_var(X)), /* Process the variable in wqs/1 */
!.
wqs0(X):-
plain_var(X), /* Redundant check for plain variable (could be optimized out) */
!,
wqs(plain_var(X)), /* Process the plain variable again */
ibreak. /* Force a break point in execution for debugging purposes */
wqs0(S):-
term_is_ansi(S), /* If S is an ANSI-compatible term */
!,
write_keeping_ansi_mb(S). /* Write S to output while preserving ANSI formatting */
wqs0(C):-
is_colorish(C), /* If C is a color or color-like term */
color_print(C, C), /* Output C with color formatting */
!.
wqs0(G):-
is_vm_map(G), /* If G is a virtual machine map */
!,
write_map(G, 'wqs'). /* Write the VM map to the output with a label 'wqs' */
wqs0(X):-
var(X), /* If X is an unbound variable */
!,
get_attrs(X, AVs), /* Get the attributes of X */
!,
writeq(X), /* Write the variable name */
write('/*{'), /* Open comment block for attributes */
print(AVs), /* Print the attributes */
write('}*/'). /* Close comment block */
/* previously: old predicates for special handling removed but still present here */
/* Code dealing with specific legacy term types, replaced with more general handling */
/*
This is the main entry predicate for processing different types of input.
It first checks if the input is an attributed variable, and then dispatches the appropriate handlers based on the input type.
*/
%% wqs0(+X)
% Entry point for various types of input
%
% @param X The input to be processed, can be of various types like variables, lists, compound terms, etc.
% @example
% ?- wqs0(attvar(X)).
% Processed attributed variable.
wqs0(X):-
% If X is an attributed variable, process it
attvar(X),
!,
wqs(attvar(X)).
% Handle the special case for 'nl_now', triggering an immediate newline
wqs0(nl_now):-
!,
nl_now.
% Handle empty strings, skipping them
wqs0(X):-
X=='',
!.
% Handle empty lists, skipping them
wqs0(X):-
X==[],
!.
% If the input is a grid structure, print the grid
wqs0(X):-
is_grid(X),
!,
print_grid(X).
% If the input is a callable term, evaluate the call
wqs0(G):-
compound(G),
G = call(C),
callable(C),
!,
call(C).
% If the input is a single element list, process the element recursively
wqs0([T]):-
!,
wqs(T).
% If the head of the list is a string, write the string and process the tail
wqs0([H|T]):-
string(H),
!,
write(H),
write_nbsp,
wqs(T).
% Skip compound terms with 'skip' structure and process the tail
wqs0([H|T]):-
compound(H),
skip(_) = H,
!,
wqs(T).
% Process the head of the list, check if a newline is needed, and process the tail
wqs0([H|T]):-
wqs(H),
need_nl(H, T),
wqs(T),
!.
% Handle objects, attempt to "tersify" (simplify) them, and process recursively
wqs0(X):-
is_object(X),
tersify1(X, Q),
X \== Q,
!,
wqs(Q).
% If the input is an object, show its shape
wqs0(X):-
is_object(X),
show_shape(X),
!.
% Handle strings that contain the character '~', format and write them with color
wqs0(X):-
string(X),
atom_contains(X, '~'),
catch((sformat(S, X, []), color_write(S)), _, fail),
!.
% Handle simple strings, write them with color
wqs0(X):-
string(X),
!,
color_write(X).
% Dead code: previously handled writing strings with special cases.
% It's skipped now as it's redundant.
% wqs([H1,H2|T]):- string(H1),string(H2),!, write(H1),write_nbsp, wqs([H2|T]).
% wqs([H1|T]):- string(H1),!, write(H1), wqs(T).
% wqs([H|T]):- compound(H),!, writeq(H), wqs(T).
% Handle the case where input is a callable term
wqs0(call(C)):-
!,
call(C).
% Handle non-compound terms, writing them with a non-breaking space
wqs0(X):-
\+ compound(X),
!,
write_nbsp,
write(X).
% Delegate compound terms to wqs1
wqs0(C):-
compound(C),
wqs1(C),
!.
% If nothing else works, delegate to wqs2
wqs0(C):-
wqs2(C).
% Dead code: previously handled ANSI terms with special conditions.
% It's skipped now due to performance reasons.
% wqs(S):- term_contains_ansi(S), !, write_nbsp, write_keeping_ansi_mb(S).
% Delegate ANSI term processing to wqs2
wqs2(S):-
term_contains_ansi(S),
!,
write_nbsp,
write_keeping_ansi_mb(S).
% Dead code: previously handled HTML writing.
% Skipped as HTML generation is no longer required.
% wqs2(P):- wants_html,!,pp(P).
% File directive: Declare a thread-local variable for wqs_fb
:- thread_local(t_l:wqs_fb/1).
% If a thread-local wqs_fb handler is set, call it
wqs2(X):-
t_l:wqs_fb(P1),
call(P1, X),
!.
% Dead code: previously wrapped wqs2 handling with writeq.
% It's skipped now as the new handlers perform better.
% wqs2(X):- with_wqs_fb(writeq,X).
% Write the output using the fallback handler if no specialized handler is found
wqs2(X):-
with_wqs_fb(writeq, print(X)),
!.
% Dead code: alternative write term strategy. Skipped for simplicity.
% wqs2(X):- with_wqs_fb(writeq,((write_nbsp,write_term(X,[quoted(true)])))).
% Helper to set a thread-local wqs_fb handler for a goal
with_wqs_fb(FB, Goal):-
locally(t_l:wqs_fb(FB), Goal).
% Convert a term to a string for output
as_arg_str(C, S):-
wots_vs(S, print(C)).
% Check if a string is a valid ANSI string
arg_string(S):-
string(S),
!.
arg_string(S):-
term_contains_ansi(S),
!.
% Delegate non-compound terms to wqs0
wqs1(C):-
\+ compound(C),
!,
wqs0(C).
% Write ANSI terms
wqs1(S):-
term_is_ansi(S),
!,
write_keeping_ansi_mb(S).
% Handle formatted output with color
wqs1(format(C, N)):-
catch((sformat(S, C, N), color_write(S)), _, fail),
!.
% Handle formatted writef calls
wqs1(writef(C, N)):-
!,
writef(C, N).
% Quoted term handling, color write the output
wqs1(q(C)):-
\+ arg_string(C),
wots_hs(S, writeq(C)),
color_write(S),
!.
% Print bold term with color
wqs1(g(C)):-
\+ arg_string(C),
wots_vs(S, bold_print(wqs1(C))),
print(g(S)),
!.
% Handle print_ss terms
wqs1(print_ss(C)):-
\+ arg_string(C),
wots_vs(S, print_ss(C)),
wqs1(print_ss(S)),
!.
% Handle bold printing for a term
wqs1(b(C)):-
\+ arg_string(C),
wots_vs(S, bold_print(wqs1(C))),
color_write(S).
% Handle ANSI term writing
wqs1(T):-
\+ is_list(T),
term_contains_ansi(T),
!,
write_keeping_ansi_mb(T).
% Print normalized grid representation
wqs1(grid_rep(norm, C)):-
writeq(grid_rep(norm, C)),
!.
% Special case for handling grid terms
wqs1(grid(C)):-
writeq(grid(C)),
!.
% Output right-hand side of a rule
wqs1(rhs(RHS)):-
nl_now,
wqnl(rhs(RHS)),
nl_now.
% Dead code: specialized grid operations, now skipped.
% wqs1(grid_ops(norm,C)):- writeq(norm(C)),!.
% Pretty print terms
wqs1(pp(P)):-
wots_vs(S, pp_no_nl(P)),
write((S)).
% Pretty print terms with no newline
wqs1(ppt(P)):-
wots_vs(S, ppt_no_nl(P)),
write((S)).
% Handle wqs terms
wqs1(wqs(P)):-
wots_vs(S, wqs(P)),
write((S)).
% Handle color printing for wqs terms
wqs1(wqs(C, P)):-
wots_vs(S, wqs(P)),
color_print(C, S).
% Print term values
wqs1(vals(C)):-
writeq(vals(C)),
!.
% Dead code: handled colored values, no longer needed.
% wqs1(colors_cc(C)):- \+ arg_string(C), as_arg_str(C,S),wqs(colorsz(S)).
% Bold print with ANSI handling
wqs1(io(C)):-
\+ arg_string(C),
wots_vs(S, bold_print(wqs(C))),
write(io(S)).
% Underline the printed term
wqs1(uc(C, W)):-
!,
write_nbsp,
color_print(C, call(underline_print(format("\t~@", [wqs(W)])))).
% Color-print terms with specified color
wqs1(cc(C, N)):-
is_color(C),
!,
color_print(C, call(writeq(cc(C, N)))).
% Write navigation command
wqs1(write_nav_cmd(C, N)):-
!,
write_nav_cmd(C, N).
% Handle colored terms followed by normal processing
wqs1(-(C, N)):-
is_color(C),
!,
color_print(C, call(writeq(C))),
write('-'),
wqs(N).
/*
@predicate wqs1/1
@desc Main predicate that dispatches based on the structure and properties of the input.
*/
% If N is not 0 and C is an attributed variable, extract attributes and continue processing with wqs/1.
wqs1(cc(C,N)):-
N \== 0,
attvar(C),
get_attrs(C,PC),
!,
wqs(ccc(PC,N)).
% If N is not 0 and C is an unbound variable, format C and continue processing.
wqs1(cc(C,N)):-
N \== 0,
var(C),
sformat(PC,"~p",[C]),
!,
wqs(ccc(PC,N)).
% If C is not an argument string, process it with color_print and continue with wqs/1.
wqs1(cc(C,N)):-
\+ arg_string(C),
wots_hs(S,color_print(C,C)),
wqs(cc(S,N)).
% Handle color_print if C is a valid color, printing with a non-breaking space.
wqs1(color_print(C,X)):-
is_color(C),
!,
write_nbsp,
color_print(C,X).
% Handle color_print if C is not a plain variable, printing with a non-breaking space.
wqs1(color_print(C,X)):-
\+ plain_var(C),
!,
write_nbsp,
color_print(C,X).
% Handle grid-like arguments with an area less than 5.
wqs1(X):-
into_f_arg1(X,_,Arg),
is_gridoid(Arg),
area_or_len(Arg,Area),
Area < 5,
writeq(X),
!.
/* previously: wqs1(C):- callable(C), is_wqs(C), wots_vs(S,catch(C,_,fail)),write((S)).
Comment: This was skipped because it's attempting to execute `C` as a goal and catch errors.
The logic is disabled but kept for potential future use.
*/
% Handle grid-like arguments using custom print logic for gridoid structures.
wqs1(X):-
is_gridoid_arg1(X),
print_gridoid_arg1(X).
/*
@predicate into_f_arg1/3
@desc Decompose compound terms into functor and single argument.
@example into_f_arg1(foo(bar), F, Arg) results in F = foo, Arg = bar.
*/
% Decompose a compound term into its functor F and argument Arg.
into_f_arg1(X,F,Arg):-
compound(X),
compound_name_arguments(X,F,[Arg]),
compound(Arg).
/*
@predicate is_gridoid_arg1/1
@desc Checks if the first argument of a term is a grid-like structure.
*/
% Check if the argument of X is a grid-like structure.
is_gridoid_arg1(X):-
into_f_arg1(X,_F,Arg),
is_gridoid(Arg).
/*
@predicate print_gridoid_arg1/1
@desc Print a gridoid structure with its functor and formatted argument.
*/
% Print the functor and argument for grid-like structures.
print_gridoid_arg1(X):-
into_f_arg1(X,F,Arg),
print_gridoid_arg1(F,Arg).
% If HTML is not required, format and print the grid structure with padding.
print_gridoid_arg1(F,Arg):-
\+ wants_html,
!,
wots_vs(VS,wqs(Arg)),
writeq(F),
write('(`'),
!,
print_with_pad(write(VS)),
write('`)').
% If HTML is required, wrap the grid structure in a styled HTML span.
print_gridoid_arg1(F,Arg):-
wots_vs(VS,wqs(Arg)),
with_tag_style(span,"display: inline; white-space: nowrap",(writeq(F),write('({'),!,write(VS),write('})'))).
/*
@predicate nl_needed/1
@desc Check if a newline is required based on the current line position.
*/
% If the current line position is beyond N, a newline is needed.
nl_needed(N):-
line_position(current_output,L1),
L1 >= N.
/*
@predicate nl_now/0
@desc Print a newline if necessary.
*/
% Handle newline based on whether HTML output is required.
nl_now :-
wants_html,
!,
nl_if_needed_ansi.
nl_now :-
nl.
/*
@predicate nl_if_needed/0
@desc Prints a newline if required based on the current line formatting.
*/
% Output a newline if ANSI formatting is enabled, or if HTML is in use.
nl_if_needed :-
ansi_main,
!,
format('~N').
nl_if_needed :-
ansi_in_pre,
ignore((nl_needed(11),write('
'))),
!.
nl_if_needed :-
wants_html,
!,
ignore((nl_needed(11),write('
\n'))).
nl_if_needed :-
format('~N').
/* previously: nl_if_needed_ansi was skipped, logic preserved for special formatting cases */
nl_if_needed_ansi :-
\+ ansi_main,
wants_html,
!.
nl_if_needed_ansi :-
nl_if_needed.
/*
@predicate write_nbsp/0
@desc Writes a non-breaking space depending on the output format (ANSI or HTML).
*/
% Output a non-breaking space based on the current output format.
write_nbsp :-
ansi_main,
!,
write(' ').
write_nbsp :-
wants_html,
!,
write(' ').
write_nbsp :-
write(' ').
/*
@predicate is_breaker/1
@desc Determines if a term is considered a "breaker" (e.g., terms with arity >= 3).
*/
% A "breaker" is defined as a compound term with arity 3 or greater.
is_breaker(P):-
compound(P),
functor(P,_,A),
A >= 3.
/*
@predicate last_f/2
@desc Extracts the functor and arity from a compound term.
*/
% Extract the functor F from a non-compound term.
last_f(H,F):-
\+ compound(H),
data_type(H,F).
% Extract the functor and arity from a compound term.
last_f(H,F/A):-
compound(H),
!,
functor(H,F,A).
/*
@predicate need_nl/2
@desc Determines if a newline is needed between certain terms based on line positioning and patterns.
*/
% Insert a newline if necessary based on the structure of the terms.
need_nl(H0,[H1,H2|_]):-
H1 \= cc(_,_),
last_f(H0,F0),
last_f(H1,F1),
last_f(H2,F2),
F0 \== F1,
F1 == F2,
!,
format('~N ').
/* previously: need_nl logic was extended to handle nested conditions; disabled sections for readability */
% No newline needed in this case.
need_nl(_,_).
/*
need_nl(_Last,[H|_]):- last_f(H,F),
once(nb_current(last_h,cc(LF,C));(LF=F,C=0)),
(LF==F-> (write_nbsp, plus(C,1,CC), nb_setval(last_h,cc(F,CC))) ; ((C>2 -> nl_now ; write_nbsp), nb_setval(last_h,cc(F,0)))).
need_nl(_,_):- wants_html,!,write_nbsp.
%need_nl(_,_):- !,write_nbsp.
need_nl(H,[P|_]):- \+ is_breaker(H),is_breaker(P),line_position(user_output,L1),L1>80,nl_now,bformatc1('\t\t').
need_nl(_,_):- line_position(user_output,L1),L1>160,nl_now,bformatc1('\t\t').
need_nl(_,_).
*/
dash_chars:- wants_html,!,section_break.
dash_chars:- dash_chars(40),!.
dash_chars(_):- wants_html,!,section_break.
dash_chars(H):- integer(H), dash_border(H).
dash_chars(S):- nl_if_needed,dash_chars(60,S),nl_if_needed_ansi.
dash_chars(_,_):- wants_html,!,section_break.
/* File Directive: Ensure all predicates are documented and preserved for code history */
/**
* dash_chars(+H, +C) is det.
*
* Recursively prints characters 'C' for 'H' times.
* If H < 1, the predicate cuts immediately.
*
* @param H Number of repetitions.
* @param C Character to be printed.
* @example dash_chars(5, '-').
* This will output '-----'.
*/
dash_chars(H, _) :-
% If H is less than 1, cut and do nothing
H < 1, !.
dash_chars(H, C) :-
% For each number between 0 and H, call bformatc1 with character C
forall(between(0, H, _), bformatc1(C)).
/* previously: % section_break was supposed to add HTML formatting in a certain mode (wants_html) */
% The section_break predicate was originally meant to insert an HTML break, but that functionality was removed.
/**
* section_break is det.
*
* Outputs a section break, potentially useful for marking different parts of output.
*/
section_break.
/* previously: Attempt to insert a Unicode border in specific output conditions was disabled for simplicity */
/* The commented-out code for `dash_uborder_no_nl_1` would use different logic based on line positioning,
but now we use a simpler method that always prints a default upper border. */
dash_uborder_no_nl_1 :-
% Simply format and print the default Unicode upper border
bformatc1('\u00AF\u00AF\u00AF ').
dash_uborder_no_nl_1 :-
% Use uborder to get the border style and print it with a space
uborder(Short, Long), !,
bformatc1(Short),
bformatc1(Long),
write_nbsp.
/**
* dash_uborder_no_nl(+Width) is det.
*
* Prints a dashed upper border based on the given width.
* Uses different strategies for width equal to 1 or greater.
*
* @param Width The width of the upper border to print.
* @example dash_uborder_no_nl(3).
* This prints a border of width 3 using the appropriate Unicode characters.
*/
dash_uborder_no_nl(1) :- !, dash_uborder_no_nl_1.
dash_uborder_no_nl(Width) :-
% When width is greater than 1, print spaces and Unicode characters
WidthM1 is Width - 1,
uborder(Short, Long),
write_nbsp,
write(Short),
dash_chars(WidthM1, Long), !.
dash_uborder_no_nl(Width) :-
% Alternative border style
WidthM1 is Width - 1,
write_nbsp,
bformat('\u00AF'),
dash_chars(WidthM1, '\u00AF\u00AF'), !.
dash_uborder_no_nl(Width) :-
% Another fallback option using different formatting rules
nl_if_needed,
WidthM1 is Width - 1,
bformatc1(' \u00AF'),
dash_chars(WidthM1, '\u00AF\u00AF').
/**
* dash_uborder(+Width) is det.
*
* Prints a dashed upper border and ensures a newline afterward.
*
* @param Width The width of the upper border.
*/
dash_uborder(Width) :-
% Print the border, ensuring a newline is added afterward
nl_if_needed,
dash_uborder_no_nl(Width),
nl_now.
/**
* uborder(?Short, ?Long) is det.
*
* Determines the characters to use for borders based on stream encoding.
* Falls back to different border styles based on whether the stream supports UTF-8.
*
* @param Short The short (single character) border.
* @param Long The long (repeated character) border.
*/
uborder('-', '--') :-
% If the current stream supports UTF-8 encoding, use Unicode borders
stream_property(current_output, encoding(utf8)), !.
uborder('\u00AF', '\u00AF\u00AF') :- !. % Use the Unicode character for the upper border
/* previously: The alternative case for non-UTF8 encodings was removed as it seems less useful nowadays */
/**
* dash_border_no_nl(+Width) is det.
*
* Prints a dashed bottom border with no newline.
*
* @param Width The width of the bottom border.
*/
dash_border_no_nl(Width) :-
% Print a bottom border only if needed based on line position
nl_if_needed,
WidthM1 is Width - 1,
bformatc1(' _'),
dash_chars(WidthM1, '__').
/**
* dash_border(+Width) is det.
*
* Prints a dashed bottom border and ensures a newline afterward.
*
* @param Width The width of the bottom border.
*/
dash_border(Width) :-
% Call dash_border_no_nl and make sure to add a newline at the end
!, dash_border_no_nl(Width), nl_now, !.
/**
* functor_test_color(+TestResult, -Color) is det.
*
* Maps a test result to a corresponding color.
*
* @param TestResult The result of a test (e.g., pass, fail, warn).
* @param Color The color associated with that result.
*/
functor_test_color(pass, green).
functor_test_color(fail, red).
functor_test_color(warn, yellow).
/**
* arcdbg(+G) is det.
*
* Debugging tool that prints a structured representation of the given goal or structure.
* If the input is a virtual machine map, it prints the map. Otherwise, it uses colors to display compound terms.
*
* @param G The goal or structure to debug.
*/
arcdbg(G) :-
% Check if G is a virtual machine map and print accordingly
is_vm_map(G), !,
write_map(G, 'arcdbg').
arcdbg(G) :-
% If G is a compound term, get the functor and color it based on the functor's name
compound(G),
compound_name_arity(G, F, _),
functor_test_color(F, C),
wots_hs(S, print(G)),
color_print(C, S),
!,
nl_if_needed_ansi.
arcdbg(G) :-
% Otherwise, just log the goal for debugging
u_dmsg(G).
/* previously: The portray clauses for the user module were disabled for performance reasons */
/* This commented code seems to have been skipped in favor of less complex portray logic.
It was supposed to handle specific types of data structures like grids or objects. */
/**
* n_times(+N, :Goal) is det.
*
* Repeats a given goal N times.
*
* @param N The number of times to repeat the goal.
* @param Goal The goal to execute repeatedly.
*/
n_times(N, Goal) :-
% For every number between 1 and N, execute the goal, ignoring failures
forall(between(1, N, _), ignore(Goal)).
/**
* banner_lines(+Color, +N) is det.
*
* Prints banner lines of a specified color and thickness (number of lines).
*
* @param Color The color to use for the banners.
* @param N The thickness of the banner in lines.
*/
banner_lines(Color) :- banner_lines(Color, 1).
banner_lines(Color, N) :-
% If HTML output is desired, format with HTML tags
wants_html, !,
format('\n