|
| 1 | +;;;; LET-VALUES & its friends |
| 2 | +;;; |
| 3 | + |
| 4 | +#+org.tfeb.tools.require-module |
| 5 | +(org.tfeb.tools.require-module:needs |
| 6 | + (:org.tfeb.hax.spam :compile t) |
| 7 | + (:org.tfeb.hax.collecting :compile t) |
| 8 | + (:org.tfeb.hax.iterate :compile t) |
| 9 | + (:org.tfeb.hax.utilities :compile t)) |
| 10 | + |
| 11 | +(defpackage :org.tfeb.hax.let-values |
| 12 | + (:use :cl) |
| 13 | + (:use |
| 14 | + :org.tfeb.hax.spam |
| 15 | + :org.tfeb.hax.collecting |
| 16 | + :org.tfeb.hax.iterate |
| 17 | + :org.tfeb.hax.utilities) |
| 18 | + (:export |
| 19 | + #:let-values |
| 20 | + #:let*-values |
| 21 | + #:let-values* |
| 22 | + #:let*-values*)) |
| 23 | + |
| 24 | +(in-package :org.tfeb.hax.let-values) |
| 25 | + |
| 26 | +(provide :org.tfeb.hax.let-values) |
| 27 | + |
| 28 | +(defun make-vme (name starred) |
| 29 | + (cons name (if starred name (make-symbol (symbol-name name))))) |
| 30 | + |
| 31 | +(defun vme-name (vme) |
| 32 | + (car vme)) |
| 33 | + |
| 34 | +(defun vme-hidden (vme) |
| 35 | + (cdr vme)) |
| 36 | + |
| 37 | +(defun mapped-variable-declarations (declarations varmap environment) |
| 38 | + ;; Appropriate variable declarations from DECLARATIONS mapped |
| 39 | + ;; through VARMAP 'Appropriate' means type declarations and |
| 40 | + ;; dynamic-extent declarations. SPECIAL never matters (the hidden |
| 41 | + ;; variable can be lexical). |
| 42 | + (flet ((mapped-variables (varmap variables) |
| 43 | + (collecting |
| 44 | + (dolist (vme varmap) |
| 45 | + (when (member (vme-name vme) variables) |
| 46 | + (collect (vme-hidden vme))))))) |
| 47 | + (collecting |
| 48 | + (dolist (declaration declarations) |
| 49 | + (dolist (specifier (mapcar (lambda (d) |
| 50 | + (canonicalize-declaration-specifier d environment)) |
| 51 | + (rest declaration))) |
| 52 | + (destructuring-bind (identifier . rest) specifier |
| 53 | + (case identifier |
| 54 | + (type |
| 55 | + (destructuring-bind (type . vars) rest |
| 56 | + (let ((mapped-variables (mapped-variables varmap vars))) |
| 57 | + (unless (null mapped-variables) |
| 58 | + (collect `(declare (type ,type ,@mapped-variables))))))) |
| 59 | + (dynamic-extent |
| 60 | + (let ((mapped-variables (mapped-variables varmap rest))) |
| 61 | + (unless (null mapped-variables) |
| 62 | + (collect `(declare (,identifier ,@mapped-variables))))))))))))) |
| 63 | + |
| 64 | +(defun expand-lv (clauses decls/forms starred environment &aux (unique-variables '())) |
| 65 | + (unless (matchp clauses (list-of (some-of (list-matches (list-of (var)) (any)) |
| 66 | + (list-matches (list-of (var)))))) |
| 67 | + (error "bad clauses ~S" clauses)) |
| 68 | + (if (null clauses) |
| 69 | + `(locally ,@decls/forms) |
| 70 | + (multiple-value-bind (varmaps vfs) |
| 71 | + (with-collectors (varmap vf) |
| 72 | + (dolist (clause clauses) |
| 73 | + (varmap |
| 74 | + (with-collectors (vme) |
| 75 | + (destructuring-bind (vars &optional (form nil)) clause |
| 76 | + (vf form) |
| 77 | + (dolist (var vars) |
| 78 | + (unless starred |
| 79 | + (when (member var unique-variables) |
| 80 | + (error "~S is not unique" var)) |
| 81 | + (push var unique-variables)) |
| 82 | + (vme (make-vme var starred)))))))) |
| 83 | + (assert (= (length varmaps) (length vfs)) () "botched") |
| 84 | + (let ((declarations (nth-value 0 (parse-simple-body decls/forms)))) |
| 85 | + (iterate mvb ((vms varmaps) (forms vfs)) |
| 86 | + (destructuring-bind (vm . more-vms) vms |
| 87 | + (destructuring-bind (form . more-forms) forms |
| 88 | + `(multiple-value-bind ,(mapcar #'vme-hidden vm) ,form |
| 89 | + ,@(mapped-variable-declarations declarations vm environment) |
| 90 | + ,(if (not (null more-vms)) |
| 91 | + (mvb more-vms more-forms) |
| 92 | + `(let ,(mapcan (lambda (varmap) |
| 93 | + (mapcar (lambda (vme) |
| 94 | + `(,(vme-name vme) ,(vme-hidden vme))) |
| 95 | + varmap)) |
| 96 | + varmaps) |
| 97 | + ,@decls/forms)))))))))) |
| 98 | + |
| 99 | +(defmacro let-values ((&rest clauses) &body decls/forms &environment environment) |
| 100 | + "Multiple-value LET form with parallel binding |
| 101 | +
|
| 102 | +LET-VALUES is like LET but each clause binds a list of variables to |
| 103 | +the multiple values of its initform. Bindings for different clauses |
| 104 | +happen in parallel, as for LET, so |
| 105 | +
|
| 106 | + (let ((a 1)) |
| 107 | + (let-values (((a) 2) |
| 108 | + ((b) a)) |
| 109 | + (values a b))) |
| 110 | +
|
| 111 | +evaluates to 2 and 1 |
| 112 | +
|
| 113 | +An initform may be omitted, when it will be NIL. The equivalent of (let (a b |
| 114 | +...) ...) is not allowed. |
| 115 | +
|
| 116 | +Declarations should be handled correctly and perhaps usefully." |
| 117 | + (expand-lv clauses decls/forms nil environment)) |
| 118 | + |
| 119 | +(defmacro let*-values ((&rest clauses) &body decls/forms &environment environment) |
| 120 | + "Multiple-value LET form with sequential binding |
| 121 | +
|
| 122 | +LET-VALUES is like LET but each clause binds a list of variables to |
| 123 | +the multiple values of its initform. Bindings for different clauses |
| 124 | +happen in sequence, as for LET*, so |
| 125 | +
|
| 126 | + (let ((a 1)) |
| 127 | + (let*-values (((a) 2) |
| 128 | + ((b) a)) |
| 129 | + (values a b))) |
| 130 | +
|
| 131 | +evaluates to 2 and 2 |
| 132 | +
|
| 133 | +An initform may be omitted, when it will be NIL. The equivalent of (let (a b |
| 134 | +...) ...) is not allowed. |
| 135 | +
|
| 136 | +Declarations should be handled correctly and perhaps usefully." |
| 137 | + (expand-lv clauses decls/forms t environment)) |
| 138 | + |
| 139 | +(defun expand-lv* (clauses decls/forms starred environment &aux (unique-variables '())) |
| 140 | + (unless (matchp clauses (list-of (list*-matches (list-of (var)) (any)))) |
| 141 | + (error "bad clauses ~S" clauses)) |
| 142 | + (if (null clauses) |
| 143 | + `(locally ,@decls/forms) |
| 144 | + (multiple-value-bind (varmaps vis) |
| 145 | + (with-collectors (varmap vi) |
| 146 | + (dolist (clause clauses) |
| 147 | + (varmap |
| 148 | + (with-collectors (vme) |
| 149 | + (destructuring-bind (vars . forms) clause |
| 150 | + (vi forms) |
| 151 | + (dolist (var vars) |
| 152 | + (unless starred |
| 153 | + (when (member var unique-variables) |
| 154 | + (error "~S is not unique" var)) |
| 155 | + (push var unique-variables)) |
| 156 | + (vme (make-vme var starred)))))))) |
| 157 | + (assert (= (length varmaps) (length vis)) () "botched") |
| 158 | + (let ((declarations (nth-value 0 (parse-simple-body decls/forms)))) |
| 159 | + (iterate mvc ((vms varmaps) (initforms vis)) |
| 160 | + (destructuring-bind (vm . more-vms) vms |
| 161 | + (destructuring-bind (this-initforms . more-initforms) initforms |
| 162 | + `(multiple-value-call |
| 163 | + (lambda ,(mapcar #'vme-hidden vm) |
| 164 | + ,@(mapped-variable-declarations declarations vm environment) |
| 165 | + ,(if (not (null more-vms)) |
| 166 | + (mvc more-vms more-initforms) |
| 167 | + `(let ,(mapcan (lambda (varmap) |
| 168 | + (mapcar (lambda (vme) |
| 169 | + `(,(vme-name vme) ,(vme-hidden vme))) |
| 170 | + varmap)) |
| 171 | + varmaps) |
| 172 | + ,@decls/forms))) |
| 173 | + ,@this-initforms)))))))) |
| 174 | + |
| 175 | +(defmacro let-values* ((&rest clauses) &body decls/forms &environment environment) |
| 176 | + "Multiple-value LET-like form with parallel binding |
| 177 | +
|
| 178 | +Each clause in LET-VALUES* consists of a list of variables to bind and |
| 179 | +any number of initforms, including zero. The variables are bound to |
| 180 | +the combined values of all the initforms: this is the same as |
| 181 | +MULTIPLE-VALUE-CALL, which this uses. Example: |
| 182 | +
|
| 183 | + (let-values* (((a b c) (values 1 2) 3)) |
| 184 | + (values a b c)) |
| 185 | +
|
| 186 | +evaluates to 1, 2 and 3 |
| 187 | +
|
| 188 | +Bindings for different clauses happen in parallel, as for LET, so |
| 189 | +
|
| 190 | + (let ((a 1)) |
| 191 | + (let-values* (((a) 2) |
| 192 | + ((b) a)) |
| 193 | + (values a b))) |
| 194 | +
|
| 195 | +evaluates to 2 and 1. |
| 196 | +
|
| 197 | +There may be no initforms which binds all variables to NIL. The |
| 198 | +equivalent of (let (a b ...) ...) is not supported. |
| 199 | +
|
| 200 | +Declarations should be handled correctly, and perhaps usefully" |
| 201 | + (expand-lv* clauses decls/forms nil environment)) |
| 202 | + |
| 203 | +(defmacro let*-values* ((&rest clauses) &body decls/forms &environment environment) |
| 204 | + "Multiple-value LET-like form with sequential binding |
| 205 | +
|
| 206 | +Each clause in LET-VALUES* consists of a list of variables to bind and |
| 207 | +any number of initforms, including zero. The variables are bound to |
| 208 | +the combined values of all the initforms: this is the same as |
| 209 | +MULTIPLE-VALUE-CALL, which this uses. Example: |
| 210 | +
|
| 211 | + (let-values* (((a b c) (values 1 2) 3)) |
| 212 | + (values a b c)) |
| 213 | +
|
| 214 | +evaluates to 1, 2 and 3 |
| 215 | +
|
| 216 | +Bindings for different clauses happen in sequence, as for LET*, so |
| 217 | +
|
| 218 | + (let ((a 1)) |
| 219 | + (let-values* (((a) 2) |
| 220 | + ((b) a)) |
| 221 | + (values a b))) |
| 222 | +
|
| 223 | +evaluates to 2 and 2. |
| 224 | +
|
| 225 | +There may be no initforms which binds all variables to NIL. The |
| 226 | +equivalent of (let (a b ...) ...) is not supported. |
| 227 | +
|
| 228 | +Declarations should be handled correctly, and perhaps usefully." |
| 229 | + (expand-lv* clauses decls/forms t environment)) |
0 commit comments