751 lines
20 KiB
Perl
751 lines
20 KiB
Perl
|
/* $Id: chr_runtime.pl,v 1.1 2005-10-28 17:41:30 vsc Exp $
|
||
|
|
||
|
Part of CHR (Constraint Handling Rules)
|
||
|
|
||
|
Author: Christian Holzbaur and Tom Schrijvers
|
||
|
E-mail: christian@ai.univie.ac.at
|
||
|
Tom.Schrijvers@cs.kuleuven.ac.be
|
||
|
WWW: http://www.swi-prolog.org
|
||
|
Copyright (C): 2003-2004, K.U. Leuven
|
||
|
|
||
|
This program is free software; you can redistribute it and/or
|
||
|
modify it under the terms of the GNU General Public License
|
||
|
as published by the Free Software Foundation; either version 2
|
||
|
of the License, or (at your option) any later version.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU Lesser General Public
|
||
|
License along with this library; if not, write to the Free Software
|
||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||
|
|
||
|
As a special exception, if you link this library with other files,
|
||
|
compiled with a Free Software compiler, to produce an executable, this
|
||
|
library does not by itself cause the resulting executable to be covered
|
||
|
by the GNU General Public License. This exception does not however
|
||
|
invalidate any other reasons why the executable file might be covered by
|
||
|
the GNU General Public License.
|
||
|
|
||
|
Distributed with SWI-Prolog under the above conditions with
|
||
|
permission from the authors.
|
||
|
*/
|
||
|
|
||
|
|
||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
|
%% _ _ _
|
||
|
%% ___| |__ _ __ _ __ _ _ _ __ | |_(_)_ __ ___ ___
|
||
|
%% / __| '_ \| '__| | '__| | | | '_ \| __| | '_ ` _ \ / _ \
|
||
|
%% | (__| | | | | | | | |_| | | | | |_| | | | | | | __/
|
||
|
%% \___|_| |_|_| |_| \__,_|_| |_|\__|_|_| |_| |_|\___|
|
||
|
%%
|
||
|
%% hProlog CHR runtime:
|
||
|
%%
|
||
|
%% * based on the SICStus CHR runtime by Christian Holzbaur
|
||
|
%%
|
||
|
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
|
%% % Constraint Handling Rules version 2.2 %
|
||
|
%% % %
|
||
|
%% % (c) Copyright 1996-98 %
|
||
|
%% % LMU, Muenchen %
|
||
|
%% % %
|
||
|
%% % File: chr.pl %
|
||
|
%% % Author: Christian Holzbaur christian@ai.univie.ac.at %
|
||
|
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
|
%%
|
||
|
%%
|
||
|
%% * modified by Tom Schrijvers, K.U.Leuven, Tom.Schrijvers@cs.kuleuven.ac.be
|
||
|
%% - ported to hProlog
|
||
|
%% - modified for eager suspension removal
|
||
|
%%
|
||
|
%% * First working version: 6 June 2003
|
||
|
%%
|
||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
|
%% SWI-Prolog changes
|
||
|
%%
|
||
|
%% * Added initialization directives for saved-states
|
||
|
%% * Renamed merge/3 --> sbag_merge/3 (name conflict)
|
||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
|
|
||
|
:- module(chr_runtime,
|
||
|
[ 'chr sbag_del_element'/3,
|
||
|
'chr sbag_member'/2,
|
||
|
'chr merge_attributes'/3,
|
||
|
|
||
|
'chr run_suspensions'/1,
|
||
|
'chr run_suspensions_loop'/1,
|
||
|
|
||
|
'chr run_suspensions_d'/1,
|
||
|
'chr run_suspensions_loop_d'/1,
|
||
|
|
||
|
'chr insert_constraint_internal'/5,
|
||
|
'chr remove_constraint_internal'/2,
|
||
|
'chr allocate_constraint'/4,
|
||
|
'chr activate_constraint'/3,
|
||
|
|
||
|
'chr global_term_ref_1'/1,
|
||
|
|
||
|
'chr via_1'/2,
|
||
|
'chr via_2'/3,
|
||
|
'chr via'/2,
|
||
|
|
||
|
'chr lock'/1,
|
||
|
'chr unlock'/1,
|
||
|
'chr not_locked'/1,
|
||
|
'chr none_locked'/1,
|
||
|
|
||
|
'chr update_mutable'/2,
|
||
|
'chr get_mutable'/2,
|
||
|
|
||
|
'chr novel_production'/2,
|
||
|
'chr extend_history'/2,
|
||
|
'chr empty_history'/1,
|
||
|
|
||
|
'chr gen_id'/1,
|
||
|
|
||
|
'chr debug_event'/1,
|
||
|
'chr debug command'/2, % Char, Command
|
||
|
|
||
|
'chr chr_indexed_variables'/2,
|
||
|
|
||
|
chr_trace/0,
|
||
|
chr_notrace/0,
|
||
|
chr_leash/1
|
||
|
]).
|
||
|
:- set_prolog_flag(generate_debug_info, false).
|
||
|
|
||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
|
|
||
|
:- use_module(library(assoc)).
|
||
|
:- use_module(hprolog).
|
||
|
%:- use_module(library(lists)).
|
||
|
:- include(chr_op).
|
||
|
|
||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
|
|
||
|
% I N I T I A L I S A T I O N
|
||
|
|
||
|
chr_init :-
|
||
|
nb_setval(id,0),
|
||
|
nb_setval(chr_global,_),
|
||
|
nb_setval(chr_debug,mutable(off)),
|
||
|
nb_setval(chr_debug_history,mutable([],0)).
|
||
|
|
||
|
:- initialization chr_init.
|
||
|
|
||
|
show_store(Mod) :-
|
||
|
(
|
||
|
Mod:'$enumerate_suspensions'(Susp),
|
||
|
arg(6,Susp,C),
|
||
|
writeln(C),
|
||
|
fail
|
||
|
;
|
||
|
true
|
||
|
).
|
||
|
|
||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
|
'chr merge_attributes'( As, Bs, Cs) :-
|
||
|
sbag_union(As,Bs,Cs).
|
||
|
|
||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
|
'chr run_suspensions'( Slots) :-
|
||
|
run_suspensions( Slots).
|
||
|
|
||
|
'chr run_suspensions_loop'([]).
|
||
|
'chr run_suspensions_loop'([L|Ls]) :-
|
||
|
run_suspensions(L),
|
||
|
'chr run_suspensions_loop'(Ls).
|
||
|
|
||
|
run_suspensions([]).
|
||
|
run_suspensions([S|Next] ) :-
|
||
|
arg( 2, S, Mref),
|
||
|
Mref = mutable(Status), % get_mutable( Status, Mref), % XXX Inlined
|
||
|
( Status==active ->
|
||
|
update_mutable( triggered, Mref),
|
||
|
arg( 4, S, Gref),
|
||
|
Gref = mutable(Gen), % get_mutable( Gen, Gref), % XXX Inlined
|
||
|
Generation is Gen+1,
|
||
|
update_mutable( Generation, Gref),
|
||
|
arg( 3, S, Goal),
|
||
|
call( Goal),
|
||
|
% get_mutable( Post, Mref), % XXX Inlined
|
||
|
( Mref = mutable(triggered) -> % Post==triggered ->
|
||
|
update_mutable( removed, Mref)
|
||
|
;
|
||
|
true
|
||
|
)
|
||
|
;
|
||
|
true
|
||
|
),
|
||
|
run_suspensions( Next).
|
||
|
|
||
|
'chr run_suspensions_d'( Slots) :-
|
||
|
run_suspensions_d( Slots).
|
||
|
|
||
|
'chr run_suspensions_loop_d'([]).
|
||
|
'chr run_suspensions_loop_d'([L|Ls]) :-
|
||
|
run_suspensions_d(L),
|
||
|
'chr run_suspensions_loop_d'(Ls).
|
||
|
|
||
|
run_suspensions_d([]).
|
||
|
run_suspensions_d([S|Next] ) :-
|
||
|
arg( 2, S, Mref),
|
||
|
Mref = mutable(Status), % get_mutable( Status, Mref), % XXX Inlined
|
||
|
( Status==active ->
|
||
|
update_mutable( triggered, Mref),
|
||
|
arg( 4, S, Gref),
|
||
|
Gref = mutable(Gen), % get_mutable( Gen, Gref), % XXX Inlined
|
||
|
Generation is Gen+1,
|
||
|
update_mutable( Generation, Gref),
|
||
|
arg( 3, S, Goal),
|
||
|
(
|
||
|
'chr debug_event'(wake(S)),
|
||
|
call( Goal)
|
||
|
;
|
||
|
'chr debug_event'(fail(S)), !,
|
||
|
fail
|
||
|
),
|
||
|
(
|
||
|
'chr debug_event'(exit(S))
|
||
|
;
|
||
|
'chr debug_event'(redo(S)),
|
||
|
fail
|
||
|
),
|
||
|
% get_mutable( Post, Mref), % XXX Inlined
|
||
|
( Mref = mutable(triggered) -> % Post==triggered ->
|
||
|
update_mutable( removed, Mref)
|
||
|
;
|
||
|
true
|
||
|
)
|
||
|
;
|
||
|
true
|
||
|
),
|
||
|
run_suspensions_d( Next).
|
||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
|
locked:attr_unify_hook(_,_) :- fail.
|
||
|
|
||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
|
'chr lock'(T) :-
|
||
|
lock(T).
|
||
|
|
||
|
'chr unlock'(T) :-
|
||
|
unlock(T).
|
||
|
|
||
|
'chr not_locked'(T) :-
|
||
|
not_locked(T).
|
||
|
|
||
|
lock(T) :-
|
||
|
( var(T)
|
||
|
-> put_attr(T, locked, x)
|
||
|
; term_variables(T,L),
|
||
|
lockv(L)
|
||
|
).
|
||
|
|
||
|
lockv([]).
|
||
|
lockv([T|R]) :- put_attr( T, locked, x), lockv(R).
|
||
|
|
||
|
unlock(T) :-
|
||
|
( var(T)
|
||
|
-> del_attr(T, locked)
|
||
|
; term_variables(T,L),
|
||
|
unlockv(L)
|
||
|
).
|
||
|
|
||
|
unlockv([]).
|
||
|
unlockv([T|R]) :- del_attr( T, locked), unlockv(R).
|
||
|
|
||
|
'chr none_locked'( []).
|
||
|
'chr none_locked'( [V|Vs]) :-
|
||
|
not_locked( V),
|
||
|
'chr none_locked'( Vs).
|
||
|
|
||
|
not_locked( V) :-
|
||
|
( var( V) ->
|
||
|
( get_attr( V, locked, _) ->
|
||
|
fail
|
||
|
;
|
||
|
true
|
||
|
)
|
||
|
;
|
||
|
true
|
||
|
).
|
||
|
|
||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
|
%
|
||
|
% Eager removal from all chains.
|
||
|
%
|
||
|
'chr remove_constraint_internal'( Susp, Agenda) :-
|
||
|
arg( 2, Susp, Mref),
|
||
|
Mref = mutable(State), % get_mutable( State, Mref), % XXX Inlined
|
||
|
update_mutable( removed, Mref), % mark in any case
|
||
|
( compound(State) -> % passive/1
|
||
|
Agenda = []
|
||
|
; State==removed ->
|
||
|
Agenda = []
|
||
|
%; State==triggered ->
|
||
|
% Agenda = []
|
||
|
;
|
||
|
Susp =.. [_,_,_,_,_,_,_|Args],
|
||
|
term_variables( Args, Vars),
|
||
|
global_term_ref_1( Global),
|
||
|
Agenda = [Global|Vars]
|
||
|
).
|
||
|
|
||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
|
'chr via_1'(X,V) :-
|
||
|
( var(X) ->
|
||
|
X = V
|
||
|
; atomic(X) ->
|
||
|
global_term_ref_1(V)
|
||
|
; nonground(X,V) ->
|
||
|
true
|
||
|
;
|
||
|
global_term_ref_1(V)
|
||
|
).
|
||
|
% 'chr via_1'( X, V) :- var(X), !, X=V.
|
||
|
% 'chr via_1'( T, V) :- compound(T), nonground( T, V), ! .
|
||
|
% 'chr via_1'( _, V) :- global_term_ref_1( V).
|
||
|
|
||
|
'chr via_2'(X,Y,V) :-
|
||
|
( var(X) ->
|
||
|
X = V
|
||
|
; var(Y) ->
|
||
|
Y = V
|
||
|
; compound(X), nonground(X,V) ->
|
||
|
true
|
||
|
; compound(Y), nonground(Y,V) ->
|
||
|
true
|
||
|
;
|
||
|
global_term_ref_1(V)
|
||
|
).
|
||
|
% 'chr via_2'( X, _, V) :- var(X), !, X=V.
|
||
|
% 'chr via_2'( _, Y, V) :- var(Y), !, Y=V.
|
||
|
% 'chr via_2'( T, _, V) :- compound(T), nonground( T, V), ! .
|
||
|
% 'chr via_2'( _, T, V) :- compound(T), nonground( T, V), ! .
|
||
|
% 'chr via_2'( _, _, V) :- global_term_ref_1( V).
|
||
|
|
||
|
%
|
||
|
% The second arg is a witness.
|
||
|
% The formulation with term_variables/2 is
|
||
|
% cycle safe, but it finds a list of all vars.
|
||
|
% We need only one, and no list in particular.
|
||
|
%
|
||
|
'chr via'(L,V) :-
|
||
|
( nonground(L,V) ->
|
||
|
true
|
||
|
;
|
||
|
global_term_ref_1(V)
|
||
|
).
|
||
|
|
||
|
nonground( Term, V) :-
|
||
|
term_variables( Term, Vs),
|
||
|
Vs = [V|_].
|
||
|
|
||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
|
'chr novel_production'( Self, Tuple) :-
|
||
|
arg( 5, Self, Ref),
|
||
|
Ref = mutable(History), % get_mutable( History, Ref), % XXX Inlined
|
||
|
( get_assoc( Tuple, History, _) ->
|
||
|
fail
|
||
|
;
|
||
|
true
|
||
|
).
|
||
|
|
||
|
%
|
||
|
% Not folded with novel_production/2 because guard checking
|
||
|
% goes in between the two calls.
|
||
|
%
|
||
|
'chr extend_history'( Self, Tuple) :-
|
||
|
arg( 5, Self, Ref),
|
||
|
Ref = mutable(History), % get_mutable( History, Ref), % XXX Inlined
|
||
|
put_assoc( Tuple, History, x, NewHistory),
|
||
|
update_mutable( NewHistory, Ref).
|
||
|
|
||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
|
constraint_generation( Susp, State, Generation) :-
|
||
|
arg( 2, Susp, Mref),
|
||
|
Mref = mutable(State), % get_mutable( State, Mref), % XXX Inlined
|
||
|
arg( 4, Susp, Gref),
|
||
|
Gref = mutable(Generation). % get_mutable( Generation, Gref). % not incremented meanwhile % XXX Inlined
|
||
|
|
||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
|
'chr allocate_constraint'( Closure, Self, F, Args) :-
|
||
|
'chr empty_history'( History),
|
||
|
create_mutable( passive(Args), Mref),
|
||
|
create_mutable( 0, Gref),
|
||
|
create_mutable( History, Href),
|
||
|
'chr gen_id'( Id),
|
||
|
Self =.. [suspension,Id,Mref,Closure,Gref,Href,F|Args].
|
||
|
|
||
|
%
|
||
|
% 'chr activate_constraint'( -, +, -).
|
||
|
%
|
||
|
% The transition gc->active should be rare
|
||
|
%
|
||
|
'chr activate_constraint'( Vars, Susp, Generation) :-
|
||
|
arg( 2, Susp, Mref),
|
||
|
Mref = mutable(State), % get_mutable( State, Mref), % XXX Inlined
|
||
|
update_mutable( active, Mref),
|
||
|
( nonvar(Generation) -> % aih
|
||
|
true
|
||
|
;
|
||
|
arg( 4, Susp, Gref),
|
||
|
Gref = mutable(Gen), % get_mutable( Gen, Gref), % XXX Inlined
|
||
|
Generation is Gen+1,
|
||
|
update_mutable( Generation, Gref)
|
||
|
),
|
||
|
( compound(State) -> % passive/1
|
||
|
term_variables( State, Vs),
|
||
|
'chr none_locked'( Vs),
|
||
|
global_term_ref_1( Global),
|
||
|
Vars = [Global|Vs]
|
||
|
; State==removed -> % the price for eager removal ...
|
||
|
Susp =.. [_,_,_,_,_,_,_|Args],
|
||
|
term_variables( Args, Vs),
|
||
|
global_term_ref_1( Global),
|
||
|
Vars = [Global|Vs]
|
||
|
;
|
||
|
Vars = []
|
||
|
).
|
||
|
|
||
|
'chr insert_constraint_internal'( [Global|Vars], Self, Closure, F, Args) :-
|
||
|
term_variables( Args, Vars),
|
||
|
'chr none_locked'( Vars),
|
||
|
global_term_ref_1( Global),
|
||
|
'chr empty_history'( History),
|
||
|
create_mutable( active, Mref),
|
||
|
create_mutable( 0, Gref),
|
||
|
create_mutable( History, Href),
|
||
|
'chr gen_id'( Id),
|
||
|
Self =.. [suspension,Id,Mref,Closure,Gref,Href,F|Args].
|
||
|
|
||
|
insert_constraint_internal( [Global|Vars], Self, Term, Closure, F, Args) :-
|
||
|
term_variables( Term, Vars),
|
||
|
'chr none_locked'( Vars),
|
||
|
global_term_ref_1( Global),
|
||
|
'chr empty_history'( History),
|
||
|
create_mutable( active, Mref),
|
||
|
create_mutable( 0, Gref),
|
||
|
create_mutable( History, Href),
|
||
|
'chr gen_id'( Id),
|
||
|
Self =.. [suspension,Id,Mref,Closure,Gref,Href,F|Args].
|
||
|
|
||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
|
change_state( Susp, State) :-
|
||
|
arg( 2, Susp, Mref),
|
||
|
update_mutable( State, Mref).
|
||
|
|
||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
|
'chr empty_history'( E) :- empty_assoc( E).
|
||
|
|
||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
|
'chr gen_id'( Id) :-
|
||
|
incval( id, Id).
|
||
|
|
||
|
incval(id,Id) :-
|
||
|
nb_getval(id,Id),
|
||
|
NextId is Id + 1,
|
||
|
nb_setval(id,NextId).
|
||
|
|
||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
|
create_mutable(V,mutable(V)).
|
||
|
|
||
|
'chr get_mutable'(V, mutable(V)).
|
||
|
|
||
|
'chr update_mutable'(V,M) :-
|
||
|
setarg(1,M,V).
|
||
|
|
||
|
get_mutable(V, mutable(V)).
|
||
|
|
||
|
update_mutable(V,M) :-
|
||
|
setarg(1,M,V).
|
||
|
|
||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
|
'chr global_term_ref_1'(X) :-
|
||
|
global_term_ref_1(X).
|
||
|
|
||
|
global_term_ref_1(X) :-
|
||
|
nb_getval(chr_global,X).
|
||
|
|
||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
|
|
||
|
'chr sbag_member'( Element, [Head|Tail]) :-
|
||
|
sbag_member( Element, Tail, Head).
|
||
|
|
||
|
% auxiliary to avoid choicepoint for last element
|
||
|
|
||
|
sbag_member( E, _, E).
|
||
|
sbag_member( E, [Head|Tail], _) :-
|
||
|
sbag_member( E, Tail, Head).
|
||
|
|
||
|
'chr sbag_del_element'( [], _, []).
|
||
|
'chr sbag_del_element'( [X|Xs], Elem, Set2) :-
|
||
|
( X==Elem ->
|
||
|
Set2 = Xs
|
||
|
;
|
||
|
Set2 = [X|Xss],
|
||
|
'chr sbag_del_element'( Xs, Elem, Xss)
|
||
|
).
|
||
|
|
||
|
sbag_union( A, B, C) :-
|
||
|
sbag_merge( A, B, C).
|
||
|
|
||
|
sbag_merge([],Ys,Ys).
|
||
|
sbag_merge([X | Xs],YL,R) :-
|
||
|
( YL = [Y | Ys] ->
|
||
|
arg(1,X,XId),
|
||
|
arg(1,Y,YId),
|
||
|
( XId < YId ->
|
||
|
R = [X | T],
|
||
|
sbag_merge(Xs,YL,T)
|
||
|
; XId > YId ->
|
||
|
R = [Y | T],
|
||
|
sbag_merge([X|Xs],Ys,T)
|
||
|
;
|
||
|
R = [X | T],
|
||
|
sbag_merge(Xs,Ys,T)
|
||
|
)
|
||
|
;
|
||
|
R = [X | Xs]
|
||
|
).
|
||
|
|
||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
|
|
||
|
:- multifile
|
||
|
chr:debug_event/2, % +State, +Event
|
||
|
chr:debug_interact/3. % +Event, +Depth, -Command
|
||
|
|
||
|
'chr debug_event'(Event) :-
|
||
|
nb_getval(chr_debug,mutable(State)),
|
||
|
( State == off ->
|
||
|
true
|
||
|
; chr:debug_event(State, Event) ->
|
||
|
true
|
||
|
; debug_event(State,Event)
|
||
|
).
|
||
|
|
||
|
chr_trace :-
|
||
|
nb_setval(chr_debug,mutable(trace)).
|
||
|
chr_notrace :-
|
||
|
nb_setval(chr_debug,mutable(off)).
|
||
|
|
||
|
% chr_leash(+Spec)
|
||
|
%
|
||
|
% Define the set of ports at which we prompt for user interaction
|
||
|
|
||
|
chr_leash(Spec) :-
|
||
|
leashed_ports(Spec, Ports),
|
||
|
nb_setval(chr_leash,mutable(Ports)).
|
||
|
|
||
|
leashed_ports(none, []).
|
||
|
leashed_ports(off, []).
|
||
|
leashed_ports(all, [call, exit, redo, fail, wake, try, apply, insert, remove]).
|
||
|
leashed_ports(default, [call,exit,fail,wake,apply]).
|
||
|
leashed_ports(One, Ports) :-
|
||
|
atom(One), One \== [], !,
|
||
|
leashed_ports([One], Ports).
|
||
|
leashed_ports(Set, Ports) :-
|
||
|
sort(Set, Ports), % make unique
|
||
|
leashed_ports(all, All),
|
||
|
valid_ports(Ports, All).
|
||
|
|
||
|
valid_ports([], _).
|
||
|
valid_ports([H|T], Valid) :-
|
||
|
( memberchk(H, Valid)
|
||
|
-> true
|
||
|
; throw(error(domain_error(chr_port, H), _))
|
||
|
),
|
||
|
valid_ports(T, Valid).
|
||
|
|
||
|
|
||
|
:- initialization
|
||
|
leashed_ports(default, Ports),
|
||
|
nb_setval(chr_leash, mutable(Ports)).
|
||
|
|
||
|
% debug_event(+State, +Event)
|
||
|
|
||
|
|
||
|
%debug_event(trace, Event) :-
|
||
|
% functor(Event, Name, Arity),
|
||
|
% writeln(Name/Arity), fail.
|
||
|
debug_event(trace,Event) :-
|
||
|
Event = call(_), !,
|
||
|
get_debug_history(History,Depth),
|
||
|
NDepth is Depth + 1,
|
||
|
chr_debug_interact(Event,NDepth),
|
||
|
set_debug_history([Event|History],NDepth).
|
||
|
debug_event(trace,Event) :-
|
||
|
Event = wake(_), !,
|
||
|
get_debug_history(History,Depth),
|
||
|
NDepth is Depth + 1,
|
||
|
chr_debug_interact(Event,NDepth),
|
||
|
set_debug_history([Event|History],NDepth).
|
||
|
debug_event(trace,Event) :-
|
||
|
Event = redo(_), !,
|
||
|
get_debug_history(_History, Depth),
|
||
|
chr_debug_interact(Event, Depth).
|
||
|
debug_event(trace,Event) :-
|
||
|
Event = exit(_),!,
|
||
|
get_debug_history([_|History],Depth),
|
||
|
chr_debug_interact(Event,Depth),
|
||
|
NDepth is Depth - 1,
|
||
|
set_debug_history(History,NDepth).
|
||
|
debug_event(trace,Event) :-
|
||
|
Event = fail(_),!,
|
||
|
get_debug_history(_,Depth),
|
||
|
chr_debug_interact(Event,Depth).
|
||
|
debug_event(trace, Event) :-
|
||
|
Event = remove(_), !,
|
||
|
get_debug_history(_,Depth),
|
||
|
chr_debug_interact(Event, Depth).
|
||
|
debug_event(trace, Event) :-
|
||
|
Event = insert(_), !,
|
||
|
get_debug_history(_,Depth),
|
||
|
chr_debug_interact(Event, Depth).
|
||
|
debug_event(trace, Event) :-
|
||
|
Event = try(_,_,_,_), !,
|
||
|
get_debug_history(_,Depth),
|
||
|
chr_debug_interact(Event, Depth).
|
||
|
debug_event(trace, Event) :-
|
||
|
Event = apply(_,_,_,_), !,
|
||
|
get_debug_history(_,Depth),
|
||
|
chr_debug_interact(Event,Depth).
|
||
|
|
||
|
debug_event(skip(_,_),Event) :-
|
||
|
Event = call(_), !,
|
||
|
get_debug_history(History,Depth),
|
||
|
NDepth is Depth + 1,
|
||
|
set_debug_history([Event|History],NDepth).
|
||
|
debug_event(skip(_,_),Event) :-
|
||
|
Event = wake(_), !,
|
||
|
get_debug_history(History,Depth),
|
||
|
NDepth is Depth + 1,
|
||
|
set_debug_history([Event|History],NDepth).
|
||
|
debug_event(skip(SkipSusp,SkipDepth),Event) :-
|
||
|
Event = exit(Susp),!,
|
||
|
get_debug_history([_|History],Depth),
|
||
|
( SkipDepth == Depth,
|
||
|
SkipSusp == Susp ->
|
||
|
set_chr_debug(trace),
|
||
|
chr_debug_interact(Event,Depth)
|
||
|
;
|
||
|
true
|
||
|
),
|
||
|
NDepth is Depth - 1,
|
||
|
set_debug_history(History,NDepth).
|
||
|
debug_event(skip(_,_),_) :- !,
|
||
|
true.
|
||
|
|
||
|
% chr_debug_interact(+Event, +Depth)
|
||
|
%
|
||
|
% Interact with the user on Event that took place at Depth. First
|
||
|
% calls chr:debug_interact(+Event, +Depth, -Command) hook. If this
|
||
|
% fails the event is printed and the system prompts for a command.
|
||
|
|
||
|
chr_debug_interact(Event, Depth) :-
|
||
|
chr:debug_interact(Event, Depth, Command), !,
|
||
|
handle_debug_command(Command,Event,Depth).
|
||
|
chr_debug_interact(Event, Depth) :-
|
||
|
print_event(Event, Depth),
|
||
|
( leashed(Event)
|
||
|
-> ask_continue(Command)
|
||
|
; Command = creep
|
||
|
),
|
||
|
handle_debug_command(Command,Event,Depth).
|
||
|
|
||
|
leashed(Event) :-
|
||
|
functor(Event, Port, _),
|
||
|
nb_getval(chr_leash, mutable(Ports)),
|
||
|
memberchk(Port, Ports).
|
||
|
|
||
|
ask_continue(Command) :-
|
||
|
print_message(debug, chr(prompt)),
|
||
|
get_single_char(CharCode),
|
||
|
( CharCode == -1
|
||
|
-> Char = end_of_file
|
||
|
; char_code(Char, CharCode)
|
||
|
),
|
||
|
( debug_command(Char, Command)
|
||
|
-> print_message(debug, chr(command(Command)))
|
||
|
; print_message(help, chr(invalid_command)),
|
||
|
ask_continue(Command)
|
||
|
).
|
||
|
|
||
|
|
||
|
'chr debug command'(Char, Command) :-
|
||
|
debug_command(Char, Command).
|
||
|
|
||
|
debug_command(c, creep).
|
||
|
debug_command(' ', creep).
|
||
|
debug_command('\r', creep).
|
||
|
debug_command(s, skip).
|
||
|
debug_command(g, ancestors).
|
||
|
debug_command(n, nodebug).
|
||
|
debug_command(a, abort).
|
||
|
debug_command(f, fail).
|
||
|
debug_command(b, break).
|
||
|
debug_command(?, help).
|
||
|
debug_command(h, help).
|
||
|
debug_command(end_of_file, exit).
|
||
|
|
||
|
|
||
|
handle_debug_command(creep,_,_) :- !.
|
||
|
handle_debug_command(skip, Event, Depth) :- !,
|
||
|
Event =.. [Type|Rest],
|
||
|
( Type \== call,
|
||
|
Type \== wake ->
|
||
|
handle_debug_command('c',Event,Depth)
|
||
|
;
|
||
|
Rest = [Susp],
|
||
|
set_chr_debug(skip(Susp,Depth))
|
||
|
).
|
||
|
|
||
|
handle_debug_command(ancestors,Event,Depth) :- !,
|
||
|
print_chr_debug_history,
|
||
|
chr_debug_interact(Event,Depth).
|
||
|
handle_debug_command(nodebug,_,_) :- !,
|
||
|
chr_notrace.
|
||
|
handle_debug_command(abort,_,_) :- !,
|
||
|
abort.
|
||
|
handle_debug_command(exit,_,_) :- !,
|
||
|
halt.
|
||
|
handle_debug_command(fail,_,_) :- !,
|
||
|
fail.
|
||
|
handle_debug_command(break,Event,Depth) :- !,
|
||
|
break,
|
||
|
chr_debug_interact(Event,Depth).
|
||
|
handle_debug_command(help,Event,Depth) :- !,
|
||
|
print_message(help, chr(debug_options)),
|
||
|
chr_debug_interact(Event,Depth).
|
||
|
handle_debug_command(Cmd, _, _) :-
|
||
|
throw(error(domain_error(chr_debug_command, Cmd), _)).
|
||
|
|
||
|
print_chr_debug_history :-
|
||
|
get_debug_history(History,Depth),
|
||
|
print_message(debug, chr(ancestors(History, Depth))).
|
||
|
|
||
|
print_event(Event, Depth) :-
|
||
|
print_message(debug, chr(event(Event, Depth))).
|
||
|
|
||
|
% {set,get}_debug_history(Ancestors, Depth)
|
||
|
%
|
||
|
% Set/get the list of ancestors and the depth of the current goal.
|
||
|
|
||
|
get_debug_history(History,Depth) :-
|
||
|
nb_getval(chr_debug_history,mutable(History,Depth)).
|
||
|
|
||
|
set_debug_history(History,Depth) :-
|
||
|
nb_getval(chr_debug_history,Mutable),
|
||
|
setarg(1,Mutable,History),
|
||
|
setarg(2,Mutable,Depth).
|
||
|
|
||
|
set_chr_debug(State) :-
|
||
|
nb_getval(chr_debug,Mutable),
|
||
|
setarg(1,Mutable,State).
|
||
|
|
||
|
'chr chr_indexed_variables'(Susp,Vars) :-
|
||
|
Susp =.. [_,_,_,_,_,_,_|Args],
|
||
|
term_variables(Args,Vars).
|