Created
November 24, 2020 01:48
-
-
Save allison-casey/754abbafc0b9e8089abe5f6763349f1a to your computer and use it in GitHub Desktop.
iterable unpacking let macro
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defmacro let [bindings &rest body] | |
" | |
sets up lexical bindings in its body | |
Bindings are processed sequentially, | |
so you can use the result of an earlier binding in a later one. | |
Basic assignments (e.g. setv, +=) will update the let binding, | |
if they use the name of a let binding. | |
But assignments via `import` are always hoisted to normal Python scope, and | |
likewise, `defclass` will assign the class to the Python scope, | |
even if it shares the name of a let binding. | |
Use `import_module` and `type` (or whatever metaclass) instead, | |
if you must avoid this hoisting. | |
Function arguments can shadow let bindings in their body, | |
as can nested let forms. | |
" | |
(if (odd? (len bindings)) | |
(macro-error bindings "let bindings must be paired")) | |
(setv g!let (gensym 'let) | |
replacements (OrderedDict) | |
unpacked-syms (OrderedDict) | |
keys [] | |
values []) | |
(defn expander [symbol] | |
(.get replacements symbol symbol)) | |
(defn destructuring-expander [symbol] | |
(cond | |
[(not (symbol? symbol)) (macro-error symbol "bind targets must be symbol or destructing assignment")] | |
[(in '. symbol) (macro-error symbol "binding target may not contain a dot")]) | |
(setv replaced (gensym symbol)) | |
(assoc unpacked-syms symbol replaced) | |
replaced) | |
(defn destructuring? [x] | |
(or (instance? HyList x) | |
(and (instance? HyExpression x) | |
(= (first x) ',)))) | |
(for [[k v] (partition bindings)] | |
(cond | |
[(and (symbol? k) (in '. k)) | |
(macro-error k "binding target may not contain a dot")] | |
[(not (or (symbol? k) (destructuring? k))) | |
(macro-error k "bind targets must be symbol or iterable unpacking assignment")]) | |
(if (destructuring? k) | |
(do | |
;; append the setv unpacking form | |
(.append keys (symbolexpand (macroexpand-all k &name) destructuring-expander)) | |
(.append values (symbolexpand (macroexpand-all v &name) expander)) | |
;; add the keys we replaced in the unpacking form into the let | |
;; dict | |
(prewalk (fn [x] | |
(cond | |
[(and (symbol? x) (in '. x)) | |
(macro-error k "bind target may not contain a dot")] | |
[(and (instance? HyExpression x) (-> x first (in #{', 'unpack-iterable}) not)) | |
(macro-error k "cannot destructure non-iterable unpacking expression")] | |
[(and (symbol? x) (in x unpacked-syms)) | |
(do (.append keys `(get ~g!let ~(name x))) | |
(.append values (.get unpacked-syms x x)) | |
(assoc replacements x (last keys)))] | |
[True x])) | |
k)) | |
(do (.append values (symbolexpand (macroexpand-all v &name) expander)) | |
(.append keys `(get ~g!let ~(name k))) | |
(assoc replacements k (last keys))))) | |
`(do | |
(setv ~g!let {} | |
~@(interleave keys values)) | |
~@(symbolexpand (macroexpand-all body &name) | |
expander))) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment