Make assoc
a macro instead of a special form
The new macro evaluates its lvalue only once.
This commit is contained in:
parent
062e24d71f
commit
2bbf886ceb
1
NEWS
1
NEWS
@ -32,6 +32,7 @@ Changes from 0.13.0
|
|||||||
* try form now possible in defmacro/deftag
|
* try form now possible in defmacro/deftag
|
||||||
* Fixed a crash when `with` suppresses an exception. `with` now returns
|
* Fixed a crash when `with` suppresses an exception. `with` now returns
|
||||||
`None` in this case.
|
`None` in this case.
|
||||||
|
* `assoc` now evaluates its arguments only once each
|
||||||
|
|
||||||
[ Misc. Improvements ]
|
[ Misc. Improvements ]
|
||||||
* `read`, `read_str`, and `eval` are exposed and documented as top-level
|
* `read`, `read_str`, and `eval` are exposed and documented as top-level
|
||||||
|
@ -1406,30 +1406,6 @@ class HyASTCompiler(object):
|
|||||||
step=step.expr),
|
step=step.expr),
|
||||||
ctx=ast.Load())
|
ctx=ast.Load())
|
||||||
|
|
||||||
@builds("assoc")
|
|
||||||
@checkargs(min=3, even=False)
|
|
||||||
def compile_assoc_expression(self, expr):
|
|
||||||
expr.pop(0) # assoc
|
|
||||||
# (assoc foo bar baz) => foo[bar] = baz
|
|
||||||
target = self.compile(expr.pop(0))
|
|
||||||
ret = target
|
|
||||||
i = iter(expr)
|
|
||||||
for (key, val) in ((self.compile(x), self.compile(y))
|
|
||||||
for (x, y) in zip(i, i)):
|
|
||||||
|
|
||||||
ret += key + val + ast.Assign(
|
|
||||||
lineno=expr.start_line,
|
|
||||||
col_offset=expr.start_column,
|
|
||||||
targets=[
|
|
||||||
ast.Subscript(
|
|
||||||
lineno=expr.start_line,
|
|
||||||
col_offset=expr.start_column,
|
|
||||||
value=target.force_expr,
|
|
||||||
slice=ast.Index(value=key.force_expr),
|
|
||||||
ctx=ast.Store())],
|
|
||||||
value=val.force_expr)
|
|
||||||
return ret
|
|
||||||
|
|
||||||
@builds("with_decorator")
|
@builds("with_decorator")
|
||||||
@checkargs(min=1)
|
@checkargs(min=1)
|
||||||
def compile_decorate_expression(self, expr):
|
def compile_decorate_expression(self, expr):
|
||||||
|
@ -328,9 +328,9 @@
|
|||||||
(do
|
(do
|
||||||
(defn merge-entry [m e]
|
(defn merge-entry [m e]
|
||||||
(setv k (get e 0) v (get e 1))
|
(setv k (get e 0) v (get e 1))
|
||||||
(if (in k m)
|
(setv (get m k) (if (in k m)
|
||||||
(assoc m k (f (get m k) v))
|
(f (get m k) v)
|
||||||
(assoc m k v))
|
v))
|
||||||
m)
|
m)
|
||||||
(defn merge2 [m1 m2]
|
(defn merge2 [m1 m2]
|
||||||
(reduce merge-entry (.items m2) (or m1 {})))
|
(reduce merge-entry (.items m2) (or m1 {})))
|
||||||
|
@ -20,6 +20,22 @@
|
|||||||
~@(interleave (repeat name) rest))
|
~@(interleave (repeat name) rest))
|
||||||
~name))
|
~name))
|
||||||
|
|
||||||
|
|
||||||
|
(defmacro assoc [coll k1 v1 &rest other-kvs]
|
||||||
|
(if (odd? (len other-kvs))
|
||||||
|
(macro-error (last other-kvs)
|
||||||
|
"`assoc` takes an odd number of arguments"))
|
||||||
|
(setv c (if other-kvs
|
||||||
|
(gensym "c")
|
||||||
|
coll))
|
||||||
|
`(setv ~@(+ (if other-kvs
|
||||||
|
[c coll]
|
||||||
|
[])
|
||||||
|
#* (genexpr [`(get ~c ~k) v]
|
||||||
|
[[k v] (partition (+ (, k1 v1)
|
||||||
|
other-kvs))]))))
|
||||||
|
|
||||||
|
|
||||||
(defmacro with [args &rest body]
|
(defmacro with [args &rest body]
|
||||||
"shorthand for nested with* loops:
|
"shorthand for nested with* loops:
|
||||||
(with [x foo y bar] baz) ->
|
(with [x foo y bar] baz) ->
|
||||||
|
@ -628,12 +628,27 @@
|
|||||||
(assoc vals "two" "three")
|
(assoc vals "two" "three")
|
||||||
(assert (= (get vals "two") "three")))
|
(assert (= (get vals "two") "three")))
|
||||||
|
|
||||||
|
|
||||||
(defn test-multiassoc []
|
(defn test-multiassoc []
|
||||||
"NATIVE: test assoc multiple values"
|
"NATIVE: test assoc multiple values"
|
||||||
(setv vals {"one" "two"})
|
(setv vals {"one" "two"})
|
||||||
(assoc vals "two" "three" "four" "five")
|
(assoc vals "two" "three" "four" "five")
|
||||||
(assert (and (= (get vals "two") "three") (= (get vals "four") "five") (= (get vals "one") "two"))))
|
(assert (and (= (get vals "two") "three") (= (get vals "four") "five") (= (get vals "one") "two"))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn test-assoc-eval-lvalue-once []
|
||||||
|
;; https://github.com/hylang/hy/issues/1068
|
||||||
|
"`assoc` only evaluates its lvalue once"
|
||||||
|
(setv counter [])
|
||||||
|
(setv d {})
|
||||||
|
(defn f []
|
||||||
|
(.append counter 1)
|
||||||
|
d)
|
||||||
|
(assoc (f) "a" 1 "b" 2 "c" 3)
|
||||||
|
(assert (= d {"a" 1 "b" 2 "c" 3}))
|
||||||
|
(assert (= counter [1])))
|
||||||
|
|
||||||
|
|
||||||
(defn test-pass []
|
(defn test-pass []
|
||||||
"NATIVE: Test pass worksish"
|
"NATIVE: Test pass worksish"
|
||||||
(if True (do) (do))
|
(if True (do) (do))
|
||||||
|
Loading…
Reference in New Issue
Block a user