Mercurial > core / lisp/std/macs/ana.lisp
changeset 698: |
96958d3eb5b0 |
parent: |
5bd0eb9fa1fa
|
author: |
Richard Westhaver <ellis@rwest.io> |
date: |
Fri, 04 Oct 2024 22:04:59 -0400 |
permissions: |
-rw-r--r-- |
description: |
fixes |
1 ;;; ana.lisp --- anaphoric macros 9 ;; (reexport-from :sb-int :include '(:make-macro-lambda :parse-lambda-list)) 12 ;; (declaim (inline make-tlist tlist-left 13 ;; tlist-right tlist-empty-p)) 15 ;; (defun make-tlist () (cons nil nil)) 16 ;; (defun tlist-left (tl) (caar tl)) 17 ;; (defun tlist-right (tl) (cadr tl)) 18 ;; (defun tlist-empty-p (tl) (null (car tl))) 20 ;; (declaim (inline tlist-add-left 23 ;; (defun tlist-add-left (tl it) 24 ;; (let ((x (cons it (car tl)))) 25 ;; (if (tlist-empty-p tl) 27 ;; (setf (car tl) x))) 29 ;; (defun tlist-add-right (tl it) 30 ;; (let ((x (cons it nil))) 31 ;; (if (tlist-empty-p tl) 33 ;; (setf (cddr tl) x)) 34 ;; (setf (cdr tl) x))) 36 ;; (declaim (inline tlist-rem-left)) 38 ;; (defun tlist-rem-left (tl) 39 ;; (if (tlist-empty-p tl) 40 ;; (error "Remove from empty tlist") 41 ;; (let ((x (car tl))) 42 ;; (setf (car tl) (cdar tl)) 43 ;; (if (tlist-empty-p tl) 44 ;; (setf (cdr tl) nil)) ;; For gc 47 ;; (declaim (inline tlist-update)) 49 ;; (defun tlist-update (tl) 50 ;; (setf (cdr tl) (last (car tl)))) 52 (defun build-batcher-sn (n) 54 (tee (ceiling (log n 2))) 55 (p (ash 1 (- tee 1)))) 56 (loop while (> p 0) do 57 (let ((q (ash 1 (- tee 1))) 60 (loop while (> d 0) do 61 (loop for i from 0 to (- n d 1) do 62 (if (= (logand i p) r) 63 (push (list i (+ i d)) 71 (defmacro! sortf (comparator &rest places) 75 #`(let ((,g!a #1=,(nth (car a1) places)) 76 (,g!b #2=,(nth (cadr a1) places))) 77 (if (,comparator ,g!b ,g!a) 80 (build-batcher-sn (length places)))))) 83 (defun dollar-symbol-p (s) 85 (> (length (symbol-name s)) 1) 86 (string= (symbol-name s) 90 (ignore-errors (parse-integer (subseq (symbol-name s) 1))))) 94 (defmacro! if-match ((match-regex str) then &optional else) 95 (let* ((dollars (remove-duplicates 96 (remove-if-not #'dollar-symbol-p 98 (top (or (car (sort (mapcar #'dollar-symbol-p dollars) #'>)) 100 `(multiple-value-bind (,g!matches ,g!captures) (,match-regex ,str) 101 (declare (ignorable ,g!matches ,g!captures)) 102 (let ((,g!captures-len (length ,g!captures))) 103 (declare (ignorable ,g!captures-len)) 104 (symbol-macrolet ,(mapcar #`(,(symb "$" a1) 105 (if (< ,g!captures-len ,a1) 106 (error "Too few matchs: ~a unbound." ,(mkstr "$" a1)) 107 (aref ,g!captures ,(1- a1)))) 108 (loop for i from 1 to top collect i)) 114 (defmacro when-match ((match-regex str) &body forms) 115 `(if-match (,match-regex ,str) 118 (defmacro once-only (specs &body forms) 119 "Constructs code whose primary goal is to help automate the handling of 120 multiple evaluation within macros. Multiple evaluation is handled by introducing 121 intermediate variables, in order to reuse the result of an expression. 123 The returned value is a list of the form 125 (let ((<gensym-1> <expr-1>) 127 (<gensym-n> <expr-n>)) 130 where GENSYM-1, ..., GENSYM-N are the intermediate variables introduced in order 131 to evaluate EXPR-1, ..., EXPR-N once, only. RES is code that is the result of 132 evaluating the implicit progn FORMS within a special context determined by 133 SPECS. RES should make use of (reference) the intermediate variables. 135 Each element within SPECS is either a symbol SYMBOL or a pair (SYMBOL INITFORM). 136 Bare symbols are equivalent to the pair (SYMBOL SYMBOL). 138 Each pair (SYMBOL INITFORM) specifies a single intermediate variable: 140 - INITFORM is an expression evaluated to produce EXPR-i 142 - SYMBOL is the name of the variable that will be bound around FORMS to the 143 corresponding gensym GENSYM-i, in order for FORMS to generate RES that 144 references the intermediate variable 146 The evaluation of INITFORMs and binding of SYMBOLs resembles LET. INITFORMs of 147 all the pairs are evaluated before binding SYMBOLs and evaluating FORMS. 151 The following expression 158 ;;; (let ((#1=#:X123 (incf y))) 161 could be used within a macro to avoid multiple evaluation like so 174 The following expression demonstrates the usage of the INITFORM field 176 (let ((expr '(incf y))) 177 (once-only ((var `(1+ ,expr))) 178 `(list ',expr ,var ,var))) 181 ;;; (let ((#1=#:VAR123 (1+ (incf y)))) 182 ;;; (list '(incf y) #1# #1)) 184 which could be used like so 186 (defmacro print-succ-twice (expr) 187 (once-only ((var `(1+ ,expr))) 188 `(format t \"Expr: ~s, Once: ~s, Twice: ~s~%\" ',expr ,var ,var))) 191 (print-succ-twice (incf y))) 194 ;;; Expr: (INCF Y), Once: 12, Twice: 12" 195 (let ((gensyms (make-gensym-list (length specs) "ONCE-ONLY")) 196 (names-and-forms (mapcar (lambda (spec) 199 (destructuring-bind (name form) spec 204 ;; bind in user-macro 205 `(let ,(mapcar (lambda (g n) (list g `(gensym ,(string (car n))))) 206 gensyms names-and-forms) 207 ;; bind in final expansion 208 `(let (,,@(mapcar (lambda (g n) 210 gensyms names-and-forms)) 211 ;; bind in user-macro 212 ,(let ,(mapcar (lambda (n g) (list (car n) g)) 213 names-and-forms gensyms) 216 ;;;; DESTRUCTURING-*CASE 218 (defun expand-destructuring-case (key clauses case) 220 `(if (typep ,key 'cons) 222 ,@(mapcar (lambda (clause) 223 (destructuring-bind ((keys . lambda-list) &body body) clause 225 (destructuring-bind ,lambda-list (cdr ,key) 228 (error "Invalid key to DESTRUCTURING-~S: ~S" ',case ,key)))) 230 (defmacro destructuring-case (keyform &body clauses) 231 "DESTRUCTURING-CASE, -CCASE, and -ECASE are a combination of CASE and DESTRUCTURING-BIND. 232 KEYFORM must evaluate to a CONS. 234 Clauses are of the form: 236 ((CASE-KEYS . DESTRUCTURING-LAMBDA-LIST) FORM*) 238 The clause whose CASE-KEYS matches CAR of KEY, as if by CASE, CCASE, or ECASE, 239 is selected, and FORMs are then executed with CDR of KEY is destructured and 240 bound by the DESTRUCTURING-LAMBDA-LIST. 245 (destructuring-case x 247 (format nil \"foo: ~S, ~S\" a b)) 249 (format nil \"bar: ~S, ~S\" a b)) 251 (format nil \"alt: ~S\" a)) 253 (format nil \"unknown: ~S\" rest)))) 255 (dcase (list :foo 1 2)) ; => \"foo: 1, 2\" 256 (dcase (list :bar :a 1 :b 2)) ; => \"bar: 1, 2\" 257 (dcase (list :alt1 1)) ; => \"alt: 1\" 258 (dcase (list :alt2 2)) ; => \"alt: 2\" 259 (dcase (list :quux 1 2 3)) ; => \"unknown: 1, 2, 3\" 262 (destructuring-case x 264 (format nil \"foo: ~S, ~S\" a b)) 266 (format nil \"bar: ~S, ~S\" a b)) 268 (format nil \"alt: ~S\" a)))) 270 (decase (list :foo 1 2)) ; => \"foo: 1, 2\" 271 (decase (list :bar :a 1 :b 2)) ; => \"bar: 1, 2\" 272 (decase (list :alt1 1)) ; => \"alt: 1\" 273 (decase (list :alt2 2)) ; => \"alt: 2\" 274 (decase (list :quux 1 2 3)) ; =| error 276 (expand-destructuring-case keyform clauses 'case)) 278 (defmacro destructuring-ccase (keyform &body clauses) 279 (expand-destructuring-case keyform clauses 'ccase)) 281 (defmacro destructuring-ecase (keyform &body clauses) 282 (expand-destructuring-case keyform clauses 'ecase)) 284 (dolist (name '(destructuring-ccase destructuring-ecase)) 285 (setf (documentation name 'function) (documentation 'destructuring-case 'function))) 287 ;;; *-let --- control-flow let-binding macros 288 ;; based on https://stevelosh.com/blog/2018/07/fun-with-macros-if-let/ 290 (defmacro when-let (bindings &body body) 291 "Bind `bindings` in parallel and execute `body`, short-circuiting on `nil`. 293 This macro combines `when` and `let`. It takes a list of bindings and 294 binds them like `let` before executing `body`, but if any binding's value 295 evaluates to `nil` the process stops and `nil` is immediately returned. 299 (when-let ((a (progn (print :a) 1)) 300 (b (progn (print :b) 2)) 307 (when-let ((a (progn (print :a) nil)) 308 (b (progn (print :b) 2))) 315 (with-gensyms (block) 317 (let ,(loop :for (symbol value) :in bindings 318 :collect `(,symbol (or ,value 319 (return-from ,block nil)))) 322 (defmacro when-let* (bindings &body body) 323 "Bind `bindings` serially and execute `body`, short-circuiting on `nil`. 325 This macro combines `when` and `let*`. It takes a list of bindings 326 and binds them like `let*` before executing `body`, but if any 327 binding's value evaluates to `nil` the process stops and `nil` is 328 immediately returned. 332 (when-let* ((a (progn (print :a) 1)) 333 (b (progn (print :b) (1+ a))) 340 (when-let* ((a (progn (print :a) nil)) 341 (b (progn (print :b) (1+ a)))) 348 (with-gensyms (block) 350 (let* ,(loop :for (symbol value) :in bindings 351 :collect `(,symbol (or ,value 352 (return-from ,block nil)))) 355 (defmacro if-let (bindings &body body) 356 "Bind `bindings` in parallel and execute `then` if all are true, or `else` otherwise. 358 `body` must be of the form `(...optional-declarations... then else)`. 360 This macro combines `if` and `let`. It takes a list of bindings and 361 binds them like `let` before executing the `then` branch of `body`, but 362 if any binding's value evaluates to `nil` the process stops there and the 363 `else` branch is immediately executed (with no bindings in effect). 365 If any `optional-declarations` are included they will only be in effect 366 for the `then` branch. 370 (if-let ((a (progn (print :a) 1)) 371 (b (progn (print :b) 2))) 379 (if-let ((a (progn (print :a) nil)) 380 (b (progn (print :b) 2))) 388 (with-gensyms (outer inner) 389 (multiple-value-bind (body declarations) (parse-body body) 390 (destructuring-bind (then else) body 393 (let ,(loop :for (symbol value) :in bindings 394 :collect `(,symbol (or ,value 395 (return-from ,inner nil)))) 397 (return-from ,outer ,then))) 400 (defmacro if-let* (bindings then else) 401 "Bind `bindings` serially and execute `then` if all are true, or `else` otherwise. 403 This macro combines `if` and `let*`. It takes a list of bindings and 404 binds them like `let*` before executing `then`, but if any binding's 405 value evaluates to `nil` the process stops and the `else` branch is 406 immediately executed (with no bindings in effect). 410 (if-let* ((a (progn (print :a) 1)) 411 (b (progn (print :b) (1+ a))) 419 (if-let* ((a (progn (print :a) nil)) 420 (b (progn (print :b) (1+ a)))) 428 (with-gensyms (outer inner) 431 (let* ,(loop :for (symbol value) :in bindings 432 :collect `(,symbol (or ,value 433 (return-from ,inner nil)))) 434 (return-from ,outer ,then))) 438 (defmacro def! (name &body body) 439 "`defun' without args." 440 `(defun ,name () ,@body)) 442 (defmacro eval-always (&body body) 443 `(eval-when (:compile-toplevel :load-toplevel :execute) ,@body)) 446 (defvar if*-keyword-list '("then" "thenret" "else" "elseif")) 448 (defmacro if* (&rest args) 449 (do ((xx (reverse args) (cdr xx)) 456 (cond ((eq state :compl) 458 (t (error "if*: illegal form ~s" args)))) 459 (cond ((and (symbolp (car xx)) 460 (member (symbol-name (car xx)) 462 :test #'string-equal)) 463 (setq lookat (symbol-name (car xx))))) 465 (cond ((eq state :init) 466 (cond (lookat (cond ((string-equal lookat "thenret") 470 "if*: bad keyword ~a" lookat)))) 473 (push (car xx) col)))) 476 (cond ((string-equal lookat "else") 479 "if*: multiples elses"))) 482 (push `(t ,@col) totalcol)) 483 ((string-equal lookat "then") 485 (t (error "if*: bad keyword ~s" 487 (t (push (car xx) col)))) 491 "if*: keyword ~s at the wrong place " (car xx))) 492 (t (setq state :compl) 493 (push `(,(car xx) ,@col) totalcol)))) 495 (cond ((not (string-equal lookat "elseif")) 496 (error "if*: missing elseif clause "))) 497 (setq state :init))))) 499 (defmacro named-lambda (name lambda-list &body body) 500 "Expands into a lambda-expression within whose BODY NAME denotes the 501 corresponding function." 502 `(labels ((,name ,lambda-list ,@body)) 506 (defmacro until (condition &body body) 507 (let ((block-name (gensym))) 511 (return-from ,block-name nil) 514 (defmacro! dlambda (&rest ds) 515 "Dynamic dispatch lambda." 516 `(lambda (&rest ,g!args) 520 `(,(if (eq t (car d)) 523 (apply (lambda ,@(cdr d)) 530 (defmacro alambda (parms &body body) 531 `(labels ((%a ,parms ,@body)) 535 (defmacro aif (test then &optional else) 537 (if it ,then ,else))) 539 ;; ;; TODO 2023-09-05: wrap, document, optimize, hack 540 ;; re-exported from SB-INT 541 (defmacro awhen (test &body body) 545 (defmacro acond (&rest clauses) 548 (destructuring-bind ((test &body body) &rest rest) clauses 549 (let ((it (copy-symbol 'it))) 552 ;; Just like COND - no body means return the tested value. 554 `(let ((it ,it)) (declare (ignorable it)) ,@body) 558 (defmacro! nlet-tail (n letargs &body body) 559 (let ((gs (loop for i in letargs 568 ',(mapcar #'car letargs) 575 ,g!b (progn ,@body)))))))) 577 (defmacro alet% (letargs &rest body) 578 `(let ((%a) ,@letargs) 579 (setq %a ,@(last body)) 583 (defmacro alet (letargs &rest body) 584 `(let ((%a) ,@letargs) 585 (setq %a ,@(last body)) 587 (lambda (&rest params) 590 ;; swiped from fiveam. This is just like acond except it assumes that 591 ;; the TEST in each element of CLAUSES returns two values as opposed 593 (defmacro acond2 (&rest clauses) 596 (with-gensyms (val foundp) 597 (destructuring-bind ((test &rest progn) &rest others) 599 `(multiple-value-bind (,val ,foundp) 601 (if (or ,val ,foundp) 603 (declare (ignorable it)) 605 (acond2 ,@others)))))))