samer@0
|
1 :- module(dcgu, [
|
samer@0
|
2 writedcg/1
|
samer@0
|
3
|
samer@0
|
4 , nop/2
|
samer@0
|
5 , out//1
|
samer@0
|
6 , (>>)//2
|
samer@0
|
7 , (\<)//1
|
samer@0
|
8 , (\>)//1
|
samer@0
|
9 , (\#)//2
|
samer@0
|
10 , run_left//3
|
samer@0
|
11 , run_right//3
|
samer@0
|
12 , trans//2
|
samer@0
|
13
|
samer@0
|
14 , maybe//1
|
samer@0
|
15 , opt//1
|
samer@0
|
16 , if//3, if//2
|
samer@0
|
17 , parmap//2, parmap//3, parmap//4, parmap//5, parmap//6
|
samer@0
|
18 , seqmap//2, seqmap//3, seqmap//4, seqmap//5, seqmap//6
|
samer@0
|
19 , seqmap_n//3, seqmap_n//4, seqmap_n//5
|
samer@0
|
20 , seqmap_with_sep//3
|
samer@0
|
21 , seqmap_with_sep//4
|
samer@0
|
22 , seqmap_with_sep//5
|
samer@0
|
23 , seqmap_with_progress//3
|
samer@0
|
24 , seqmap_with_progress//4
|
samer@0
|
25 , seqmap_ints//3
|
samer@0
|
26 , seqmap_args//4
|
samer@0
|
27 , seqmap_args//5
|
samer@0
|
28 , seqmap_args//6
|
samer@0
|
29 , iterate//3
|
samer@0
|
30 %, apply/4, apply/5
|
samer@0
|
31 , seq//1, seq//2, seq_n//3
|
samer@0
|
32 , smap//2
|
samer@0
|
33 , rep//2, rep_nocopy//2
|
samer@0
|
34 , at//1, wr//1, str//1, fmt//2
|
samer@0
|
35 , brace//1, paren//1, sqbr//1
|
samer@0
|
36 , q//1, qq//1
|
samer@0
|
37 , escape//2, escape_with//3
|
samer@0
|
38 , null//0, cr//0, sp//0, fs//0
|
samer@0
|
39 , fssp/2, tb/2, comma/2, commasp/2
|
samer@0
|
40 , padint/5
|
samer@0
|
41
|
samer@0
|
42 , do_then_call/5
|
samer@0
|
43 , do_then_call/6
|
samer@0
|
44 , do_then_call/7
|
samer@0
|
45
|
samer@0
|
46 , any/3, notany/3, arb/2, arbno/3, bal/2
|
samer@0
|
47 , span/3, break/3, len/3
|
samer@0
|
48 , exhaust/3
|
samer@0
|
49 , set/3, get/3, set_with/3
|
samer@0
|
50 , with/4, iso/3
|
samer@0
|
51 , once/3
|
samer@0
|
52 , repeat/2
|
samer@0
|
53 , (//)//2
|
samer@0
|
54 , until//2
|
samer@0
|
55
|
samer@0
|
56 , findall//3
|
samer@0
|
57 , setof//3
|
samer@0
|
58
|
samer@0
|
59 , op(900,fy,\<)
|
samer@0
|
60 , op(900,fy,\>)
|
samer@0
|
61 , op(900,xfy,\#)
|
samer@0
|
62
|
samer@0
|
63 , lift//1
|
samer@0
|
64 , lift//2
|
samer@0
|
65 , lift//3
|
samer@0
|
66
|
samer@0
|
67 , stats/0
|
samer@0
|
68 , stats/1
|
samer@0
|
69
|
samer@0
|
70 , select_def_option//2 % like select_option/4 but for DCGs
|
samer@0
|
71 ]).
|
samer@0
|
72
|
samer@0
|
73 /** <module> DCG utilities
|
samer@0
|
74
|
samer@0
|
75 This module contains predicates for working with definite clause
|
samer@0
|
76 grammars and the related stateful programming style where state
|
samer@0
|
77 arguments are automatically threaded through sequences
|
samer@0
|
78 of calls. Some useful DCG procedures are also included.
|
samer@0
|
79
|
samer@0
|
80 When a predicate is declared with type =|foo(...)// is Det|=,
|
samer@0
|
81 any requirements on the type of the DCG state are hidden, i.e. the
|
samer@0
|
82 types of the two extra arguments are hidden. In these cases,
|
samer@0
|
83 the documentation below will sometimes state that the predicate
|
samer@0
|
84 'runs in the =|S|= DCG'.
|
samer@0
|
85
|
samer@0
|
86 ---+++ Types used in this module
|
samer@0
|
87
|
samer@0
|
88 We use the following to denote types of terms that can
|
samer@0
|
89 be interpreted as DCG phrases with or without further
|
samer@0
|
90 arguments.
|
samer@0
|
91 * phrase(S)
|
samer@0
|
92 If P is a term of type =|phrase(S)|=, then P is a valid DCG phrase
|
samer@0
|
93 when the DCG state is of type =|S|=, i.e. =|phrase(P,S1,S2)|= is
|
samer@0
|
94 valid Prolog goal when S1 and S2 are of type =|S|=. N.B. the type
|
samer@0
|
95 =|phrase(S)|= is almost but not quite equivalent to the binary
|
samer@0
|
96 predicate type =|pred(S,S)|=. All such predicates are valid phrases,
|
samer@0
|
97 but phrases involving braces (e.g. {Goal}), commas, semicolons,
|
samer@0
|
98 and if-then constructs (->) are not equivalent to predicates
|
samer@0
|
99 with two extra arguments.
|
samer@0
|
100 * phrase(A,S)
|
samer@0
|
101 If P is of type =|phrase(A,S)|= and X has type A, then =|call(P,X)|=
|
samer@0
|
102 is a valid DCG phrase when the DCG is of type S. This type _|is|_
|
samer@0
|
103 equivalent to =|pred(A,S,S)|= because the only way to call it
|
samer@0
|
104 is with call//1 inside a DCG or call/3 outside it.
|
samer@0
|
105 * phrase(A,B,S)
|
samer@0
|
106 If P is of type =|phrase(A,B)|= and =|X|= and =|Y|= are of types
|
samer@0
|
107 =|A|= and =|B|= respectively, then =|call(P,X,Y)|=
|
samer@0
|
108 is a valid DCG phrase. And so on. You get the idea.
|
samer@0
|
109
|
samer@0
|
110 The type =|pair(A,B)|= will be used to denote the type of terms
|
samer@0
|
111 with functor (,)/2 and arguments of types =|A|= and =|B|= respectively:
|
samer@0
|
112 ==
|
samer@0
|
113 pair(A,B) ---> (A,B).
|
samer@0
|
114 ==
|
samer@0
|
115 This type is used to support a set of general purpose predicates
|
samer@0
|
116 for combining commands in two distinct DCGs into a single DCG
|
samer@0
|
117 over a product space of states.
|
samer@0
|
118 */
|
samer@0
|
119
|
samer@0
|
120 :- module_transparent seq/3, seq/4, smap/4.
|
samer@0
|
121
|
samer@0
|
122 :- meta_predicate
|
samer@0
|
123 writedcg(2)
|
samer@0
|
124 , if(0,//,//,?,?)
|
samer@0
|
125 , if(0,//,?,?)
|
samer@0
|
126 , maybe(//,?,?)
|
samer@0
|
127 , opt(//,?,?)
|
samer@0
|
128 , once(//,?,?)
|
samer@0
|
129 , repeat(?,?)
|
samer@0
|
130 , >>(//,//,?,?)
|
samer@0
|
131 , //(//,?,?,?)
|
samer@0
|
132 , \<(//,?,?)
|
samer@0
|
133 , \>(//,?,?)
|
samer@0
|
134 , \#(?,//,?,?)
|
samer@0
|
135 , brace(//,?,?)
|
samer@0
|
136 , paren(//,?,?)
|
samer@0
|
137 , sqbr(//,?,?)
|
samer@0
|
138 , qq(//,?,?)
|
samer@0
|
139 , q(//,?,?)
|
samer@0
|
140 , arbno(//,?,?)
|
samer@0
|
141 , rep(?,//,?,?)
|
samer@0
|
142 , rep_nocopy(+,//,?,?)
|
samer@0
|
143 , exhaust(//,?,?)
|
samer@0
|
144 , with(?,//,?,?)
|
samer@0
|
145 , iso(//,?,?)
|
samer@0
|
146 , set_with(1,?,?)
|
samer@0
|
147 , run_left(//,?,?,?,?)
|
samer@0
|
148 , run_right(//,?,?,?,?)
|
samer@0
|
149 , iterate(4,?,?,?,?)
|
samer@0
|
150 , parmap(3,?,?,?)
|
samer@0
|
151 , parmap(4,?,?,?,?)
|
samer@0
|
152 , parmap(5,?,?,?,?,?)
|
samer@0
|
153 , parmap(6,?,?,?,?,?,?)
|
samer@0
|
154 , parmap(7,?,?,?,?,?,?,?)
|
samer@0
|
155 , seqmap(3,?,?,?)
|
samer@0
|
156 , seqmap(4,?,?,?,?)
|
samer@0
|
157 , seqmap(5,?,?,?,?,?)
|
samer@0
|
158 , seqmap(6,?,?,?,?,?,?)
|
samer@0
|
159 , seqmap(7,?,?,?,?,?,?,?)
|
samer@0
|
160 , seqmap_n(+,3,?,?,?)
|
samer@0
|
161 , seqmap_n(+,4,?,?,?,?)
|
samer@0
|
162 , seqmap_n(+,5,?,?,?,?,?)
|
samer@0
|
163 , seqmap_ints(3,+,+,?,?)
|
samer@0
|
164 , seqmap_with_sep(//,3,?,?,?)
|
samer@0
|
165 , seqmap_with_sep(//,4,?,?,?,?)
|
samer@0
|
166 , seqmap_with_sep(//,5,?,?,?,?,?)
|
samer@0
|
167 , seqmap_args(3,+,+,?,?,?)
|
samer@0
|
168 , seqmap_args(4,+,+,?,?,?,?)
|
samer@0
|
169 , seqmap_args(5,+,+,?,?,?,?,?)
|
samer@0
|
170 , do_then_call(//,3,?,?,?)
|
samer@0
|
171 , do_then_call(//,4,?,?,?,?)
|
samer@0
|
172 , do_then_call(//,5,?,?,?,?,?)
|
samer@0
|
173 , until(0,//,?,?)
|
samer@0
|
174 .
|
samer@0
|
175
|
samer@0
|
176 :- op(900,fy,\<).
|
samer@0
|
177 :- op(900,fy,\>).
|
samer@0
|
178 :- op(900,xfy,\#).
|
samer@0
|
179 :- op(550,xfx,..).
|
samer@0
|
180
|
samer@0
|
181
|
samer@0
|
182 %%%
|
samer@0
|
183 %%% The first lot of stuff is completely general for any stateful system.
|
samer@0
|
184 %%%
|
samer@0
|
185
|
samer@0
|
186
|
samer@0
|
187 %% trans( ?Old:S, ?New:S, ?S1:int, ?S2:S) is det.
|
samer@0
|
188 %
|
samer@0
|
189 % Unifies Old and New with the states S1 and S2 respectively.
|
samer@0
|
190 trans(X,Y,X,Y).
|
samer@0
|
191
|
samer@0
|
192 % these will be useful for seq (they define a sort of generalised
|
samer@0
|
193 % lazy mapping over sequences of DCG terms)
|
samer@0
|
194 empty([]).
|
samer@0
|
195 empty(_:[]).
|
samer@0
|
196 empty(map(_,L)) :- empty(L).
|
samer@0
|
197 empty(_:map(_,L)) :- empty(L).
|
samer@0
|
198 empty(M..N) :- N<M.
|
samer@0
|
199
|
samer@0
|
200 singleton([H|T],H) :- empty(T).
|
samer@0
|
201 singleton(M:[H|T],M:H) :- empty(T).
|
samer@0
|
202 singleton(map(F,L),call(F,H)) :- singleton(L,H).
|
samer@0
|
203 singleton(M:map(F,L),call(M:F,H)) :- singleton(L,H).
|
samer@0
|
204 singleton(M..M,M).
|
samer@0
|
205
|
samer@0
|
206 properlist([H|T],H,T) :- \+empty(T).
|
samer@0
|
207 properlist(M:[H|T],M:H,M:T) :- \+empty(T).
|
samer@0
|
208 properlist(map(F,L),call(F,H),map(F,T)) :- properlist(L,H,T).
|
samer@0
|
209 properlist(M:map(F,L),call(M:F,H),M:map(F,T)) :- properlist(L,H,T).
|
samer@0
|
210 properlist(M..N,M,M1..N) :- N>M, succ(M,M1).
|
samer@0
|
211
|
samer@0
|
212 %% nop// is det.
|
samer@0
|
213 %
|
samer@0
|
214 % Do nothing. (More neutral than []).
|
samer@0
|
215 nop(X,X).
|
samer@0
|
216
|
samer@0
|
217 %% set(S:A, S1:_, S2:A) is det.
|
samer@0
|
218 % Set state to S. Implemented by goal expansion.
|
samer@0
|
219 set(S,_,S).
|
samer@0
|
220
|
samer@0
|
221 %% get(S:A, S1:A, S2:A) is det.
|
samer@0
|
222 % Get state to S. Implemented by goal expansion.
|
samer@0
|
223 get(S,S,S).
|
samer@0
|
224
|
samer@0
|
225 %% with(S:A, P:phrase(A), S1:B, S2:B) is nondet.
|
samer@0
|
226 %
|
samer@0
|
227 % Run phrase P starting from state S and discarding
|
samer@0
|
228 % the final state, meanwhile preserving the state
|
samer@0
|
229 % of the current system, i.e. guarantees S1=S2.
|
samer@0
|
230 with(S,G) --> {phrase(G,S,_)}.
|
samer@0
|
231
|
samer@0
|
232 %% iso(P:phrase(A), S1:A, S2:A) is nondet.
|
samer@0
|
233 %
|
samer@0
|
234 % Run phrase P starting with current state but discarding
|
samer@0
|
235 % its final state and preserving the current state, so
|
samer@0
|
236 % that S1=S2.
|
samer@0
|
237 iso(G) --> get(S), {phrase(G,S,_)}.
|
samer@0
|
238
|
samer@0
|
239 %% set_with(+G:pred(A), S1:_, S2:A) is det.
|
samer@0
|
240 %
|
samer@0
|
241 % Set current state using a given callable goal G, which should accept one argument.
|
samer@0
|
242 % should be of type pred( -S:A), ie it should set S to the new desired
|
samer@0
|
243 % state, which is installed in the DCG state.
|
samer@0
|
244 set_with(G,_,S) :- call(G,S).
|
samer@0
|
245
|
samer@0
|
246 %% \<(P:phrase(A), ?S1:pair(A,B), ?S2:pair(A,B)) is nondet.
|
samer@0
|
247 %
|
samer@0
|
248 % Apply phrase P to left part of a paired state.
|
samer@0
|
249 % Implemented by goal expansion so incurs only very small
|
samer@0
|
250 % speed penalty.
|
samer@0
|
251 \<(P,(A1,B),(A2,B)) :- phrase(P,A1,A2).
|
samer@0
|
252
|
samer@0
|
253 %% \>(P:phrase(B), ?S1:pair(A,B), ?S2:pair(A,B)) is nondet.
|
samer@0
|
254 %
|
samer@0
|
255 % Apply phrase P which must be of type pred(B,B) to right
|
samer@0
|
256 % part of a paired state.
|
samer@0
|
257 % Implemented by goal expansion so incurs only very small
|
samer@0
|
258 % speed penalty.
|
samer@0
|
259 \>(P,(A,B1),(A,B2)) :- phrase(P,B1,B2).
|
samer@0
|
260
|
samer@0
|
261 %% run_left(P:phrase(pair(A,B)), ?A1:A, ?A2:A, ?B1:B, ?B2:B) is multi.
|
samer@0
|
262 %
|
samer@0
|
263 % Applies DCG phrase P to state formed by pairing A1 and A2 with
|
samer@0
|
264 % current DCG states B1 and B2. Phrase can use (\<) to access the
|
samer@0
|
265 % A state and (\>) to access the underlying B state.
|
samer@0
|
266 run_left(P,S1,S2,T1,T2) :- phrase(P,(S1,T1),(S2,T2)).
|
samer@0
|
267
|
samer@0
|
268 %% run_right(P:phrase(pair(A,B)), ?B1:B, ?B2:B, ?A1:A, ?A2:A) is multi.
|
samer@0
|
269 %
|
samer@0
|
270 % Applies DCG phrase P to state formed by pairing A1 and A2 with
|
samer@0
|
271 % current DCG states B1 and B2. Phrase can use (\<) to access the
|
samer@0
|
272 % A state and (\>) to access the underlying B state.
|
samer@0
|
273 run_right(P,S1,S2,T1,T2) :- phrase(P,(T1,S1),(T2,S2)).
|
samer@0
|
274
|
samer@0
|
275 %% \#(N:natural, P:phrase(A), ?S1, ?S2) is nondet.
|
samer@0
|
276 %
|
samer@0
|
277 % Apply phrase P to the Nth argument of state which must
|
samer@0
|
278 % be a compound term (with arbitrary functor), with the
|
samer@0
|
279 % Nth argument of type A.
|
samer@0
|
280 \#(N, P, S1, S2) :- with_nth_arg(N,P,S1,S2).
|
samer@0
|
281
|
samer@0
|
282
|
samer@0
|
283 system:goal_expansion( run_left(P,S1,S2,T1,T2), phrase(P,(S1,T1),(S2,T2))).
|
samer@0
|
284 system:goal_expansion( run_right(P,S1,S2,T1,T2), phrase(P,(T1,S1),(T2,S2))).
|
samer@0
|
285 system:goal_expansion( \<(P,S1,S2), (S1=(L1,R),S2=(L2,R),phrase(P,L1,L2)) ).
|
samer@0
|
286 system:goal_expansion( \>(P,S1,S2), (S1=(L,R1),S2=(L,R2),phrase(P,R1,R2)) ).
|
samer@0
|
287 system:goal_expansion( nop(S1,S2), (S1=S2) ).
|
samer@0
|
288 system:goal_expansion( out(X,S1,S2), (S1=[X|S2]) ).
|
samer@0
|
289 system:goal_expansion( get(S,S1,S2), (S=S1,S1=S2) ).
|
samer@0
|
290 system:goal_expansion( set(S,_,S2), (S=S2) ).
|
samer@0
|
291 system:goal_expansion( A >> B, (A,B) ).
|
samer@0
|
292 system:goal_expansion( set_with(C,_,S2), Call) :- mk_call(C,[S2],Call).
|
samer@0
|
293 system:goal_expansion( trans(A1,A2,S1,S2), (S1=A1,S2=A2) ).
|
samer@0
|
294 system:goal_expansion( //(P1,P2,S1,S2), (G1,G2)) :-
|
samer@0
|
295 nonvar(P1), P1=..[F1|A1], append(A1,[S1,S2],B1), G1=..[F1|B1],
|
samer@0
|
296 nonvar(P2), P2=..[F2|A2], append(A2,[S1,S2],B2), G2=..[F2|B2].
|
samer@0
|
297
|
samer@0
|
298 mk_call(C,XX,Call) :- var(C), !, mk_call(call(C),XX,Call).
|
samer@0
|
299 mk_call(M:C,XX,M:Call) :- !, mk_call(C,XX,Call).
|
samer@0
|
300 mk_call(C,XX,Call) :- C =.. CL, append(CL,XX,CL2), Call =.. CL2.
|
samer@0
|
301
|
samer@0
|
302
|
samer@0
|
303 %% pushl(S:A,S1:B,S2:pair(A,B)) is det.
|
samer@0
|
304 % Create a paired state by putting S on the left and the
|
samer@0
|
305 % old state on the right.
|
samer@0
|
306 pushl(S,S0,(S,S0)).
|
samer@0
|
307
|
samer@0
|
308 %% pushr(S:A,S1:B,S2:pair(B,A)) is det.
|
samer@0
|
309 % Create a paired state by putting S on the right and the
|
samer@0
|
310 % old state on the left.
|
samer@0
|
311 pushr(S,S0,(S0,S)).
|
samer@0
|
312
|
samer@0
|
313 %% popl(S:A,S1:pair(A,B),S2:B) is det.
|
samer@0
|
314 % Unpair state by removing left state and unifying it with S.
|
samer@0
|
315 popl(S,(S,S0),S0).
|
samer@0
|
316
|
samer@0
|
317 %% popr(S:A,S1:(B,A),S2:B) is det.
|
samer@0
|
318 % Unpair state by removing right state and unifying it with S.
|
samer@0
|
319 popr(S,(S0,S),S0).
|
samer@0
|
320
|
samer@0
|
321 %% >>(G1:phrase(S), G2:phrase(S))// is nondet.
|
samer@0
|
322 % Sequential conjuction of phrases G1 and G2, equivalent to (G1,G2),
|
samer@0
|
323 % but sometimes more convenient in terms of operator priorities.
|
samer@0
|
324 % Implemented by goal expansion.
|
samer@0
|
325 A >> B --> A, B.
|
samer@0
|
326
|
samer@0
|
327 %% once(G:phrase(_))// is semidet.
|
samer@0
|
328 % Call DCG phrase G succeeding at most once.
|
samer@0
|
329 once(G,A,B) :- once(phrase(G,A,B)).
|
samer@0
|
330
|
samer@0
|
331 %% repeat// is nondet.
|
samer@0
|
332 % Create an infinite number of choice points.
|
samer@0
|
333 repeat(A,A) :- repeat.
|
samer@0
|
334
|
samer@0
|
335 %% maybe(P:phrase(_))// is det.
|
samer@0
|
336 % Try P, if it fails, then do nothing. If it succeeds,
|
samer@0
|
337 % cut choicepoints and continue.
|
samer@0
|
338 maybe(P) --> P -> nop; nop.
|
samer@0
|
339
|
samer@0
|
340 %% opt(P:phrase(_))// is nondet.
|
samer@0
|
341 % P or nothing. Like maybe but does not cut if P succeeds.
|
samer@0
|
342 opt(P) --> P; nop.
|
samer@0
|
343
|
samer@0
|
344 %% if(G:pred,P,Q)// is det.
|
samer@0
|
345 %% if(G:pred,P)// is det.
|
samer@0
|
346 %
|
samer@0
|
347 % If Prolog goal =|call(G)|= succeeds, do P, otherwise, do Q.
|
samer@0
|
348 % if(G,P) is equivalent to if(G,P,nop), i.e. does nothing
|
samer@0
|
349 % if P fails.
|
samer@0
|
350 if(A,B,C) --> {call(A)} -> B; C. % used to have nonvar(A)
|
samer@0
|
351 if(A,B) --> {call(A)} -> B; nop.
|
samer@0
|
352
|
samer@0
|
353
|
samer@0
|
354 %% exhaust( P:phrase(_))// is det.
|
samer@0
|
355 %
|
samer@0
|
356 % Run phrase sequentially as many times as possible until it fails.
|
samer@0
|
357 % Any choice points left by G are cut.
|
samer@0
|
358 exhaust(G) --> G -> exhaust(G); nop.
|
samer@0
|
359
|
samer@0
|
360
|
samer@0
|
361 %% until( +Q:pred, +P:phrase(_))// is det.
|
samer@0
|
362 %
|
samer@0
|
363 % Repeatedly call phrase P and test ordinary Prolog goal
|
samer@0
|
364 % Q until Q fails. P and Q are copied together before each
|
samer@0
|
365 % iteration, so variables can be shared between them, but
|
samer@0
|
366 % are not shared between iterations.
|
samer@0
|
367 until( Pred, Op) -->
|
samer@0
|
368 {copy_term(Pred/Op,Pred1/Op1)},
|
samer@0
|
369 call(Op1),
|
samer@0
|
370 ( {call(Pred1)}
|
samer@0
|
371 -> {Pred/Op=Pred1/Op1}
|
samer@0
|
372 ; until(Pred, Op)
|
samer@0
|
373 ).
|
samer@0
|
374
|
samer@0
|
375 %% iterate( +P:phrase(A,A,S), +X:A, -Y:A)// is nondet.
|
samer@0
|
376 %
|
samer@0
|
377 % Sequentially call P zero or more times, passing in X on
|
samer@0
|
378 % the first call and threading the result through subsequent calls,
|
samer@0
|
379 % (as well as threading the DCG state in the normal way)
|
samer@0
|
380 % ending in Y.
|
samer@0
|
381
|
samer@0
|
382 iterate(_,A,A) --> [].
|
samer@0
|
383 iterate(F,A1,A3) --> call(F,A1,A2), iterate(F,A2,A3).
|
samer@0
|
384
|
samer@0
|
385
|
samer@0
|
386 %% rep( +N:natural, +P:phrase(_))// is nondet.
|
samer@0
|
387 %% rep( -N:natural, +P:phrase(_))// is nondet.
|
samer@0
|
388 %
|
samer@0
|
389 % Equivalent to N sequential copies of phrase P.
|
samer@0
|
390 % Free variables in P are *not* shared between copies.
|
samer@0
|
391 % If N is unbound on entry, rep//2 is _cautious_: it tries
|
samer@0
|
392 % gradually increasing N from 0 on backtracking.
|
samer@0
|
393
|
samer@0
|
394 rep(N,G,S1,S2) :-
|
samer@0
|
395 ( var(N)
|
samer@0
|
396 -> rep_var(N,G,S1,S2)
|
samer@0
|
397 ; rep_nonvar(N,G,S1,S2)
|
samer@0
|
398 ).
|
samer@0
|
399
|
samer@0
|
400 rep_var(0,_,S,S).
|
samer@0
|
401 rep_var(N,G,S1,S3) :-
|
samer@0
|
402 copy_term(G,G1), phrase(G1,S1,S2),
|
samer@0
|
403 rep_var(M,G,S2,S3), succ(M,N).
|
samer@0
|
404
|
samer@0
|
405 rep_nonvar(0,_,S,S) :- !.
|
samer@0
|
406 rep_nonvar(N,G,S1,S3) :-
|
samer@0
|
407 copy_term(G,G1), phrase(G1,S1,S2),
|
samer@0
|
408 succ(M,N), rep_nonvar(M,G,S2,S3).
|
samer@0
|
409
|
samer@0
|
410
|
samer@0
|
411 %% rep_nocopy( +N:natural, +P:phrase(_))// is nondet.
|
samer@0
|
412 %
|
samer@0
|
413 % Like rep//2 but does not copy P before calling, so
|
samer@0
|
414 % any variables in P are shared between all calls.
|
samer@0
|
415 % Also, N cannot be a variable in this implementation.
|
samer@0
|
416 rep_nocopy(0,_) --> !.
|
samer@0
|
417 rep_nocopy(N,P) --> call(P), {succ(M,N)}, rep_nocopy(M,P).
|
samer@0
|
418
|
samer@0
|
419
|
samer@0
|
420 %% seq( +L:plist, +Sep)// is nondet.
|
samer@0
|
421 %% seq( +L:plist)// is nondet.
|
samer@0
|
422 % Sequence list of phrases with separator. L can be a sort of _generalised_
|
samer@0
|
423 % list of phrases, which can be:
|
samer@0
|
424 % ==
|
samer@0
|
425 % plist ---> list(A) % ordinary list
|
samer@0
|
426 % ; map(phrase(B),plist) % map phrase head P over list
|
samer@0
|
427 % .
|
samer@0
|
428 % ==
|
samer@0
|
429 % Sep is inserted strictly betweened elements of L. seq(L) is equivalent
|
samer@0
|
430 % to seq(L,nop).
|
samer@0
|
431
|
samer@0
|
432 seq(L,_) --> {dcgu:empty(L)}.
|
samer@0
|
433 seq(L,_) --> {dcgu:singleton(L,H)}, H.
|
samer@0
|
434 seq(L,S) --> {dcgu:properlist(L,H,T)}, H, S, seq(T,S).
|
samer@0
|
435 seq(L) --> seq(L,nop). % if no separator specified, use nop.
|
samer@0
|
436
|
samer@0
|
437
|
samer@0
|
438 %% seq_n( N:natural, +L:plist, +Sep)// is nondet.
|
samer@0
|
439 % Sequence list of phrases with separator and counting.
|
samer@0
|
440 %
|
samer@0
|
441 % @see seq//2.
|
samer@0
|
442
|
samer@0
|
443 seq_n(0,L,_) --> {dcgu:empty(L)}.
|
samer@0
|
444 seq_n(1,L,_) --> {dcgu:singleton(L,H)}, H.
|
samer@0
|
445 seq_n(N,L,S) --> {dcgu:properlist(L,H,T)}, H, S, seq_n(M,T,S), {succ(M,N)}.
|
samer@0
|
446
|
samer@0
|
447 %% smap(+F,+L:list)// is nondet.
|
samer@0
|
448 % Equivalent to seq(map(F,L),nop).
|
samer@0
|
449 smap(F,L) --> seq(map(F,L),nop).
|
samer@0
|
450
|
samer@0
|
451
|
samer@0
|
452
|
samer@0
|
453 %% seqmap( +P:phrase(A,S), X:list(A))// is nondet.
|
samer@0
|
454 %% seqmap( +P:phrase(A,B,S), X:list(A), Y:list(B))// is nondet.
|
samer@0
|
455 %% seqmap( +P:phrase(A,B,C,S), X:list(A), Y:list(B), Z:list(C))// is nondet.
|
samer@0
|
456 %% seqmap( +P:phrase(A,B,C,D,S), X:list(A), Y:list(B), Z:list(C), W:list(D))// is nondet.
|
samer@0
|
457 %% seqmap( +P:phrase(A,B,C,D,E,S), X:list(A), Y:list(B), Z:list(C), W:list(D), V:list(E))// is nondet.
|
samer@0
|
458 %
|
samer@0
|
459 % seqmap//N is like maplist/N except that P is an incomplete _phrase_
|
samer@0
|
460 % rather an ordinary goal, which is applied to the elements of the supplied
|
samer@0
|
461 % lists _|in order|_, while threading the DCG state correctly through all
|
samer@0
|
462 % the calls.
|
samer@0
|
463 %
|
samer@0
|
464 % seqmap//N is very powerful - it is like =foldl= and =mapaccum= in functional
|
samer@0
|
465 % languages, but with the added flexibility of bidirectional Prolog variables.
|
samer@0
|
466 %
|
samer@0
|
467 % @see maplist/2.
|
samer@0
|
468
|
samer@0
|
469 seqmap(_,[]) --> [].
|
samer@0
|
470 seqmap(P,[A|AX]) --> call(P,A), seqmap(P,AX).
|
samer@0
|
471 seqmap(_,[],[]) --> [].
|
samer@0
|
472 seqmap(P,[A|AX],[B|BX]) --> call(P,A,B), seqmap(P,AX,BX).
|
samer@0
|
473 seqmap(_,[],[],[]) --> [].
|
samer@0
|
474 seqmap(P,[A|AX],[B|BX],[C|CX]) --> call(P,A,B,C), seqmap(P,AX,BX,CX).
|
samer@0
|
475 seqmap(_,[],[],[],[]) --> [].
|
samer@0
|
476 seqmap(P,[A|AX],[B|BX],[C|CX],[D|DX]) --> call(P,A,B,C,D), seqmap(P,AX,BX,CX,DX).
|
samer@0
|
477 seqmap(_,[],[],[],[],[]) --> [].
|
samer@0
|
478 seqmap(P,[A|AX],[B|BX],[C|CX],[D|DX],[E|EX]) --> call(P,A,B,C,D,E), seqmap(P,AX,BX,CX,DX,EX).
|
samer@0
|
479
|
samer@0
|
480 true(_,_).
|
samer@0
|
481 parmap(_,[]) --> true.
|
samer@0
|
482 parmap(P,[A|AX]) --> call(P,A) // parmap(P,AX).
|
samer@0
|
483 parmap(_,[],[]) --> true.
|
samer@0
|
484 parmap(P,[A|AX],[B|BX]) --> call(P,A,B) // parmap(P,AX,BX).
|
samer@0
|
485 parmap(_,[],[],[]) --> true.
|
samer@0
|
486 parmap(P,[A|AX],[B|BX],[C|CX]) --> call(P,A,B,C) // parmap(P,AX,BX,CX).
|
samer@0
|
487 parmap(_,[],[],[],[]) --> true.
|
samer@0
|
488 parmap(P,[A|AX],[B|BX],[C|CX],[D|DX]) --> call(P,A,B,C,D) // parmap(P,AX,BX,CX,DX).
|
samer@0
|
489 parmap(_,[],[],[],[],[]) --> true.
|
samer@0
|
490 parmap(P,[A|AX],[B|BX],[C|CX],[D|DX],[E|EX]) --> call(P,A,B,C,D,E) // parmap(P,AX,BX,CX,DX,EX).
|
samer@0
|
491
|
samer@0
|
492 %% seqmap_n( +N:natural, +P:phrase(A), X:list(A))// is nondet.
|
samer@0
|
493 %% seqmap_n( +N:natural, +P:phrase(A,B), X:list(A), Y:list(B))// is nondet.
|
samer@0
|
494 %% seqmap_n( +N:natural, +P:phrase(A,B,C), X:list(A), Y:list(B), Z:list(C))// is nondet.
|
samer@0
|
495 %
|
samer@0
|
496 % seqmap_n//.. is like seqmap/N except that the lists of arguments are of lenght N.
|
samer@0
|
497
|
samer@0
|
498 seqmap_n(0,_,[]) --> [].
|
samer@0
|
499 seqmap_n(N,P,[A|AX]) --> {succ(M,N)}, call(P,A), seqmap_n(M,P,AX).
|
samer@0
|
500 seqmap_n(0,_,[],[]) --> [].
|
samer@0
|
501 seqmap_n(N,P,[A|AX],[B|BX]) --> {succ(M,N)}, call(P,A,B), seqmap_n(M,P,AX,BX).
|
samer@0
|
502 seqmap_n(0,_,[],[],[]) --> [].
|
samer@0
|
503 seqmap_n(N,P,[A|AX],[B|BX],[C|CX]) --> {succ(M,N)}, call(P,A,B,C), seqmap_n(M,P,AX,BX,CX).
|
samer@0
|
504
|
samer@0
|
505
|
samer@0
|
506 /*
|
samer@0
|
507 * Goal expansions
|
samer@0
|
508 */
|
samer@0
|
509
|
samer@0
|
510 cons(A,B,[A|B]).
|
samer@0
|
511
|
samer@0
|
512 expand_seqmap_with_prefix(Sep0, Callable0, SeqmapArgs, Goal) :-
|
samer@0
|
513 ( Callable0 = M:Callable
|
samer@0
|
514 -> NextGoal = M:NextCall
|
samer@0
|
515 ; Callable = Callable0,
|
samer@0
|
516 NextGoal = NextCall
|
samer@0
|
517 ),
|
samer@0
|
518
|
samer@0
|
519 append(Lists, [St1,St2], SeqmapArgs),
|
samer@0
|
520
|
samer@0
|
521 Callable =.. [Pred|Args],
|
samer@0
|
522 length(Args, Argc),
|
samer@0
|
523 length(Argv, Argc),
|
samer@0
|
524 length(Lists, N),
|
samer@0
|
525 length(Vars, N),
|
samer@0
|
526 MapArity is N + 4,
|
samer@0
|
527 format(atom(AuxName), '__aux_seqmap/~d_~w_~w+~d', [MapArity, Sep0, Pred, Argc]),
|
samer@0
|
528 build_term(AuxName, Lists, Args, St1, St2, Goal),
|
samer@0
|
529
|
samer@0
|
530 AuxArity is N+Argc+2,
|
samer@0
|
531 prolog_load_context(module, Module),
|
samer@0
|
532 ( current_predicate(Module:AuxName/AuxArity)
|
samer@0
|
533 -> true
|
samer@0
|
534 ; rep(N,[[]],BaseLists,[]),
|
samer@0
|
535 length(Anon, Argc),
|
samer@0
|
536 build_term(AuxName, BaseLists, Anon, S0, S0, BaseClause),
|
samer@0
|
537
|
samer@0
|
538 length(Vars,N),
|
samer@0
|
539 maplist(cons, Vars, Tails, NextArgs),
|
samer@0
|
540 ( Sep0=_:Sep -> true; Sep=Sep0 ),
|
samer@0
|
541 ( is_list(Sep) -> append(Sep,S2,S1), NextThing=NextGoal
|
samer@0
|
542 ; build_term(phrase, [Sep0], [], S1, S2, NextSep),
|
samer@0
|
543 NextThing = (NextSep,NextGoal)
|
samer@0
|
544 ),
|
samer@0
|
545 build_term(Pred, Argv, Vars, S2, S3, NextCall1),
|
samer@0
|
546 build_term(AuxName, Tails, Argv, S3, S4, NextIterate),
|
samer@0
|
547 build_term(AuxName, NextArgs, Argv, S1, S4, NextHead),
|
samer@0
|
548
|
samer@0
|
549 ( goal_expansion(NextCall1,NextCall) -> true
|
samer@0
|
550 ; NextCall1=NextCall),
|
samer@0
|
551
|
samer@0
|
552 NextClause = (NextHead :- NextThing, NextIterate),
|
samer@0
|
553
|
samer@0
|
554 ( predicate_property(Module:NextGoal, transparent)
|
samer@0
|
555 -> compile_aux_clauses([ (:- module_transparent(Module:AuxName/AuxArity)),
|
samer@0
|
556 BaseClause,
|
samer@0
|
557 NextClause
|
samer@0
|
558 ])
|
samer@0
|
559 ; compile_aux_clauses([BaseClause, NextClause])
|
samer@0
|
560 )
|
samer@0
|
561 ).
|
samer@0
|
562
|
samer@0
|
563 expand_call_with_prefix(Sep0, Callable0, InArgs, (SepGoal,CallGoal)) :-
|
samer@0
|
564 append(CallArgs, [S1,S3], InArgs),
|
samer@0
|
565
|
samer@0
|
566 ( Sep0=_:Sep -> true; Sep=Sep0 ),
|
samer@0
|
567 ( is_list(Sep) -> append(Sep,S2,SS), SepGoal=(S1=SS)
|
samer@0
|
568 ; build_term(phrase, [Sep0], [], S1, S2, SepGoal)
|
samer@0
|
569 ),
|
samer@0
|
570
|
samer@0
|
571 ( var(Callable0)
|
samer@0
|
572 -> build_term(call,[Callable0], CallArgs, S2, S3, CallGoal1)
|
samer@0
|
573 ; ( Callable0 = M:Callable
|
samer@0
|
574 -> CallGoal1 = M:NextCall
|
samer@0
|
575 ; Callable = Callable0,
|
samer@0
|
576 CallGoal1 = NextCall
|
samer@0
|
577 ),
|
samer@0
|
578 Callable =.. [Pred|Args],
|
samer@0
|
579 build_term(Pred, Args, CallArgs, S2, S3, NextCall)
|
samer@0
|
580 ),
|
samer@0
|
581 ( goal_expansion(CallGoal1,CallGoal) -> true
|
samer@0
|
582 ; CallGoal1=CallGoal
|
samer@0
|
583 ).
|
samer@0
|
584
|
samer@0
|
585 seqmap_with_sep_first_call(P,[A1|AX],AX) --> call(P,A1).
|
samer@0
|
586 seqmap_with_sep_first_call(P,[A1|AX],[B1|BX],AX,BX) --> call(P,A1,B1).
|
samer@0
|
587 seqmap_with_sep_first_call(P,[A1|AX],[B1|BX],[C1|CX],AX,BX,CX) --> call(P,A1,B1,C1).
|
samer@0
|
588
|
samer@0
|
589 expand_seqmap_with_sep(Sep, Pred, SeqmapArgs, (dcgu:FirstCall,dcgu:SeqmapCall)) :-
|
samer@0
|
590 prolog_load_context(module,Context),
|
samer@0
|
591 (Sep=SMod:Sep1 -> true; SMod=Context, Sep1=Sep),
|
samer@0
|
592 (Pred=CMod:Pred1 -> true; CMod=Context, Pred1=Pred),
|
samer@0
|
593 append(Lists, [St1,St3], SeqmapArgs),
|
samer@0
|
594 length(Lists, N),
|
samer@0
|
595 length(Tails, N),
|
samer@0
|
596 build_term(seqmap_with_sep_first_call, [CMod:Pred1|Lists], Tails, St1, St2, FirstCall),
|
samer@0
|
597 build_term(seqmap_with_prefix, [SMod:Sep1,CMod:Pred1], Tails, St2, St3, SeqmapCall).
|
samer@0
|
598
|
samer@0
|
599 build_term(H,L1,L2,S1,S2,Term) :-
|
samer@0
|
600 append(L2,[S1,S2],L23),
|
samer@0
|
601 append(L1,L23,L123),
|
samer@0
|
602 Term =.. [H | L123].
|
samer@0
|
603
|
samer@0
|
604
|
samer@0
|
605 expand_dcgu(Term, Goal) :-
|
samer@0
|
606 functor(Term, seqmap, N), N >= 4,
|
samer@0
|
607 Term =.. [seqmap, Callable | Args],
|
samer@0
|
608 callable(Callable), !,
|
samer@0
|
609 expand_seqmap_with_prefix([],Callable, Args, Goal).
|
samer@0
|
610
|
samer@0
|
611 expand_dcgu(Term, Goal) :-
|
samer@0
|
612 functor(Term, seqmap_with_sep, N), N >= 5,
|
samer@0
|
613 Term =.. [seqmap_with_sep, Sep, Callable | Args],
|
samer@0
|
614 nonvar(Sep), callable(Callable), !,
|
samer@0
|
615 expand_seqmap_with_sep(Sep, Callable, Args, Goal).
|
samer@0
|
616
|
samer@0
|
617 expand_dcgu(Term, Goal) :-
|
samer@0
|
618 functor(Term, seqmap_with_prefix, N), N >= 5,
|
samer@0
|
619 Term =.. [seqmap_with_prefix, Sep, Callable | Args],
|
samer@0
|
620 callable(Callable), nonvar(Sep), !,
|
samer@0
|
621 expand_seqmap_with_prefix(Sep, Callable, Args, Goal).
|
samer@0
|
622
|
samer@0
|
623 expand_dcgu(Term, Goal) :-
|
samer@0
|
624 functor(Term, do_then_call, N), N >= 2,
|
samer@0
|
625 Term =.. [do_then_call, Prefix, Callable | Args],
|
samer@0
|
626 nonvar(Prefix), !,
|
samer@0
|
627 expand_call_with_prefix(Prefix, Callable, Args, Goal).
|
samer@0
|
628
|
samer@0
|
629 system:goal_expansion(GoalIn, GoalOut) :-
|
samer@0
|
630 \+current_prolog_flag(xref, true),
|
samer@0
|
631 expand_dcgu(GoalIn, GoalOut).
|
samer@0
|
632 % prolog_load_context(module,Mod),
|
samer@0
|
633 % writeln(expanded(Mod:GoalIn)).
|
samer@0
|
634
|
samer@0
|
635
|
samer@0
|
636 %% seqmap_with_sep(+S:phrase, +P:phrase(A), X:list(A))// is nondet.
|
samer@0
|
637 %% seqmap_with_sep(+S:phrase, +P:phrase(A,B), X:list(A), Y:list(B))// is nondet.
|
samer@0
|
638 %% seqmap_with_sep(+S:phrase, +P:phrase(A,B,C), X:list(A), Y:list(B), Z:list(C))// is nondet.
|
samer@0
|
639 %
|
samer@0
|
640 % As seqmap//2.. but inserting the separator phrase S between each call to P.
|
samer@0
|
641 % NB: *Fails* for empty lists.
|
samer@0
|
642 %
|
samer@0
|
643 % @see seqmap//2
|
samer@0
|
644 %seqmap_with_sep(S,P,[A|AX]) --> call(P,A), seqmap_with_prefix(S,P,AX).
|
samer@0
|
645 %seqmap_with_sep(S,P,[A|AX],[B|BX]) --> call(P,A,B), seqmap_with_prefix(S,P,AX,BX).
|
samer@0
|
646 %seqmap_with_sep(S,P,[A|AX],[B|BX],[C|CX]) --> call(P,A,B,C), seqmap_with_prefix(S,P,AX,BX,CX).
|
samer@0
|
647 seqmap_with_sep(S,P,[A|AX]) --> call(P,A), seqmap(do_then_call(S,P),AX).
|
samer@0
|
648 seqmap_with_sep(S,P,[A|AX],[B|BX]) --> call(P,A,B), seqmap(do_then_call(S,P),AX,BX).
|
samer@0
|
649 seqmap_with_sep(S,P,[A|AX],[B|BX],[C|CX]) --> call(P,A,B,C), seqmap(do_then_call(S,P),AX,BX,CX).
|
samer@0
|
650
|
samer@0
|
651 %seqmap_with_prefix(_,_,[]) --> [].
|
samer@0
|
652 %seqmap_with_prefix(S,P,[A|AX]) --> S, call(P,A), seqmap_with_prefix(S,P,AX).
|
samer@0
|
653 %seqmap_with_prefix(_,_,[],[]) --> [].
|
samer@0
|
654 %seqmap_with_prefix(S,P,[A|AX],[B|BX]) --> S, call(P,A,B), seqmap_with_prefix(S,P,AX,BX).
|
samer@0
|
655 %seqmap_with_prefix(_,_,[],[],[]) --> [].
|
samer@0
|
656 %seqmap_with_prefix(S,P,[A|AX],[B|BX],[C|CX]) --> S, call(P,A,B,C), seqmap_with_prefix(S,P,AX,BX,CX).
|
samer@0
|
657
|
samer@0
|
658
|
samer@0
|
659 % do_then_call( +S:phrase, +P:phrase(A), X:A)// is nondet.
|
samer@0
|
660 % do_then_call( +S:phrase, +P:phrase(A,B), X:A, Y:B)// is nondet.
|
samer@0
|
661 % do_then_call( +S:phrase, +P:phrase(A,B,C), X:A, Y:B, Z:C)// is nondet.
|
samer@0
|
662 %
|
samer@0
|
663 % Call phrase S, then call phrase P with arguments A, B, C etc.
|
samer@0
|
664 do_then_call(S,P,A) --> S, call(P,A).
|
samer@0
|
665 do_then_call(S,P,A,B) --> S, call(P,A,B).
|
samer@0
|
666 do_then_call(S,P,A,B,C) --> S, call(P,A,B,C).
|
samer@0
|
667
|
samer@0
|
668
|
samer@0
|
669 %% seqmap_ints( +P:phrase(integer), +I:integer, +J:integer)// is nondet.
|
samer@0
|
670 %
|
samer@0
|
671 % Equivalent to seqmap(P) applied to the list of integers from I to J inclusive.
|
samer@0
|
672 %
|
samer@0
|
673 % @see seqmap//2.
|
samer@0
|
674 seqmap_ints(P,L,N) -->
|
samer@0
|
675 ( {L>N} -> []
|
samer@0
|
676 ; {M is L+1}, call(P,L), seqmap_ints(P,M,N)
|
samer@0
|
677 ).
|
samer@0
|
678
|
samer@0
|
679
|
samer@0
|
680 %% seqmap_args( +P:phrase(integer), +I:integer, +J:integer, X:term)// is nondet.
|
samer@0
|
681 %% seqmap_args( +P:phrase(integer), +I:integer, +J:integer, X:term, Y:term)// is nondet.
|
samer@0
|
682 %% seqmap_args( +P:phrase(integer), +I:integer, +J:integer, X:term, Y:term, Z:term)// is nondet.
|
samer@0
|
683 %
|
samer@0
|
684 % Like seqmap//N, but applied to the arguments of term X, Y and Z, from the I th to the
|
samer@0
|
685 % J th inclusive.
|
samer@0
|
686 %
|
samer@0
|
687 % @see seqmap//2.
|
samer@0
|
688
|
samer@0
|
689 seqmap_args(P,L,N,A) -->
|
samer@0
|
690 ( {L>N} -> []
|
samer@0
|
691 ; {succ(L,M), arg(L,A,AA)},
|
samer@0
|
692 call(P,AA), seqmap_args(P,M,N,A)
|
samer@0
|
693 ).
|
samer@0
|
694
|
samer@0
|
695 seqmap_args(P,L,N,A,B) -->
|
samer@0
|
696 ( {L>N} -> []
|
samer@0
|
697 ; {succ(L,M), arg(L,A,AA), arg(L,B,BB)},
|
samer@0
|
698 call(P,AA,BB), seqmap_args(P,M,N,A,B)
|
samer@0
|
699 ).
|
samer@0
|
700
|
samer@0
|
701 seqmap_args(P,L,N,A,B,C) -->
|
samer@0
|
702 ( {L>N} -> []
|
samer@0
|
703 ; {succ(L,M), arg(L,A,AA), arg(L,B,BB), arg(L,C,CC)},
|
samer@0
|
704 call(P,AA,BB,CC), seqmap_args(P,M,N,A,B,C)
|
samer@0
|
705 ).
|
samer@0
|
706
|
samer@0
|
707
|
samer@0
|
708
|
samer@0
|
709 %%% ------------------------------------------------------------------
|
samer@0
|
710 %%% These are for sequence building DCGs.
|
samer@0
|
711 %%% ------------------------------------------------------------------
|
samer@0
|
712
|
samer@0
|
713
|
samer@0
|
714
|
samer@0
|
715 %% out(?X)// is det.
|
samer@0
|
716 %
|
samer@0
|
717 % Equivalent to [X]. prepends X to the difference list represented by
|
samer@0
|
718 % the DCG state variables.
|
samer@0
|
719 out(L,[L|L0],L0).
|
samer@0
|
720
|
samer@0
|
721
|
samer@0
|
722 % SNOBOL4ish rules
|
samer@0
|
723 %
|
samer@0
|
724 % Others:
|
samer@0
|
725 % maxarb
|
samer@0
|
726 % pos rpos
|
samer@0
|
727 % tab rtab
|
samer@0
|
728 % rem
|
samer@0
|
729
|
samer@0
|
730
|
samer@0
|
731 %% any(+L:list(_))// is nondet.
|
samer@0
|
732 % Matches any element of L.
|
samer@0
|
733 any(L) --> [X], {member(X,L)}.
|
samer@0
|
734
|
samer@0
|
735 %% notany(+L:list(_))// is nondet.
|
samer@0
|
736 % Matches anything not in L.
|
samer@0
|
737 notany(L) --> [X], {maplist(dif(X),L)}.
|
samer@0
|
738
|
samer@0
|
739 %% arb// is nondet.
|
samer@0
|
740 % Matches an arbitrary sequence. Proceeds cautiously.
|
samer@0
|
741 arb --> []; [_], arb.
|
samer@0
|
742
|
samer@0
|
743 %% arbno(+P:phrase)// is nondet.
|
samer@0
|
744 % Matches an arbitrary number of P. Proceeds cautiously.
|
samer@0
|
745 % Any variables in P are shared across calls.
|
samer@0
|
746 arbno(P) --> []; P, arbno(P).
|
samer@0
|
747
|
samer@0
|
748 %% bal// is nondet.
|
samer@0
|
749 % Matches any expression with balanced parentheses.
|
samer@0
|
750 bal --> balexp, arbno(balexp).
|
samer@0
|
751 balexp --> "(", bal, ")".
|
samer@0
|
752 balexp --> notany("()").
|
samer@0
|
753
|
samer@0
|
754 %% span(+L:list(_))// is nondet.
|
samer@0
|
755 % Matches the longest possible sequence of symbols from L.
|
samer@0
|
756 span(L,A,[]) :- any(L,A,[]).
|
samer@0
|
757 span(L) --> any(L), span(L).
|
samer@0
|
758 span(L), [N] --> any(L), [N], { maplist( dif( N), L) }.
|
samer@0
|
759
|
samer@0
|
760 %% break(+L:list(_))// is nondet.
|
samer@0
|
761 % Matches the longest possible sequence of symbols not in L.
|
samer@0
|
762 break(L,A,[]) :- notany(L,A,[]).
|
samer@0
|
763 break(L) --> notany(L), break(L).
|
samer@0
|
764 break(L), [N] --> notany(L), [N], { member(N,L) }.
|
samer@0
|
765
|
samer@0
|
766 %% len(N:natural)// is nondet.
|
samer@0
|
767 % Matches any N symbols.
|
samer@0
|
768 len(0) --> [].
|
samer@0
|
769 len(N) --> [_], ({var(N)} -> len(M), {succ(M,N)}; {succ(M,N)}, len(M)).
|
samer@0
|
770
|
samer@0
|
771
|
samer@0
|
772 %% //(+P:phrase(A), ?C:list(A), ?S1:list(A), ?S2:list(A)) is nondet.
|
samer@0
|
773 %% //(+P:phrase(A), +C:phrase(A), ?S1:list(A), ?S2:list(A)) is nondet.
|
samer@0
|
774 %
|
samer@0
|
775 % Sequence capture operator - captures the matching sequence C of any
|
samer@0
|
776 % phrase P, eg.
|
samer@0
|
777 % ==
|
samer@0
|
778 % ?- phrase(paren(arb)//C,"(hello)world",_)
|
samer@0
|
779 % C = "(hello)".
|
samer@0
|
780 % true
|
samer@0
|
781 % ==
|
samer@0
|
782 % If nonvar(C) and C is a phrase, it is called after calling P.
|
samer@0
|
783
|
samer@0
|
784 //(H,C,L,T) :-
|
samer@0
|
785 ( var(C)
|
samer@0
|
786 -> phrase(H,L,T), append(C,T,L)
|
samer@0
|
787 ; phrase(H,L,T), phrase(C,L,T)
|
samer@0
|
788 ).
|
samer@0
|
789
|
samer@0
|
790 %%% ------------------------------------------------------------------
|
samer@0
|
791 %%% These are for character sequences DCGs.
|
samer@0
|
792
|
samer@0
|
793 %% writedcg(+P:phrase) is nondet.
|
samer@0
|
794 %
|
samer@0
|
795 % Run the phrase P, which must be a standard list-of-codes DCG,
|
samer@0
|
796 % and print the output.
|
samer@0
|
797 writedcg(Phrase) :-
|
samer@0
|
798 phrase(Phrase,Codes),
|
samer@0
|
799 format('~s',[Codes]).
|
samer@0
|
800
|
samer@0
|
801 %% null// is det.
|
samer@0
|
802 % Empty string.
|
samer@0
|
803 null --> "".
|
samer@0
|
804
|
samer@0
|
805 %% cr// is det.
|
samer@0
|
806 % Carriage return "\n".
|
samer@0
|
807 cr --> "\n".
|
samer@0
|
808
|
samer@0
|
809 %% sp// is det.
|
samer@0
|
810 % Space " ".
|
samer@0
|
811 sp --> " ".
|
samer@0
|
812
|
samer@0
|
813 %% fs// is det.
|
samer@0
|
814 % Full stop (period) ".".
|
samer@0
|
815 fs --> ".".
|
samer@0
|
816
|
samer@0
|
817 %% fssp// is det.
|
samer@0
|
818 % Full stop (period) followed by space.
|
samer@0
|
819 fssp --> ". ".
|
samer@0
|
820
|
samer@0
|
821 %% tb// is det.
|
samer@0
|
822 % Tab "\t".
|
samer@0
|
823 tb --> "\t".
|
samer@0
|
824
|
samer@0
|
825 %% comma// is det.
|
samer@0
|
826 % Comma ",".
|
samer@0
|
827 comma --> ",".
|
samer@0
|
828
|
samer@0
|
829 %% commasp// is det.
|
samer@0
|
830 % Comma and space ", ".
|
samer@0
|
831 commasp --> ", ".
|
samer@0
|
832
|
samer@0
|
833 %% at(X:atom)// is det.
|
samer@0
|
834 % Generate code list for textual representation of atom X.
|
samer@0
|
835 at(A,C,T) :- atomic(A), with_output_to(codes(C,T),write(A)).
|
samer@0
|
836
|
samer@0
|
837 %% wr(X:term)// is det.
|
samer@0
|
838 % Generate the list of codes for term X, as produced by write/1.
|
samer@0
|
839 wr(X,C,T) :- ground(X), with_output_to(codes(C,T),write(X)).
|
samer@0
|
840
|
samer@0
|
841 %% wq(X:term)// is det.
|
samer@0
|
842 % Generate the list of codes for term X, as produced by writeq/1.
|
samer@0
|
843 wq(X,C,T) :- ground(X), with_output_to(codes(C,T),writeq(X)).
|
samer@0
|
844
|
samer@0
|
845 %% str(X:term)// is det.
|
samer@0
|
846 % Generate the list of codes for string X, as produced by writeq/1.
|
samer@0
|
847 str(X,C,T):- string(X), with_output_to(codes(C,T),write(X)).
|
samer@0
|
848
|
samer@0
|
849 %% fmt(+F:atom,+Args:list)// is det
|
samer@0
|
850 % Generate list of codes using format/3.
|
samer@0
|
851 fmt(F,A,C,T) :- format(codes(C,T),F,A).
|
samer@0
|
852
|
samer@0
|
853 %% brace(P:phrase)// is nondet.
|
samer@0
|
854 % Generate "{" before and "}" after the phrase P.
|
samer@0
|
855 brace(A) --> "{", A, "}".
|
samer@0
|
856
|
samer@0
|
857 %% paren(P:phrase)// is nondet.
|
samer@0
|
858 % Generate "(" before and ")" after the phrase P.
|
samer@0
|
859 paren(A) --> "(", A, ")".
|
samer@0
|
860
|
samer@0
|
861 %% sqbr(P:phrase)// is nondet.
|
samer@0
|
862 % Generate "[" before and "]" after the phrase P.
|
samer@0
|
863 sqbr(A) --> "[", A, "]".
|
samer@0
|
864
|
samer@0
|
865 %% q(P:phrase(list(code)))// is nondet.
|
samer@0
|
866 % Generate list of codes from phrase P, surrounds it with single quotes,
|
samer@0
|
867 % and escapes (by doubling up) any internal quotes so that the
|
samer@0
|
868 % generated string is a valid quoted string. Must be list of codes DCG.
|
samer@0
|
869 q(X,[39|C],T) :- T1=[39|T], escape_with(39,39,X,C,T1). % 39 is '
|
samer@0
|
870
|
samer@0
|
871 %% qq(P:phrase(list(code)))// is nondet.
|
samer@0
|
872 % Generate list of codes from phrase P, surrounds it with double quotes,
|
samer@0
|
873 % and escapes (by doubling up) any double quotes so that the
|
samer@0
|
874 % generated string is a valid double quoted string.
|
samer@0
|
875 qq(X,[34|C],T) :- T1=[34|T], escape_with(34,34,X,C,T1). % 34 is "
|
samer@0
|
876
|
samer@0
|
877 % escape difference list of codes with given escape character
|
samer@0
|
878 escape_codes(_,_,A,A,A).
|
samer@0
|
879 escape_codes(E,Q,[Q|X],[E,Q|Y],T) :-escape_codes(E,Q,X,Y,T).
|
samer@0
|
880 escape_codes(E,Q,[A|X],[A|Y],T) :- Q\=A, escape_codes(E,Q,X,Y,T).
|
samer@0
|
881
|
samer@0
|
882 %% escape_with(E:C, Q:C, P:phrase(list(C)))// is nondet.
|
samer@0
|
883 %
|
samer@0
|
884 % Runs phrase P to generate a list of elements of type C and
|
samer@0
|
885 % then escapes any occurrences of Q by prefixing them with E, e.g.,
|
samer@0
|
886 % =|escape_with(92,39,"some 'text' here")|= escapes the single quotes
|
samer@0
|
887 % with backslashes, yielding =|"some \'text\' here"|=.
|
samer@0
|
888 escape_with(E,Q,Phrase,L1,L2) :-
|
samer@0
|
889 phrase(Phrase,L0,L2),
|
samer@0
|
890 escape_codes(E,Q,L0,L1,L2).
|
samer@0
|
891
|
samer@0
|
892 %% escape(Q:C, P:phrase(list(C)))// is nondet.
|
samer@0
|
893 %
|
samer@0
|
894 % Runs phrase P to generate a list of elements of type C and
|
samer@0
|
895 % then escapes any occurrences of Q by doubling them up, e.g.,
|
samer@0
|
896 % =|escape(39,"some 'text' here")|= doubles up the single quotes
|
samer@0
|
897 % yielding =|"some ''text'' here"|=.
|
samer@0
|
898 escape(Q,A) --> escape_with(Q,Q,A).
|
samer@0
|
899
|
samer@0
|
900 %% padint( +N:integer, +Range, +X:integer)// is nondet.
|
samer@0
|
901 %
|
samer@0
|
902 % Write integer X padded with zeros ("0") to width N.
|
samer@0
|
903 padint(N,L..H,X,C,T) :-
|
samer@0
|
904 between(L,H,X),
|
samer@0
|
905 format(atom(Format),'~~`0t~~d~~~d|',[N]),
|
samer@0
|
906 format(codes(C,T),Format,[X]).
|
samer@0
|
907
|
samer@0
|
908 difflength(A-B,N) :- unify_with_occurs_check(A,B) -> N=0; A=[_|T], difflength(T-B,M), succ(M,N).
|
samer@0
|
909
|
samer@0
|
910 % tail recursive version
|
samer@0
|
911 difflength_x(A-B,M) :- difflength_x(A-B,0,M).
|
samer@0
|
912 difflength_x(A-B,M,M) :- unify_with_occurs_check(A,B).
|
samer@0
|
913 difflength_x([_|T]-A,M,N) :- succ(M,L), difflength_x(T-A,L,N).
|
samer@0
|
914
|
samer@0
|
915
|
samer@0
|
916 %term_codes(T,C) :- with_output_to(codes(C),write(T)).
|
samer@0
|
917
|
samer@0
|
918
|
samer@0
|
919
|
samer@0
|
920
|
samer@0
|
921 % try these?
|
samer@0
|
922 %setof(X,Q,XS,S1,S2) :- setof(X,phrase(Q,S1,S2),XS).
|
samer@0
|
923 %findall(X,Q,XS,S1,S2) :- findall(X,phrase(Q,S1,S2),XS).
|
samer@0
|
924
|
samer@0
|
925 with_nth_arg(K,P,T1,T2) :-
|
samer@0
|
926 functor(T1,F,N),
|
samer@0
|
927 functor(T2,F,N),
|
samer@0
|
928 with_nth_arg(N,K,P,T1,T2).
|
samer@0
|
929
|
samer@0
|
930 with_nth_arg(K,K,P,T1,T2) :-
|
samer@0
|
931 arg(K,T1,C1), phrase(P,C1,C2),
|
samer@0
|
932 arg(K,T2,C2), succ(N,K),
|
samer@0
|
933 copy_args(N,T1,T2).
|
samer@0
|
934
|
samer@0
|
935 with_nth_arg(N,K,P,T1,T2) :-
|
samer@0
|
936 arg(N,T1,C),
|
samer@0
|
937 arg(N,T2,C),
|
samer@0
|
938 succ(M,N),
|
samer@0
|
939 with_nth_arg(M,K,P,T1,T2).
|
samer@0
|
940
|
samer@0
|
941 copy_args(0,_,_) :- !.
|
samer@0
|
942 copy_args(N,T1,T2) :-
|
samer@0
|
943 succ(M,N), arg(N,T1,X), arg(N,T2,X),
|
samer@0
|
944 copy_args(M,T1,T2).
|
samer@0
|
945
|
samer@0
|
946
|
samer@0
|
947 %% setof( Template:X, Phrase:phrase(S), Results:list(X), S1:S, S2:S) is nondet.
|
samer@0
|
948 setof(X,Q,XS,S1,S2) :- setof(X,phrase(Q,S1,S2),XS).
|
samer@0
|
949
|
samer@0
|
950 %% findall( Template:X, Phrase:phrase(S), Results:list(X), S1:S, S2:S) is nondet.
|
samer@0
|
951 findall(X,Q,XS,S1,S2) :- findall(X,phrase(Q,S1,S2),XS).
|
samer@0
|
952
|
samer@0
|
953
|
samer@0
|
954 :- meta_predicate lift(0,?,?), lift(1,?,?), lift(2,?,?).
|
samer@0
|
955
|
samer@0
|
956 lift(P) --> { call(P) }.
|
samer@0
|
957 lift(P,X) --> { call(P,X) }.
|
samer@0
|
958 lift(P,X,Y) --> { call(P,X,Y) }.
|
samer@0
|
959
|
samer@0
|
960
|
samer@0
|
961
|
samer@0
|
962 %% seqmap_with_progress( +Period:natural, +Pred:pred(A,S,S), +X:list(A))// is nondet.
|
samer@0
|
963 %% seqmap_with_progress( +Period:natural, +Pred:pred(A,B,S,S), +X:list(A), ?Y:list(B))// is nondet.
|
samer@0
|
964 %
|
samer@0
|
965 % Just like seqmap//2 and seqmap//3 but prints progress and memory usage statistics while running.
|
samer@0
|
966 % Information is printed every Period iterations. The first input list must be
|
samer@0
|
967 % valid list skeleton with a definite length, so that a percentage progress indicator
|
samer@0
|
968 % can be printed.
|
samer@0
|
969 seqmap_with_progress(E,P,X) --> {progress_init(E,X,Pr0)}, smp(X,P,Pr0).
|
samer@0
|
970 seqmap_with_progress(E,P,X,Y) --> {progress_init(E,X,Pr0)}, smp(X,Y,P,Pr0).
|
samer@0
|
971
|
samer@0
|
972 smp([],_,Pr) --> !, {progress_finish(Pr)}.
|
samer@0
|
973 smp([X|XX],P,Pr1) --> {progress_next(Pr1,Pr2)}, call(P,X), !, smp(XX,P,Pr2).
|
samer@0
|
974
|
samer@0
|
975 smp([],_,_,Pr) --> !, {progress_finish(Pr)}.
|
samer@0
|
976 smp([X|XX],[Y|YY],P,Pr1) --> {progress_next(Pr1,Pr2)}, call(P,X,Y), !, smp(XX,YY,P,Pr2).
|
samer@0
|
977
|
samer@0
|
978
|
samer@0
|
979 progress_init(E,X,pr(T0,T,E,0,0)) :- length(X,T), get_time(T0).
|
samer@0
|
980 progress_finish(Pr) :-
|
samer@0
|
981 progress_next(Pr,_),
|
samer@0
|
982 get_time(T1), Pr=pr(T0,N,_,_,_),
|
samer@0
|
983 format('\nFinished ~w items in ~3g minutes.\n',[N,(T1-T0)/60]).
|
samer@0
|
984
|
samer@0
|
985 progress_next(pr(T0,Total,E,N,E),pr(T0,Total,E,M,1)) :- !,
|
samer@0
|
986 succ(N,M),
|
samer@0
|
987 stats(Codes),
|
samer@0
|
988 get_time(T1),
|
samer@0
|
989 format('~s | done ~0f% in ~3g s \r', [Codes,100*N/Total,T1-T0]),
|
samer@0
|
990 flush_output.
|
samer@0
|
991
|
samer@0
|
992 progress_next(pr(T0,T,E,N,C),pr(T0,T,E,M,D)) :- succ(C,D), succ(N,M).
|
samer@0
|
993
|
samer@0
|
994
|
samer@0
|
995 %% stats is det.
|
samer@0
|
996 %% stats( -Codes:list(code)) is det.
|
samer@0
|
997 %
|
samer@0
|
998 % Print or return memory usage statistics.
|
samer@0
|
999 stats :- !,
|
samer@0
|
1000 stats(Codes),
|
samer@0
|
1001 format('~s\r',[Codes]),
|
samer@0
|
1002 flush_output.
|
samer@0
|
1003
|
samer@0
|
1004 stats(Codes) :- !,
|
samer@0
|
1005 statistics(heapused,Heap),
|
samer@0
|
1006 statistics(localused,Local),
|
samer@0
|
1007 statistics(globalused,Global),
|
samer@0
|
1008 statistics(trailused,Trail),
|
samer@0
|
1009 format(codes(Codes), 'heap: ~t~D ~18| local: ~t~D ~36| global: ~t~D ~57| trail: ~t~D ~77|',
|
samer@0
|
1010 [Heap,Local,Global,Trail]).
|
samer@0
|
1011
|
samer@0
|
1012
|
samer@0
|
1013 %% select_def_option(+Option,+Default,+OptsIn,-OptsOut) is det.
|
samer@0
|
1014 %
|
samer@0
|
1015 % Exactly the same as select_option/4 but with a different argument order:
|
samer@0
|
1016 % =|option(Opt,Def,Opts1,Opts2)|= is equivalent to =|select_option(Opt,Opt1,Opts2,Def)|=.
|
samer@0
|
1017 % Changed argument order allows multiple option selection to be written in
|
samer@0
|
1018 % DCG notation with the options list as the state.
|
samer@0
|
1019
|
samer@0
|
1020 select_def_option(Opt,Def,Opts1,Opts2) :- select_option(Opt,Opts1,Opts2,Def).
|