factor out smacrolet from let

This commit is contained in:
gilch 2017-09-18 23:01:13 -06:00
parent bcc93fb1fe
commit 081a6e2575

View File

@ -93,29 +93,35 @@ Arguments without a header are under None.
(.append (get sections header) arg))) (.append (get sections header) arg)))
sections) sections)
(defn symbolexpand [form expander
&optional
[protected (frozenset)]
[quote-level 0]]
(.expand (SymbolExpander form expander protected quote-level)))
(defclass SymbolExpander[] (defclass SymbolExpander[]
(defn __init__ [self form expander protected quote-level]
(setv self.form form
self.expander expander
self.protected protected
self.quote-level quote-level))
(defn expand-symbols [self form &optional protected quote-level] (defn expand-symbols [self form &optional protected quote-level]
(if (none? protected) (if (none? protected)
(setv protected self.protected)) (setv protected self.protected))
(if (none? quote-level) (if (none? quote-level)
(setv quote-level self.quote-level)) (setv quote-level self.quote-level))
(.expand (SymbolExpander form self.expander protected quote-level))) (symbolexpand form self.expander protected quote-level))
(defn __init__ [self form expander
&optional
[protected (frozenset)]
[quote-level 0]]
(setv self.form form
self.expander expander
self.protected protected
self.quote-level quote-level))
(defn traverse [self form &optional protected quote-level] (defn traverse [self form &optional protected quote-level]
(if (none? protected) (if (none? protected)
(setv protected self.protected)) (setv protected self.protected))
(if (none? quote-level) (if (none? quote-level)
(setv quote-level self.quote-level)) (setv quote-level self.quote-level))
(walk (partial self.expand-symbols (walk (partial symbolexpand
:expander self.expander
:protected protected :protected protected
:quote-level quote-level) :quote-level quote-level)
identity identity
@ -256,6 +262,26 @@ Arguments without a header are under None.
;; recursive base case--it's an atom. Put it back. ;; recursive base case--it's an atom. Put it back.
(self.handle-base)))) (self.handle-base))))
(defmacro smacrolet [bindings &rest body]
"
symbol macro let.
Replaces symbols in body, but only where it would be a valid let binding.
The bindings pairs the target symbol and the expansion form for that symbol.
"
(if (odd? (len bindings))
(macro-error bindings "bindings must be paired"))
(for [k (cut bindings None None 2)]
(if-not (symbol? k)
(macro-error k "bind targets must be symbols")
(if (in '. k)
(macro-error k "binding target may not contain a dot"))))
(setv bindings (dict (partition bindings))
body (macroexpand-all body))
(symbolexpand `(do ~@body)
(fn [symbol]
(.get bindings symbol symbol))))
(defmacro let [bindings &rest body] (defmacro let [bindings &rest body]
" "
sets up lexical bindings in its body sets up lexical bindings in its body
@ -276,26 +302,22 @@ if you must avoid this hoisting.
Function arguments can shadow let bindings in their body, Function arguments can shadow let bindings in their body,
as can nested let forms. as can nested let forms.
" "
;; I'd use defmacro/g!, but it loses the docstring hylang/hy#1424 (setv g!let (gensym 'let)
(setv g!let (gensym 'let)) symbols (cut bindings None None 2))
(if (odd? (len bindings)) (if (odd? (len bindings))
(macro-error bindings "let bindings must be paired")) (macro-error bindings "let bindings must be paired"))
;; pre-expanding the body means we only have to worry about a small number (for [k symbols]
;; of special forms
(setv body (macroexpand-all body)
bound-symbols (cut bindings None None 2))
(for [k bound-symbols]
(if-not (symbol? k) (if-not (symbol? k)
(macro-error k "let can only bind to symbols") (macro-error k "let can only bind to symbols")))
(if (in '. k) (macroexpand
(macro-error k "let binding symbols may not contain a dot")))) `(smacrolet [~@(interleave symbols
(.expand (SymbolExpander `(do (genexpr (HySymbol (+ g!let "::" symbol))
[symbol symbols]))]
(do
(setv ~@bindings) (setv ~@bindings)
~@body) ~@body))))
(fn [symbol]
(if (in symbol bound-symbols) ;; (defmacro macrolet [])
(HySymbol (+ g!let "::" symbol))
symbol)))))
#_[special cases for let #_[special cases for let
;; Symbols containing a dot should be converted to this form. ;; Symbols containing a dot should be converted to this form.