annotate prolog/library/dcgu.pl @ 6:172d152c7588

Updated packfile as well.
author samer
date Sat, 12 Apr 2014 12:47:42 +0100
parents b12b733d1dd0
children
rev   line source
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).