1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
|
% Sdda3 5-Oct-86
% For use on simulator
%% To do: (look for '%%')
%% recursion - keep list of call procedures, ignore recursive calls
%% problem: doesn't work for typical procedure working on a list,
%% since the list is smaller (different) each time.
%% possible optimization: "recognize" base case & skip to it
%% follow atoms, g is 'any atom', all others unique, does it work?
%% stats - write heapused, cputime to files (as comments)
%% worst_case - handle ground terms (copy unify, modify atomic)
%% handle disjunction - needs worst_case
%% add cuts where possible to save space
%% fill in rest of built-ins
%% how to handle op?
%% Handle assert/retract? call? (If given ground terms- ok, vars- no)
%% must have ground functor, definite number of args!
% Front end for simulator use
sdda(ShowResult):-
do_sdda(test,_A,_B,_C, ShowResult).
% Does the sdda on FileName, instantiates Exitmodes to list of exit modes,
% ExitModes structure: [[Funtor/Arity, Activation, Exit], ... ],
% e.g. [[a/2, [g,X], [g,g]]
do_sdda(_FileName, ExitModes, _BackList, _PredList, ShowResult) :-
%%see(FileName),
read_procedures(Procs, ExitModes, Entries), % collect all procedures
%%seen,
( ShowResult = true ->
write('Procedures '), nl, write_list(Procs), nl,
write('Entry points '), nl, write_list(Entries), nl,
(nonvar(ExitModes) -> % Don't mention there
(write('Declared exit modes '), nl, % aren't any
write_list(ExitModes), nl) ;
true),
entry_exit_modes_list(Procs, ExitModes, Entries),
write('Exit modes '), nl, write_list(ExitModes), nl
; true).
%%% !!! Hard code in read for test:
% sdda_entry(c(A,B,C)).
% a(X, Y).
% a(X, X).
% c(A,B,C) :- a(A,B).
read_procedures([[a/2,a(_109,_110),a(_148,_148)|_184],
[c/3,(c(_191,_192,_193):-a(_191,_192))|_238]|_239],
_68,[c(_76,_77,_78)|_102]) :- !.
% For each entry point in Entries do sdda, building Known, an unbound-tail list
% Known structure: [[Name/Arity, ActivationModes, ExitModes], ...|_],
% where ActivationModes and ExitModes are lists of variables and the atom 'g'.
% 'g' represents a ground element and variables represent equivalence classes.
entry_exit_modes_list(_, _, Entries) :- % Done
var(Entries).
entry_exit_modes_list(ProcList, Known, [Entry|Entries]) :-
Entry =.. [Functor|Act], % Get functor/arity & activation
my_length(Act, Arity), % from entry declaration
proc_exit_mode(ProcList, Known, [], Functor/Arity, Act, _), % No invoc.
entry_exit_modes_list(ProcList, Known, Entries).
% Do sdda on procedure Functor/Arity, given activation mode Act. Instantiates
% Known to known exit modes and Act to exit modes for Functor/Arity under Act
proc_exit_mode(_, _, _, Functor/Arity, Act, Exit) :-
built_in(Functor/Arity, Act, Exit). % This is a built-in
proc_exit_mode(_, Known, _, Functor/Arity, Act, Exit) :-
look_up_act([Functor/Arity, Act, Exit], Known). % Already did this
proc_exit_mode(ProcList, Known, Invocations, Functor/Arity, Act, Exit) :-
umember([Functor/Arity|Clauses], ProcList), % Look up definition
dup(Clauses, ClausesCopy), % Don't munge original
clause_exit_modes_list(ProcList, Known, Invocations,
ClausesCopy, Act, Exits),
(Exits=[] -> fail ; true), % didn't find any => fail
worst_case(Exits, Exit), % assume the worst
dup(Act, ActCopy), % Need copy because Body
add_to_list([Functor/Arity, ActCopy, Exit], Known). % binds Act & Exit
proc_exit_mode(_, Known, _, Functor/Arity, Act, Exit) :-
write('No such procedure at compile time '),
Activation=..[Functor|Act],
write(Activation), nl,
all_shared(Act, Exit), % return worst possible - all shared
add_to_list([Functor/Arity, Act, Exit], Known).
my_length(L, N) :-
my_length1(L, 0, N).
my_length1([], N, N).
my_length1([_|L], M, N) :-
M1 is M+1,
my_length1(L, M1, N).
% Analyze all clauses for this procedure, instantiate Exits to all exit modes
clause_exit_modes_list(_, _, _, Clauses, _, []) :-
var(Clauses), !. % No more clauses => done
clause_exit_modes_list(ProcList, Known, Invocations,
[Clause|Clauses], Act, Exits) :-
eqmember([Clause, Act], Invocations), % This is a recursive
write('skipping clause exit mode for '),
write(Clause), write(' '), write(Act), nl,
clause_exit_modes_list(ProcList, Known, Invocations, % call, ignore
Clauses, Act, Exits). % it
clause_exit_modes_list(ProcList, Known, Invocations,
[Clause|Clauses], Act, [Exit|Exits]) :-
dup(Act, Exit), % We'll bind Exit
clause_exit_mode(ProcList, Known, [[Clause, Act]|Invocations],
Clause, Exit), % Record invocation
clause_exit_modes_list(ProcList, Known, Invocations,
Clauses, Act, Exits).
clause_exit_modes_list(ProcList, Known, Invocations,
[_Clause|Clauses], Act, Exits) :- % Unify failed
clause_exit_modes_list(ProcList, Known, Invocations,
Clauses, Act, Exits).
% Given activation modes for this clause, return its exit modes
clause_exit_mode(ProcList, Known, Invocations, Clause, Act) :-
(Clause = ':-'(Head, Body) ; Clause=Head, Body=true), % Decompose it
Head =.. [_|Args], % Bind the head
unify(Args, Act), % to activation
body_exit_mode(ProcList, Known, Invocations, Body). % do the body
body_exit_mode(ProcList, Known, Invocations, ','(Goal, Goals)) :- % Conjunction
body_exit_mode(ProcList, Known, Invocations, Goal), % Do 1st
body_exit_mode(ProcList, Known, Invocations, Goals). % & rest
body_exit_mode(ProcList, Known, Invocation, Goal) :-
functor(Goal, Functor, Arity),
Goal =.. [Functor|Act],
proc_exit_mode(ProcList, Known, Invocation, Functor/Arity, Act, Exit),
unify(Act, Exit).
% Unifies Left and Right with the special case that the atom 'g' matches
% any atom (except [])
unify(Left, Left) :- !. % Try standard unify first
unify(Left, g) :- % else, is it special case
atomic(Left), !,
\+ Left=[].
unify(g, Right) :-
atomic(Right), !,
\+ Right=[].
unify([LeftHead|LeftTail], [RightHead|RightTail]) :- % or list
!, unify(LeftHead, RightHead),
unify(LeftTail, RightTail).
unify(Left, Right) :- % or structure
Left =.. [Functor|LeftArgs],
Right =.. [Functor|RightArgs],
unify(LeftArgs, RightArgs).
% Succeed if Left and Right are equivalent, i.e. they are the exact same
% with variables renamed
equiv(Left, Right) :-
equiv(Left, Right, _).
equiv(Left, Right, _) :-
Left==Right, !.
equiv(g, Right, _) :-
atomic(Right), !,
\+ Right=[].
equiv(Left, g, _) :-
atomic(Left), !,
\+ Left=[].
equiv(Left, Right, Bindings) :-
var(Left), !,
var(Right),
equiv_vars(Left, Right, Bindings).
equiv(Left, Right, Bindings) :-
var(Right), !,
var(Left),
equiv_vars(Left, Right, Bindings).
equiv([LeftHead|LeftTail], [RightHead|RightTail], Bindings) :-
!, equiv(LeftHead, RightHead, Bindings),
equiv(LeftTail, RightTail, Bindings).
equiv(Left, Right, Bindings) :-
Left=..[Functor|LeftArgs],
Right=..[Functor|RightArgs],
equiv(LeftArgs, RightArgs, Bindings).
equiv_vars(Left, Right, Bindings) :-
var(Bindings), !,
Bindings=[[Left, Right]|_].
equiv_vars(Left, Right, [[AnyVar, AnyBinding]|_]) :-
Left==AnyVar, !,
Right==AnyBinding.
equiv_vars(Left, Right, [[AnyVar, AnyBinding]|_]) :-
Right==AnyBinding, !,
Left==AnyVar.
equiv_vars(Left, Right, [ _|Bindings]) :-
equiv_vars(Left, Right, Bindings).
% Make a copy of Orig with new vars. Copy must be a variable.
% E.g. dup([A,s(A,B),[B,C]], New) binds New to [X,s(X,Y),[Y,Z]]
dup(Orig, Copy) :-
dup(Orig, Copy, _).
dup(Orig, Copy, Bindings) :-
var(Orig), !,
dup_var(Orig, Copy, Bindings).
dup(Orig, Orig, _) :- % Atoms, including []
atomic(Orig), !.
dup([OrigHead|OrigTail], [CopyHead|CopyTail], Bindings) :-
!, dup(OrigHead, CopyHead, Bindings),
dup(OrigTail, CopyTail, Bindings).
dup(Orig, Copy, Bindings) :-
Orig=..[Functor|OrigArgs],
dup(OrigArgs, CopyArgs, Bindings),
Copy=..[Functor|CopyArgs].
dup_var(Orig, Copy, Bindings) :-
var(Bindings), !,
Bindings=[[Orig, Copy]|_].
dup_var(Orig, Copy, [[AnyVar, Copy]|_]) :-
Orig==AnyVar, !.
dup_var(Orig, Copy, [_|Bindings]) :-
dup_var(Orig, Copy, Bindings).
% ----- Built-ins ----- %
built_in(true/0, [], []). % No change
built_in(fail/0, [], []). % No change
built_in((=)/2, [X, Y], [g, g]) :-
(atomic(X) ; atomic(Y)). % Ground both if either atomic
built_in((=)/2, [X, _Y], [X, X]). % else bind them
built_in(/('+',2), [X, Y], [X, Y]). % No change
built_in(/('-',2), [X, Y], [X, Y]). % No change
built_in(/('*',2), [X, Y], [X, Y]). % No change
built_in(/('/',2), [X, Y], [X, Y]). % No change
built_in(/('>=',2), [X, Y], [X, Y]). % No change
built_in(/('<',2), [X, Y], [X, Y]). % No change
built_in((is)/2, [_X, Y], [g, Y]). % Ground result
% ----- Utilities ----- %
worst_case([], _). %% Doesn't work if any Exits
worst_case([Exit|Exits], Worst) :- %% fail to match, e.g.
unify(Exit, Worst), %% [[s(1)], [f(1)]].
worst_case(Exits, Worst).
look_up_act(_, Known) :-
var(Known),
!, fail.
look_up_act([Functor/Arity, Act, Exit], [[Functor/Arity, KnownAct, Exit]|_]) :-
equiv(Act, KnownAct).
look_up_act([Functor/Arity, Act, Exit], [_|Known]) :-
look_up_act([Functor/Arity, Act, Exit], Known).
all_shared(_Act, _Exit) :- %% Wrong
fail. % DD: I have put fail since unify/3 does not exist
/*
all_shared(Act, Exit) :- %% Wrong
unify(Act, _, VarModesList),
bind_all(_, VarModesList),
unify(Act, Exit, VarModesList).
bind_all(_, VarModesList) :-
var(VarModesList).
bind_all(Mode, [[Var, Mode]|VarModesList]) :-
var(Mode),
bind_all(Mode, VarModesList).
bind_all(Mode, [[_, _]|VarModesList]) :-
bind_all(Mode, VarModesList).
*/
% Adds Element to the tail of List, an unbound-tail list
add_to_list(Element, List) :-
var(List),
List=[Element|_].
add_to_list(Element, [_|List]) :-
add_to_list(Element, List).
% Membership relation for unbound-tail lists
umember(_, List) :-
var(List), !, fail.
umember(Element, [Element|_]).
umember(Element, [_|Tail]) :- umember(Element, Tail).
/*
% Membership relation for standard nil-tail lists
member(X, [X|_]).
member(X, [_|T]) :- member(X, T).
*/
% Equiv membership relation for standard nil-tail lists
eqmember(X, [Y|_]) :- equiv(X, Y).
eqmember(X, [_|T]) :- eqmember(X, T).
% Pretty prints unbound-tail lists -- dies on NIL tail lists
write_list(List) :-
dup(List, NewList),
(var(NewList) -> (name_vars(NewList, 0, _),
write(NewList)) ;
(write('['),
write_list2(NewList, 0, _),
write('|_].'))), % write('].') to write nil tails
nl.
write_list2([H|T], NextName, NewNextName) :-
name_vars(H, NextName, TempNextName),
write(H),
(nonvar(T) -> (write(','), nl,
write(' '),
write_list2(T, TempNextName, NewNextName)) ;
NewNextName = TempNextName).
name_vars(Term, NextName, NewNextName) :-
var(Term), !,
make_name(NextName, Term),
NewNextName is NextName + 1.
name_vars(Term, NextName, NextName) :-
atom(Term), !.
name_vars([TermHead|TermTail], NextName, NewNextName) :-
!, name_vars(TermHead, NextName, TempNextName),
name_vars(TermTail, TempNextName, NewNextName).
name_vars(Term, NextName, NewNextName) :-
Term =.. [_|TermArgs],
name_vars(TermArgs, NextName, NewNextName).
make_name(IntName, Variable) :-
Count is IntName // 26,
NewIntName is IntName mod 26 + "A",
build_name(Count, NewIntName, Name),
name(Variable, Name).
build_name(0, IntName, [IntName]) :- !.
build_name(Count, IntName, [IntName|Rest]) :- Count>0,
NewCount is Count - 1,
build_name(NewCount, IntName, Rest).
% benchmark interface
benchmark(ShowResult) :-
sdda(ShowResult).
:- include(common).
|