diff --git a/hy/compiler.py b/hy/compiler.py index 0eb067e9a..bcc364324 100755 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -699,28 +699,31 @@ def _render_quoted_form(self, form, level): imports = set([name]) if isinstance(form, (HyList, HyDict, HySet)): - if not form: - contents = HyList() - else: - # If there are arguments, they can be spliced - # so we build a sum... - contents = HyExpression([HySymbol("+"), HyList()]) - + contents = HyList() for x in form: f_imports, f_contents, splice = self._render_quoted_form(x, level) imports.update(f_imports) if splice: - to_add = HyExpression([ - HySymbol("list"), - HyExpression([HySymbol("or"), f_contents, HyList()])]) + # On Python 3.5 and newer, leverage PEP 448 to generate + # expression using tuple unpacking instead of addition. + # + # Should be marginally faster too. + contents.append(HyExpression([ + HySymbol("unpack-iterable" if PY35 else "list"), + HyExpression([HySymbol("or"), f_contents, HyList()]) + ])) else: - to_add = HyList([f_contents]) + contents.append(f_contents if PY35 + else HyList([f_contents])) - contents.append(to_add) + if not PY35: + # Without Python 3.5's unpacking improvements, we need + # to concatenate the expression manually + contents = HyExpression([HySymbol("sum"), contents, HyList()]) - return imports, HyExpression([HySymbol(name), - contents]).replace(form), False + expr = HyExpression([HySymbol(name), contents]) + return imports, expr.replace(form), False elif isinstance(form, HyCons): ret = HyExpression([HySymbol(name)]) diff --git a/hy/models.py b/hy/models.py index 35ff55acb..8e5b45c38 100644 --- a/hy/models.py +++ b/hy/models.py @@ -3,6 +3,7 @@ # license. See the LICENSE. from __future__ import unicode_literals +import collections from contextlib import contextmanager from math import isnan, isinf from hy._compat import PY3, str_type, bytes_type, long_type, string_types @@ -62,9 +63,11 @@ def wrap_value(x): wrapper = _wrappers.get(type(x)) if wrapper is None: + if (not isinstance(x, HyObject) + and isinstance(x, collections.Iterable)): + return wrap_value(list(x)) return x - else: - return wrapper(x) + return wrapper(x) def replace_hy_obj(obj, other): @@ -226,11 +229,8 @@ class HyList(HyObject, list): """ def replace(self, other): - for x in self: - replace_hy_obj(x, other) - - HyObject.replace(self, other) - return self + self[:] = [replace_hy_obj(x, other) for x in self] + return HyObject.replace(self, other) def __add__(self, other): return self.__class__(super(HyList, self).__add__(other)) @@ -387,11 +387,11 @@ def __iter__(self): def replace(self, other): if self.car is not None: - replace_hy_obj(self.car, other) + self.car = replace_hy_obj(self.car, other) if self.cdr is not None: - replace_hy_obj(self.cdr, other) + self.cdr = replace_hy_obj(self.cdr, other) - HyObject.replace(self, other) + return HyObject.replace(self, other) def __repr__(self): if PRETTY: diff --git a/tests/native_tests/quote.hy b/tests/native_tests/quote.hy index 09aafc58a..dc1d5f614 100644 --- a/tests/native_tests/quote.hy +++ b/tests/native_tests/quote.hy @@ -98,3 +98,12 @@ [1 2 3] [4 5 6]) [4 5 6]))) + +(defn test-unquote-with-generators [] + (defn values [] + (yield '1) + (yield '2) + (yield '3)) + + (assert (= (eval `[~@(values)]) [1 2 3])) + (assert (= (eval `[~(values)]) [[1 2 3]])))