aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Stallman2024-08-02 12:03:45 -0400
committerRichard Stallman2024-08-02 12:03:45 -0400
commit18491f48d973c9cbc453d9f742ec7f73e83df3bb (patch)
tree44959170cca5b7818647952fdfbf52b61a96215b
parent80108438e5e2e95ca75e59212fb1669a723241b5 (diff)
downloademacs-18491f48d973c9cbc453d9f742ec7f73e83df3bb.tar.gz
emacs-18491f48d973c9cbc453d9f742ec7f73e83df3bb.zip
Install cond*
* oond-star.el: New file.
-rw-r--r--lisp/emacs-lisp/cond-star.el707
1 files changed, 707 insertions, 0 deletions
diff --git a/lisp/emacs-lisp/cond-star.el b/lisp/emacs-lisp/cond-star.el
new file mode 100644
index 00000000000..6309b0d1a15
--- /dev/null
+++ b/lisp/emacs-lisp/cond-star.el
@@ -0,0 +1,707 @@
1;;; -*-lexical-binding: t; -*-
2
3;; Copyright (C) 1985-2024 Free Software Foundation, Inc.
4
5;; Maintainer: rms@gnu.org
6;; Package: emacs
7
8;; This file is part of GNU Emacs. It implements `cond*'.
9
10;; cond* is free software: you can redistribute it and/or modify
11;; it under the terms of the GNU General Public License as published by
12;; the Free Software Foundation, either version 3 of the License, or
13;; (at your option) any later version.
14
15;; cond* is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
19
20;; You should have received a copy of the GNU General Public License
21;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
22
23;; Here is the list of functions the generated code is known to call:
24;; car, cdr, car-safe, cdr-safe, nth, nthcdr, null, eq, equal, eql, =,
25;; vectorp, length.
26;; It also uses these control and binding promitives:
27;; and, or, if, progn, let, let*, setq.
28;; For regexp matching only, it can call string-match and match-string.
29
30;;; ??? If a clause starts with a keyword,
31;;; should the element after the kwyword be treated in the usual way
32;;; as a pattern? Curently `cond*-non-exit-clause-substance' explicitly
33;;; prevents that by adding t at the front of its value.
34
35(defmacro cond* (&rest clauses)
36 "Extended form of traditional Lisp `cond' construct.
37A `cond*' construct is a series of clauses, and a clause
38normally has the form (CONDITION BDOY...).
39
40CONDITION can be a Lisp expression, as in `cond'.
41Or it can be `(bind* BINDINGS...)' or `(match* PATTERN DATUM)'.
42
43`(bind* BINDINGS...)' means to bind BINDINGS (as if they were in `let*')
44for the body of the clause. As a condition, it counts as true
45if the first binding's value is non-nil. All the bindings are made
46unconditionally for whatever scope they cover.
47
48`(match* PATTERN DATUM)' means to match DATUM against the pattern PATTERN
49The condition counts as true if PATTERN matches DATUM.
50
51When a clause's condition is true, and it exits the `cond*'
52or is the last clause, the value of the last expression
53in its body becomes the return value of the `cond*' construct.
54
55Mon-exit clause:
56
57If a clause has only one element, or if its first element is
58t, or if it ends with the keyword :non-exit, then
59this clause never exits the `cond*' construct. Instead,
60control falls through to the next clause (if any).
61The bindings made in CONDITION for the BODY of the non-exit clause
62are passed along to the rest of the clauses in this `cond*' construct.
63
64\\[match*\\] for documentation of the patterns for use in `match*'."
65 (cond*-convert clauses))
66
67(defmacro match* (pattern datum)
68 "This specifies matching DATUM against PATTERN.
69It is not really a LIsp function, and it is meaningful
70only in the CONDITION of a `cond*' clause.
71
72`_' matches any value.
73KEYWORD matches that keyword.
74nil matches nil.
75t matches t.
76SYMBOL matches any value and binds SYMBOL to that value.
77 If SYMBOL has been matched and bound earlier in this pattern,
78 it matches here the same value that it matched before.
79REGEXP matches a string if REGEXP matches it.
80 The match must cover the entire string from its first char to its last.
81ATOM (meaning any other kind of non-list not described above)
82 matches anything `equal' to it.
83(rx REGEXP) uses a regexp specified in s-expression form,
84 as in the function `rx', and matches the data that way.
85(rx REGEXP SYM0 SYM1...) uses a regexp specified in s-expression form,
86 and binds the symbols SYM0, SYM1, and so on
87 to (match-string 0 DATUM), (match-string 1 DATUM), and so on.
88 You can use as many SYMs as regexp matching supports.
89
90`OBJECT matches any value `equal' to OBJECT.
91(cons CARPAT CDRPAT)
92 matches a cons cell if CARPAT matches its car and CDRPAT matches its cdr.
93(list ELTPATS...)
94 matches a list if the ELTPATS match its elements.
95 The first ELTPAT should match the list's first element.
96 The second ELTPAT should match the list's second element. And so on.
97(vector ELTPATS...)
98 matches a vector if the ELTPATS match its elements.
99 The first ELTPAT should match the vector's first element.
100 The second ELTPAT should match the vector's second element. And so on.
101(cdr PATTERN) matches PATTERN with strict checking of cdrs.
102 That means that `list' patterns verify that the final cdr is nil.
103 Strict checking is the default.
104(cdr-safe PATTERN) matches PATTERN with lax checking of cdrs.
105 That means that `list' patterns do not examine the final cdr.
106(and CONJUNCTS...) matches each of the CONJUNCTS against the same data.
107 If all of them match, this pattern succeeds.
108 If one CONJUNCT fails, this pattern fails and does not try more CONJUNCTS.
109(or DISJUNCTS...) matches each of te DISJUNCTS against the same data.
110 If one DISJUNCT succeeds, this pattern succeeds
111 and does not try more DISJUNCTs.
112 If all of them fail, this pattern fails.
113(COND*-EXPANDER ...)
114 Here the car is a symbol that has a `cond*-expander' property
115 which defines how to handle it in a pattern. The property value
116 is a function. Trying to match such a pattern calls that
117 function with one argument, the pattern in question (including its car).
118 The function should return an equivalent pattern
119 to be matched inetead.
120(PREDICATE SYMBOL)
121 matches datum if (PREDICATE DATUM) is true,
122 then binds SYMBOL to DATUM.
123(PREDICATE SYMBOL MORE-ARGS...)
124 matches datum if (PREDICATE DATUM MORE-ARGS...) is true,
125 then binds SYMBOL to DATUM.
126 MORE-ARGS... can refer to symbols bound earlier in the pattern.
127(constrain SYMBOL EXP)
128 matches datum if the form EXP is true.
129 EXP can refer to symbols bound earlier in the pattern."
130 (ignore datum)
131 (byte-compile-warn-x pattern "`match*' used other than as a `cond*' condition"))
132
133(defun cond*-non-exit-clause-p (clause)
134 "If CLAUSE, a cond* clause, is a non-exit clause, return t."
135 (or (null (cdr-safe clause)) ;; clause has only one element.
136 (and (cdr-safe clause)
137 ;; Starts with t.
138 (or (eq (car clause) t)
139 ;; Begins with keyword.
140 (keywordp (car clause))))
141 ;; Ends with keyword.
142 (keywordp (car (last clause)))))
143
144(defun cond*-non-exit-clause-substance (clause)
145 "For a non-exit cond* clause CLAUSE, return its substance.
146This removes a final keyword if that's what makes CLAUSE non-exit."
147 (cond ((null (cdr-safe clause)) ;; clause has only one element.
148 clause)
149 ;; Starts with t or a keyword.
150 ;; Include t as the first element of the substancea
151 ;; so that the following element is not treated as a pattern.
152 ((and (cdr-safe clause)
153 (or (eq (car clause) t)
154 (keywordp (car clause))))
155 ;; Standardize on t as the first element.
156 (cons t (cdr clause)))
157
158 ;; Ends with keyword.
159 ((keywordp (car (last clause)))
160 ;; Do NOT include the final keyword.
161 (butlast clause))))
162
163(defun cond*-convert (clauses)
164 "Process a list of cond* clauses, CLAUSES.
165Returns the equivalent Lisp expression."
166 (if clauses
167 (cond*-convert-clause (car-safe clauses) (cdr-safe clauses))))
168
169(defun cond*-convert-clause (clause rest)
170 "Process one `cond*' clause, CLAUSE.
171REST is the rest of the clauses of this cond* expression."
172 (if (cond*-non-exit-clause-p clause)
173 ;; Handle a non-exit clause. Make its bindings active
174 ;; around the whole rest of this cond*, treating it as
175 ;; a condition whose value is always t, around the rest
176 ;; of this cond*.
177 (let ((substance (cond*-non-exit-clause-substance clause)))
178 (cond*-convert-condition
179 ;; Handle the first substantial element in the non-exit clause
180 ;; as a matching condition.
181 (car substance)
182 ;; Any following elements in the
183 ;; non-exit clause are just expressions.
184 (cdr substance)
185 ;; Remaining clauses will be UNCONDIT-CLAUSES:
186 ;; run unconditionally and handled as a cond* body.
187 rest
188 nil nil))
189 ;; Handle a normal (conditional exit) clauss.
190 (cond*-convert-condition (car-safe clause) (cdr-safe clause) nil
191 rest (cond*-convert rest))))
192
193(defun cond*-convert-condition (condition true-exps uncondit-clauses rest iffalse)
194 "Process the condition part of one cond* clause.
195TRUE-EXPS is a list of Lisp expressions to be executed if this
196condition is true, and inside its bindings.
197UNCONDIT-CLAUSES is a list of cond*-clauses to be executed if this
198condition is true, and inside its bindings.
199This is used for non-exit clauses; it is nil for conditional-exit clauses.
200
201REST and IFFALSE are non-nil for conditional-exit clauses that are not final.
202REST is a list of clauses to process after this one if
203this one could have exited but does not exit.
204This is used for conditional exit clauses.
205IFFALSE is the value to compute after this one if
206this one could have exited but does not exit.
207This is used for conditional exit clauses."
208 (if (and uncondit-clauses rest)
209 (error "Clause is both exiting and non-exit"))
210 (let ((pat-type (car-safe condition)))
211 (cond ((eq pat-type 'bind*)
212 (let* ((bindings (cdr condition))
213 (first-binding (car bindings))
214 (first-variable (if (symbolp first-binding) first-binding
215 (car first-binding)))
216 (first-value (if (symbolp first-binding) nil
217 (cadr first-binding)))
218 (init-gensym (gensym "init"))
219 ;; BINDINGS with the initial value of the first binding
220 ;; replaced by INIT-GENSYM.
221 (mod-bindings
222 (cons (list first-variable init-gensym) (cdr bindings))))
223 ;;; ??? Here pull out all nontrivial initial values
224 ;;; ??? to compute them earlier.
225 (if rest
226 ;; bind* starts an exiting clause which is not final.
227 ;; Therefore, must run IFFALSE.
228 `(let ((,init-gensym ,first-value))
229 (if ,init-gensym
230 (let* ,mod-bindings
231 . ,true-exps)
232 ;; Always calculate all bindings' initial values,
233 ;; but the bindings must not cover IFFALSE.
234 (let* ,mod-bindings nil)
235 ,iffalse))
236 (if uncondit-clauses
237 ;; bind* starts a non-exit clause which is not final.
238 ;; Run the TRUE-EXPS if condition value is true.
239 ;; Then always go on to run the UNCONDIT-CLAUSES.
240 (if true-exps
241 `(let ((,init-gensym ,first-value))
242;;; ??? Should we make the bindings a second time for the UNCONDIT-CLAUSES.
243;;; as the doc string says, for uniformity with match*?
244 (let* ,mod-bindings
245 (when ,init-gensym
246 . ,true-exps)
247 ,(cond*-convert uncondit-clauses)))
248 `(let* ,bindings
249 ,(cond*-convert uncondit-clauses)))
250 ;; bind* starts a final clause.
251 ;; If there are TRUE-EXPS, run them if condition succeeded.
252 ;; Always make the bindings, in case the
253 ;; initial values have side effects.
254 `(let ((,init-gensym ,first-value))
255 ;; Calculate all binding values unconditionally.
256 (let* ,mod-bindings
257 (when ,init-gensym
258 . ,true-exps)))))))
259 ((eq pat-type 'match*)
260 (cond*-match condition true-exps uncondit-clauses iffalse))
261 (t
262 ;; Ordinary Lixp expression is the condition
263 (if rest
264 ;; A nonfinal exiting clause.
265 ;; If condition succeeds, run the TRUE-EXPS.
266 ;; There are following clauses, so run IFFALSE
267 ;; if the condition fails.
268 `(if ,condition
269 (progn . ,true-exps)
270 ,iffalse)
271 (if uncondit-clauses
272 ;; A non-exit clause.
273 ;; If condition succeeds, run the TRUE-EXPS.
274 ;; Then always go on to run the UNCONDIT-CLAUSES.
275 `(progn (if ,condition
276 (progn . ,true-exps))
277 ,(cond*-convert uncondit-clauses))
278 ;; An exiting clause which is also final.
279 ;; If there are TRUE-EXPS, run them if CONDITION succeeds.
280 (if true-exps
281 `(if ,condition (progn . ,true-exps))
282 ;; Run and return CONDITION.
283 condition)))))))
284
285(defun cond*-match (matchexp true-exps uncondit-clauses iffalse)
286 "Generate code to match a match* pattern PATTERN.
287Match it against data represented by the expression DATA.
288TRUE-EXPS, UNCONDIT-CLAUSES and IFFALSE have the same meanings
289as in `cond*-condition'."
290 (when (or (null matchexp) (null (cdr-safe matchexp))
291 (null (cdr-safe (cdr matchexp)))
292 (cdr-safe (cdr (cdr matchexp))))
293 (byte-compile-warn-x matchexp "Malformed (match* ...) expression"))
294 (let* (raw-result
295 (pattern (nth 1 matchexp))
296 (data (nth 2 matchexp))
297 expression
298 (inner-data data)
299 ;; Add backtrack aliases for or-subpatterns to cdr of this.
300 (backtrack-aliases (list nil))
301 run-true-exps
302 store-value-swap-outs retrieve-value-swap-outs
303 gensym)
304 ;; For now, always bind a gensym to the data to be matched.
305 (setq gensym (gensym "d") inner-data gensym)
306 ;; Process the whole pattern as a subpattern.
307 (setq raw-result (cond*-subpat pattern nil nil nil backtrack-aliases inner-data))
308 (setq expression (cdr raw-result))
309 ;; If there are conditional expressions and some
310 ;; unconditional clauses to follow,
311 ;; and the pattern bound some variables,
312 ;; copy their values into special aliases
313 ;; to be copied back at the start of the unonditional clauses.
314 (when (and uncondit-clauses true-exps
315 (car raw-result))
316 (dolist (bound-var (car raw-result))
317 (push `(setq ,(gensym "ua") ,(car bound-var)) store-value-swap-outs)
318 (push `(,(car bound-var) ,(gensym "ua")) retrieve-value-swap-outs)))
319
320 ;; Make an expression to run the TRUE-EXPS inside our bindings.
321 (if store-value-swap-outs
322 ;; If we have to store those bindings' values in aliases
323 ;; for the UNCONDIT-CLAUSES, ;; do so inside these bindigs.
324 (setq run-true-exps
325 (cond*-bind-pattern-syms
326 (car raw-result)
327 `(prog1 (progn . ,true-exps) . ,store-value-swap-outs)))
328 (setq run-true-exps
329 (cond*-bind-pattern-syms
330 (car raw-result)
331 `(progn . ,true-exps))))
332 ;; Run TRUE-EXPS if match succeeded. Bind our bindings around it.
333 (setq expression
334 (if (and (null run-true-exps) (null iffalse))
335 ;; We MUST compute the expression, even when no decision
336 ;; depends on its value, because it may call functions with
337 ;; side effects.
338 expression
339 `(if ,expression
340 ,run-true-exps
341 ;; For a non-final exiting clause, run IFFALSE if match failed.
342 ;; Don't bind the bindings around it, since
343 ;; an exiting clause's bindings don't affect later clauses.
344 ,iffalse)))
345 ;; For a non-final non-exiting clause,
346 ;; always run the UNCONDIT-CLAUSES.
347 (if uncondit-clauses
348 (setq expression
349 `(progn ,expression
350 (cond*-bind-pattern-syms
351 ,(if retrieve-value-swap-outs
352 ;; If we saved the bindings' values after the
353 ;; true-clauses, bind the same variables
354 ;; here to the values we saved then.
355 retrieve-value-swap-outs
356 ;; Otherwise bind them to the values
357 ;; they matched in the pattern.
358 (car raw-result))
359 (cond*-convert uncondit-clauses)))))
360 ;; Bind the backtrack-aliases if any.
361 ;; We need them bound for the TRUE-EXPS.
362 ;; It is harmless to bind them around IFFALSE
363 ;; because they are all gensyms anyway.
364 (if (cdr backtrack-aliases)
365 (setq expression
366 `(let ,(mapcar 'cdr (cdr backtrack-aliases))
367 ,expression)))
368 (if retrieve-value-swap-outs
369 (setq expression
370 `(let ,(mapcar 'cadr retrieve-value-swap-outs)
371 ,expression)))
372 ;; If we used a gensym, wrap on code to bind it.
373 (if gensym
374 (if (and (listp expression) (eq (car expression) 'progn))
375 `(let ((,gensym ,data)) . ,(cdr expression))
376 `(let ((,gensym ,data)) ,expression))
377 expression)))
378
379(defun cond*-bind-pattern-syms (bindings expr)
380 "Wrap EXPR in code to bind the BINDINGS.
381This is used for the bindings specified explicitly in match* patterns."
382 ;; They can't have side effects. Skip them
383 ;; if we don't actually need them.
384 (if (equal expr '(progn))
385 nil
386 (if bindings
387 (if (eq (car expr) 'progn)
388 `(let* ,bindings . ,(cdr expr))
389 `(let* ,bindings ,expr))
390 expr)))
391
392(defvar cond*-debug-pattern nil)
393
394;;; ??? Structure type patterns not implemented yet.
395;;; ??? Probably should optimize the `nth' calls in handling `list'.
396
397(defun cond*-subpat (subpat cdr-ignore bindings inside-or backtrack-aliases data)
398 "Generate code to match the subpattern within `match*'.
399SUBPAT is the subpattern to handle.
400CDR-IGNORE if true means don't verify there are no extra elts in a list.
401BINDINGS is the list of bindings made by
402the containing and previous subpatterns of this pattern.
403Each element of BINDINGS must have the form (VAR VALUE).
404BACKTRACK-ALIASES is used to pass data upward. Initial call should
405pass (list). The cdr of this collects backtracking aliases made for
406variables bound within (or...) patterns so that the caller
407can bind them etc. Each of them has the form (USER-SYMBOL . GENSYM).
408DATA is the expression for the data that this subpattern is
409supposed to match against.
410
411Return Value has the form (BINDINGS . CONDITION), where
412BINDINGS is the list of bindings to be made for SUBPAT
413plus the subpatterns that contain/precede it.
414Each element of BINDINGS has the form (VAR VALUE).
415CONDITION is the condition to be tested to decide
416whether SUBPAT (as well as the subpatterns that contain/precede it) matches,"
417 (if (equal cond*-debug-pattern subpat)
418 (debug))
419;;; (push subpat subpat-log)
420 (cond ((eq subpat '_)
421 ;; _ as pattern makes no bindings and matches any data.
422 (cons bindings t))
423 ((memq subpat '(nil t))
424 (cons bindings `(eq ,subpat ,data)))
425 ((keywordp subpat)
426 (cons bindings `(eq ,subpat ,data)))
427 ((symbolp subpat)
428 (let ((this-binding (assq subpat bindings))
429 (this-alias (assq subpat (cdr backtrack-aliases))))
430 (if this-binding
431 ;; Variable already bound.
432 ;; Compare what this variable should be bound to
433 ;; to the data it is supposed to match.
434 ;; That is because we don't actually bind these bindings
435 ;; around the condition-testing expression.
436 (cons bindings `(equal ,(cadr this-binding) ,data))
437 (if inside-or
438 (let (alias-gensym)
439 (if this-alias
440 ;; Inside `or' subpattern, if this symbol already
441 ;; has an alias for backtracking, just use that.
442 ;; This means the symbol was matched
443 ;; in a previous arm of the `or'.
444 (setq alias-gensym (cdr this-alias))
445 ;; Inside `or' subpattern but this symbol has no alias,
446 ;; make an alias for it.
447 (setq alias-gensym (gensym "ba"))
448 (push (cons subpat alias-gensym) (cdr backtrack-aliases)))
449 ;; Make a binding for the symbol, to its backtrack-alias,
450 ;; and set the alias (a gensym) to nil.
451 (cons `((,subpat ,alias-gensym) . ,bindings)
452 `(setq ,alias-gensym ,data)))
453 ;; Not inside `or' subpattern: ask for a binding for this symbol
454 ;; and say it does match whatever datum.
455 (cons `((,subpat ,data) . ,bindings)
456 t)))))
457 ;; Various constants.
458 ((numberp subpat)
459 (cons bindings `(eql ,subpat ,data)))
460 ;; Regular expressions as strings.
461 ((stringp subpat)
462 (cons bindings `(string-match ,(concat subpat "\\'") ,data)))
463 ;; All other atoms match with `equal'.
464 ((not (consp subpat))
465 (cons bindings `(equal ,subpat ,data)))
466 ((not (consp (cdr subpat)))
467 (byte-compile-warn-x subpat "%s subpattern with malformed or missing arguments" (car subpat)))
468 ;; Regular expressions specified as list structure.
469 ;; (rx REGEXP VARS...)
470 ((eq (car subpat) 'rx)
471 (let* ((rxpat (concat (rx-to-string (cadr subpat) t) "\\'"))
472 (vars (cddr subpat)) setqs (varnum 0)
473 (match-exp `(string-match ,rxpat ,data)))
474 (if (null vars)
475 (cons bindings match-exp)
476 ;; There are variables to bind to the matched substrings.
477 (if (> (length vars) 10)
478 (byte-compile-warn-x vars "Too many variables specified for matched substrings"))
479 (dolist (elt vars)
480 (unless (symbolp elt)
481 (byte-compile-warn-x vars "Non-symbol %s given as name for matched substring" elt)))
482 ;; Bind these variables to nil, before the pattern.
483 (setq bindings (nconc (mapcar 'list vars) bindings))
484 ;; Make the expressions to set the variables.
485 (setq setqs (mapcar
486 (lambda (var)
487 (prog1 `(setq ,var (match-string ,varnum ,data))
488 (setq varnum (1+ varnum))))
489 vars))
490 (cons bindings `(if ,match-exp
491 (progn ,@setqs t))))))
492 ;; Quoted object as constant to match with `eq' or `equal'.
493 ((eq (car subpat) 'quote)
494 (if (symbolp (car-safe (cdr-safe subpat)))
495 (cons bindings `(eq ,subpat ,data))
496 (cons bindings `(equal ,subpat ,data))))
497 ;; Match a call to `cons' by destructuring.
498 ((eq (car subpat) 'cons)
499 (let (car-result cdr-result car-exp cdr-exp)
500 (setq car-result
501 (cond*-subpat (nth 1 subpat) cdr-ignore bindings inside-or backtrack-aliases `(car ,data)))
502 (setq bindings (car car-result)
503 car-exp (cdr car-result))
504 (setq cdr-result
505 (cond*-subpat (nth 2 subpat) cdr-ignore bindings inside-or backtrack-aliases `(cdr ,data)))
506 (setq bindings (car cdr-result)
507 cdr-exp (cdr cdr-result))
508 (cons bindings
509 (cond*-and `((consp ,data) ,car-exp ,cdr-exp)))))
510 ;; Match a call to `list' by destructuring.
511 ((eq (car subpat) 'list)
512 (let ((i 0) expressions)
513 ;; Check for bad structure of SUBPAT here?
514 (dolist (this-elt (cdr subpat))
515 (let ((result
516 (cond*-subpat this-elt cdr-ignore bindings inside-or backtrack-aliases `(nth ,i ,data))))
517 (setq bindings (car result))
518 (push `(consp ,(if (zerop i) data `(nthcdr ,i ,data)))
519 expressions)
520 (setq i (1+ i))
521 (push (cdr result) expressions)))
522 ;; Verify that list ends here, if we are supposed to check that.
523 (unless cdr-ignore
524 (push `(null (nthcdr ,i ,data)) expressions))
525 (cons bindings (cond*-and (nreverse expressions)))))
526 ;; Match (apply 'vector (backquote-list* LIST...)), destructuring.
527 ((eq (car subpat) 'apply)
528 ;; We only try to handle the case generated by backquote.
529 ;; Convert it to a call to `vector' and handle that.
530 (let ((cleaned-up
531 `(vector . ,(cond*-un-backquote-list* (cdr (nth 2 subpat))))))
532 ;; (cdr (nth 2 subpat)) gets LIST as above.
533 (cond*-subpat cleaned-up
534 cdr-ignore bindings inside-or backtrack-aliases data)))
535 ;; Match a call to `vector' by destructuring.
536 ((eq (car subpat) 'vector)
537 (let* ((elts (cdr subpat))
538 (length (length elts))
539 expressions (i 0))
540 (dolist (elt elts)
541 (let* ((result
542 (cond*-subpat elt cdr-ignore
543 bindings inside-or backtrack-aliases `(aref ,i ,data))))
544 (setq i (1+ i))
545 (setq bindings (car result))
546 (push (cdr result) expressions)))
547 (cons bindings
548 (cond*-and `((vectorp ,data) (= (length ,data) ,length)
549 . ,(nreverse expressions))))))
550 ;; Subpattern to set the cdr-ignore flag
551 ((eq (car subpat) 'cdr-ignore)
552 (cond*-subpat (cadr subpat) t bindings inside-or backtrack-aliases data))
553 ;; Subpattern to clear the cdr-ignore flag
554 ((eq (car subpat) 'cdr)
555 (cond*-subpat (cadr subpat) nil bindings inside-or backtrack-aliases data))
556 ;; Handle conjunction subpatterns.
557 ((eq (car subpat) 'and)
558 (let (expressions)
559 ;; Check for bad structure of SUBPAT here?
560 (dolist (this-elt (cdr subpat))
561 (let ((result
562 (cond*-subpat this-elt cdr-ignore bindings inside-or backtrack-aliases data)))
563 (setq bindings (car result))
564 (push (cdr result) expressions)))
565 (cons bindings (cond*-and (nreverse expressions)))))
566 ;; Handle disjunction subpatterns.
567 ((eq (car subpat) 'or)
568 ;; The main complexity is unsetting the pattern variables
569 ;; that tentatively matche in an or-branch that later failed.
570 (let (expressions
571 (bindings-before-or bindings)
572 (aliases-before-or (cdr backtrack-aliases)))
573 ;; Check for bad structure of SUBPAT here?
574 (dolist (this-elt (cdr subpat))
575 (let* ((bindings bindings-before-or)
576 bindings-to-clear expression
577 result)
578 (setq result
579 (cond*-subpat this-elt cdr-ignore bindings t backtrack-aliases data))
580 (setq bindings (car result))
581 (setq expression (cdr result))
582 ;; Were any bindings made by this arm of the disjunction?
583 (when (not (eq bindings bindings-before-or))
584 ;; Ok, arrange to clear their backtrack aliases
585 ;; if this arm does not match.
586 (setq bindings-to-clear bindings)
587 (let (clearing)
588 ;; For each of those bindings,
589 (while (not (eq bindings-to-clear bindings-before-or))
590 ;; Make an expression to set it to nil, in CLEARING.
591 (let* ((this-variable (caar bindings-to-clear))
592 (this-backtrack (assq this-variable
593 (cdr backtrack-aliases))))
594 (push `(setq ,(cdr this-backtrack) nil) clearing))
595 (setq bindings-to-clear (cdr bindings-to-clear)))
596 ;; Wrap EXPRESSION to clear those backtrack aliases
597 ;; if EXPRESSION is false.
598 (setq expression
599 (if (null clearing)
600 expression
601 (if (null (cdr clearing))
602 `(or ,expression
603 ,(car clearing))
604 `(progn ,@clearing))))))
605 (push expression expressions)))
606 ;; At end of (or...), EACH variable bound by any arm
607 ;; has a backtrack alias gensym. At run time, that gensym's value
608 ;; will be what was bound in the successful arm, or nil.
609 ;; Now make a binding for each variable from its alias gensym.
610 (let ((aliases (cdr backtrack-aliases)))
611 (while (not (eq aliases aliases-before-or))
612 (push `(,(caar aliases) ,(cdar aliases)) bindings)
613 (pop aliases)))
614 (cons bindings `(or . ,(nreverse expressions)))))
615 ;; Expand cond*-macro call, treat result as a subpattern.
616 ((get (car subpat) 'cond*-expander)
617 ;; Treat result as a subpattern.
618 (cond*-subpat (funcall (get (car subpat) 'cond*-expander) subpat)
619 cdr-ignore bindings inside-or backtrack-aliases data))
620 ((macrop (car subpat))
621 (cond*-subpat (macroexpand subpat) cdr-ignore bindings inside-or backtrack-aliases data))
622 ;; Simple constrained variable, as in (symbolp x).
623 ((functionp (car subpat))
624 ;; Without this, nested constrained variables just work.
625 (unless (symbolp (cadr subpat))
626 (byte-compile-warn-x subpat "Complex pattern nested in constrained variable pattern"))
627 (let* ((rest-args (cddr subpat))
628 ;; Process VAR to get a binding for it.
629 (result (cond*-subpat (cadr subpat) cdr-ignore bindings inside-or backtrack-aliases data))
630 (new-bindings (car result))
631 (expression (cdr result))
632 (combined-exp
633 (cond*-and (list `(,(car subpat) ,data . ,rest-args) expression))))
634
635 (cons new-bindings
636 (cond*-bind-around new-bindings combined-exp))))
637 ;; Generalized constrained variable: (constrain VAR EXP)
638 ((eq (car subpat) 'constrain)
639 ;; Without this, nested constrained variables just work.
640 (unless (symbolp (cadr subpat))
641 (byte-compile-warn-x subpat "Complex pattern nested in constrained variable pattern"))
642 ;; Process VAR to get a binding for it.
643 (let ((result (cond*-subpat (cadr subpat) cdr-ignore bindings inside-or backtrack-aliases data)))
644 (cons (car result)
645 ;; This is the test condition.
646 (cond*-bind-around (car result) (nth 2 subpat)))))
647 (t
648 (byte-compile-warn-x subpat "Undefined pattern type `%s' in `cond*'" (car subpat)))))
649
650;;; Subroutines of cond*-subpat.
651
652(defun cond*-bind-around (bindings exp)
653 "Wrap a `let*' around EXP, to bind those of BINDINGS used in EXP."
654 (let ((what-to-bind (cond*-used-within bindings exp)))
655 (if what-to-bind
656 `(let* ,(nreverse what-to-bind) ,exp)
657 exp)))
658
659(defun cond*-used-within (bindings exp)
660 "Return the list of those bindings in BINDINGS which EXP refers to.
661This operates naively and errs on the side of overinclusion,
662and does not distinguish function names from variable names.
663That is safe for the purpose this is used for."
664 (cond ((symbolp exp)
665 (let ((which (assq exp bindings)))
666 (if which (list which))))
667 ((listp exp)
668 (let (combined (rest exp))
669 ;; Find the bindings used in each element of EXP
670 ;; and merge them together in COMBINED.
671 ;; It would be simpler to use dolist at each level,
672 ;; but this avoids errors from improper lists.
673 (while rest
674 (let ((in-this-elt (cond*-used-within bindings (car rest))))
675 (while in-this-elt
676 ;; Don't insert the same binding twice.
677 (unless (memq (car-safe in-this-elt) combined)
678 (push (car-safe in-this-elt) combined))
679 (pop in-this-elt)))
680 (pop rest))
681 combined))))
682
683;; Construct a simplified equivalent to `(and . ,CONJUNCTS),
684;; assuming that it will be used only as a truth value.
685;; We don't bother checking for nil in CONJUNCTS
686;; because that would not normally happen.
687(defun cond*-and (conjuncts)
688 (setq conjuncts (remq t conjuncts))
689 (if (null conjuncts)
690 t
691 (if (null (cdr conjuncts))
692 (car conjuncts)
693 `(and . ,conjuncts))))
694
695;; Convert the arguments in a form that calls `backquote-list*'
696;; into equivalent args to pass to `list'.
697;; We assume the last argument has the form 'LIST.
698;; That means quotify each of that list's elements,
699;; and preserve the other arguments in front of them.
700(defun cond*-un-backquote-list* (args)
701 (if (cdr args)
702 (cons (car args)
703 (cond*-un-backquote-list* (cdr args)))
704 (mapcar (lambda (x) (list 'quote x)) (cadr (car args)))))
705
706
707