From cbc2eed90008261fe5444aab7aa21abf7af2bb60 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Tue, 4 Aug 2015 16:43:07 +0200 Subject: [PATCH 01/53] defclass reimagined defclass now has a new syntax: (defclass Name [BaseList] [property value property value] ;; optional (defn method [self] self.property)) Anything after the optional property list (which will be translated to a setv within the class context) will be added to the class body. This allows one to have side effects and complex expressions within the class definition. As a side effect, defining methods is much more friendly now! Closes #850. Signed-off-by: Gergely Nagy --- docs/language/api.rst | 11 +++--- docs/tutorial.rst | 23 ++++++------ hy/compiler.py | 52 ++++++++++++---------------- tests/native_tests/contrib/meth.hy | 11 +++--- tests/native_tests/defclass.hy | 44 ++++++++++++++++------- tests/native_tests/mathematics.hy | 30 ++++++++-------- tests/native_tests/with_decorator.hy | 2 +- tests/native_tests/with_test.hy | 17 ++++----- 8 files changed, 99 insertions(+), 91 deletions(-) diff --git a/docs/language/api.rst b/docs/language/api.rst index 67ef630..8bb86a9 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -357,7 +357,9 @@ attributes of the new class as two item vectors. .. code-block:: clj (defclass class-name [super-class-1 super-class-2] - [[attribute value]]) + [attribute value] + + (defn method [self] (print "hello!"))) Both values and functions can be bound on the new class as shown by the example below: @@ -365,9 +367,10 @@ below: .. code-block:: clj => (defclass Cat [] - ... [[age None] - ... [colour "white"] - ... [speak (fn [self] (print "Meow"))]]) + ... [age None + ... colour "white"] + ... + ... (defn speak [self] (print "Meow"))) => (def spot (Cat)) => (setv spot.colour "Black") diff --git a/docs/tutorial.rst b/docs/tutorial.rst index 6b32b30..1eedea1 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -477,17 +477,14 @@ In Hy: (defclass FooBar [object] "Yet Another Example Class" - [[--init-- - (fn [self x] - (setv self.x x) - ; Currently needed for --init-- because __init__ needs None - ; Hopefully this will go away :) - None)] - [get-x - (fn [self] - "Return our copy of x" - self.x)]]) + (defn --init-- [self x] + (setv self.x x) + None) + + (defn get-x [self] + "Return our copy of x" + self.x)) You can also do class-level attributes. In Python:: @@ -502,9 +499,9 @@ In Hy: .. code-block:: clj (defclass Customer [models.Model] - [[name (models.CharField :max-length 255})] - [address (models.TextField)] - [notes (models.TextField)]]) + [name (models.CharField :max-length 255}) + address (models.TextField) + notes (models.TextField)]) Hy <-> Python interop ===================== diff --git a/hy/compiler.py b/hy/compiler.py index cd00530..dfc2bb1 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -2238,15 +2238,15 @@ class HyASTCompiler(object): @builds("defclass") @checkargs(min=1) - def compile_class_expression(self, expression): - expression.pop(0) # class + def compile_class_expression(self, expressions): + expressions.pop(0) # class - class_name = expression.pop(0) + class_name = expressions.pop(0) - if expression: - base_list = expression.pop(0) + if expressions: + base_list = expressions.pop(0) if not isinstance(base_list, HyList): - raise HyTypeError(expression, + raise HyTypeError(expressions, "Bases class must be a list") bases_expr, bases, _ = self._compile_collect(base_list) else: @@ -2256,8 +2256,8 @@ class HyASTCompiler(object): body = Result() # grab the doc string, if there is one - if expression and isinstance(expression[0], HyString): - docstring = expression.pop(0) + if expressions and isinstance(expressions[0], HyString): + docstring = expressions.pop(0) symb = HySymbol("__doc__") symb.start_line = docstring.start_line symb.start_column = docstring.start_column @@ -2266,31 +2266,25 @@ class HyASTCompiler(object): docstring.start_column) body += body.expr_as_stmt() - if expression: - try: - body_expression = iter(expression.pop(0)) - except TypeError: - raise HyTypeError( - expression, - "Wrong argument type for defclass attributes definition.") - for b in body_expression: - if isinstance(b, HyExpression): - b = macroexpand(b, self.module_name) - if len(b) != 2: - raise HyTypeError( - expression, - "Wrong number of argument in defclass attribute.") - body += self._compile_assign(b[0], b[1], - b.start_line, b.start_column) - body += body.expr_as_stmt() + if expressions and isinstance(expressions[0], HyList) \ + and not isinstance(expressions[0], HyExpression): + expr = expressions.pop(0) + body += self.compile( + HyExpression([ + HySymbol("setv") + ] + expr).replace(expr) + ) + + for expression in expressions: + body += self.compile(macroexpand(expression, self.module_name)) if not body.stmts: - body += ast.Pass(lineno=expression.start_line, - col_offset=expression.start_column) + body += ast.Pass(lineno=expressions.start_line, + col_offset=expressions.start_column) return bases + ast.ClassDef( - lineno=expression.start_line, - col_offset=expression.start_column, + lineno=expressions.start_line, + col_offset=expressions.start_column, decorator_list=[], name=ast_str(class_name), keywords=[], diff --git a/tests/native_tests/contrib/meth.hy b/tests/native_tests/contrib/meth.hy index 446bb81..d07adeb 100644 --- a/tests/native_tests/contrib/meth.hy +++ b/tests/native_tests/contrib/meth.hy @@ -2,12 +2,11 @@ (defclass FakeMeth [] "Mocking decorator class" - [[rules {}] - [route (fn [self rule &kwargs options] - (fn [f] - (assoc self.rules rule (, f options)) - f))]]) - + [rules {}] + (defn route [self rule &kwargs options] + (fn [f] + (assoc self.rules rule (, f options)) + f))) (defn test_route [] (let [[app (FakeMeth)]] diff --git a/tests/native_tests/defclass.hy b/tests/native_tests/defclass.hy index badb74c..3da9aa4 100644 --- a/tests/native_tests/defclass.hy +++ b/tests/native_tests/defclass.hy @@ -23,7 +23,7 @@ (defn test-defclass-attrs [] "NATIVE: test defclass attributes" (defclass A [] - [[x 42]]) + [x 42]) (assert (= A.x 42)) (assert (= (getattr (A) "x") 42))) @@ -31,9 +31,9 @@ (defn test-defclass-attrs-fn [] "NATIVE: test defclass attributes with fn" (defclass B [] - [[x 42] - [y (fn [self value] - (+ self.x value))]]) + [x 42 + y (fn [self value] + (+ self.x value))]) (assert (= B.x 42)) (assert (= (.y (B) 5) 47)) (let [[b (B)]] @@ -44,17 +44,17 @@ (defn test-defclass-dynamic-inheritance [] "NATIVE: test defclass with dynamic inheritance" (defclass A [((fn [] (if true list dict)))] - [[x 42]]) + [x 42]) (assert (isinstance (A) list)) (defclass A [((fn [] (if false list dict)))] - [[x 42]]) + [x 42]) (assert (isinstance (A) dict))) (defn test-defclass-no-fn-leak [] "NATIVE: test defclass attributes with fn" (defclass A [] - [[x (fn [] 1)]]) + [x (fn [] 1)]) (try (do (x) @@ -64,13 +64,13 @@ (defn test-defclass-docstring [] "NATIVE: test defclass docstring" (defclass A [] - [[--doc-- "doc string"] - [x 1]]) + [--doc-- "doc string" + x 1]) (setv a (A)) (assert (= a.__doc__ "doc string")) (defclass B [] "doc string" - [[x 1]]) + [x 1]) (setv b (B)) (assert (= b.x 1)) (assert (= b.__doc__ "doc string")) @@ -78,7 +78,7 @@ "begin a very long multi-line string to make sure that it comes out the way we hope and can span 3 lines end." - [[x 1]]) + [x 1]) (setv mL (MultiLine)) (assert (= mL.x 1)) (assert (in "begin" mL.__doc__)) @@ -86,8 +86,26 @@ (defn test-defclass-macroexpand [] "NATIVE: test defclass with macro expand" - (defmacro M [] `[x (fn [self x] (setv self._x x))]) - (defclass A [] [(M)]) + (defmacro M [] `(defn x [self x] (setv self._x x))) + (defclass A [] (M)) (setv a (A)) (a.x 1) (assert (= a._x 1))) + +(defn test-defclass-syntax [] + "NATIVE: test defclass syntax with properties and methods and side-effects" + (setv foo 1) + (defclass A [] + [x 1 + y 2] + (global foo) + (setv foo 2) + (defn greet [self] + "Greet the caller" + + "hello!")) + (setv a (A)) + (assert (= a.x 1)) + (assert (= a.y 2)) + (assert foo 2) + (assert (.greet a) "hello")) diff --git a/tests/native_tests/mathematics.hy b/tests/native_tests/mathematics.hy index e9a8945..7660363 100644 --- a/tests/native_tests/mathematics.hy +++ b/tests/native_tests/mathematics.hy @@ -145,21 +145,21 @@ (defclass HyTestMatrix [list] - [[--matmul-- - (fn [self other] - (let [[n (len self)] - [m (len (. other [0]))] - [result []]] - (for [i (range m)] - (let [[result-row []]] - (for [j (range n)] - (let [[dot-product 0]] - (for [k (range (len (. self [0])))] - (+= dot-product (* (. self [i] [k]) - (. other [k] [j])))) - (.append result-row dot-product))) - (.append result result-row))) - result))]]) + [--matmul-- + (fn [self other] + (let [[n (len self)] + [m (len (. other [0]))] + [result []]] + (for [i (range m)] + (let [[result-row []]] + (for [j (range n)] + (let [[dot-product 0]] + (for [k (range (len (. self [0])))] + (+= dot-product (* (. self [i] [k]) + (. other [k] [j])))) + (.append result-row dot-product))) + (.append result result-row))) + result))]) (def first-test-matrix (HyTestMatrix [[1 2 3] [4 5 6] diff --git a/tests/native_tests/with_decorator.hy b/tests/native_tests/with_decorator.hy index 2c8e930..5c5772d 100644 --- a/tests/native_tests/with_decorator.hy +++ b/tests/native_tests/with_decorator.hy @@ -13,7 +13,7 @@ (with-decorator bardec (defclass cls [] - [[my_attr 456]])) + [my_attr 456])) (defn test-decorator-clobbing [] "NATIVE: Tests whether nested decorators work" diff --git a/tests/native_tests/with_test.hy b/tests/native_tests/with_test.hy index b094f35..7b51923 100644 --- a/tests/native_tests/with_test.hy +++ b/tests/native_tests/with_test.hy @@ -1,16 +1,13 @@ (defclass WithTest [object] - [(--init-- - (fn [self val] - (setv self.val val) - None)) + (defn --init-- [self val] + (setv self.val val) + None) - (--enter-- - (fn [self] - self.val)) + (defn --enter-- [self] + self.val) - (--exit-- - (fn [self type value traceback] - (setv self.val None)))]) + (defn --exit-- [self type value traceback] + (setv self.val None))) (defn test-single-with [] "NATIVE: test a single with" From e8d26f106724d1c4bc660d5ebf36b54260f87254 Mon Sep 17 00:00:00 2001 From: gilch Date: Sat, 8 Aug 2015 22:04:02 -0600 Subject: [PATCH 02/53] remove throw in favor of raise --- docs/contrib/loop.rst | 2 +- docs/language/api.rst | 24 ++++++++++++------------ hy/compiler.py | 3 +-- hy/core/language.hy | 2 +- hy/errors.py | 2 +- tests/compilers/test_ast.py | 11 ----------- tests/native_tests/language.hy | 18 +++++++++--------- tests/native_tests/shadow.hy | 10 +++++----- 8 files changed, 30 insertions(+), 42 deletions(-) diff --git a/docs/contrib/loop.rst b/docs/contrib/loop.rst index 5dd9877..9968aab 100644 --- a/docs/contrib/loop.rst +++ b/docs/contrib/loop.rst @@ -37,7 +37,7 @@ loop ``loop`` establishes a recursion point. With ``loop``, ``recur`` rebinds the variables set in the recursion point and sends code execution back to that recursion point. If ``recur`` is used in a -non-tail position, an exception is thrown. +non-tail position, an exception is raised. Usage: `(loop bindings &rest body)` diff --git a/docs/language/api.rst b/docs/language/api.rst index 496caca..5891366 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -63,10 +63,10 @@ Compiles down to: which to do the attribute dereference. It uses bare symbols as attributes to access (in the example, *bar*, *baz*, *frob*), and compiles the contents of lists (in the example, ``[(+ 1 2)]``) for indexation. Other arguments -throw a compilation error. +raise a compilation error. -Access to unknown attributes throws an :exc:`AttributeError`. Access to -unknown keys throws an :exc:`IndexError` (on lists and tuples) or a +Access to unknown attributes raises an :exc:`AttributeError`. Access to +unknown keys raises an :exc:`IndexError` (on lists and tuples) or a :exc:`KeyError` (on dictionaries). -> @@ -835,7 +835,7 @@ assign a value to a global symbol. Reading a global symbol does not require the The following example shows how the global symbol ``a`` is assigned a value in a function and is later on printed in another function. Without the ``global`` -keyword, the second function would have thrown a ``NameError``. +keyword, the second function would have raised a ``NameError``. .. code-block:: clj @@ -1275,25 +1275,25 @@ counted starting from the end of the list. Some example usage: [6, 7] -throw / raise +raise ------------- -The ``throw`` or ``raise`` forms can be used to raise an ``Exception`` at +The or ``raise`` forms can be used to raise an ``Exception`` at runtime. Example usage: .. code-block:: clj - (throw) + (raise) ; re-rase the last exception - (throw IOError) - ; Throw an IOError + (raise IOError) + ; raise an IOError - (throw (IOError "foobar")) - ; Throw an IOError("foobar") + (raise (IOError "foobar")) + ; raise an IOError("foobar") -``throw`` can accept a single argument (an ``Exception`` class or instance) +``raise`` can accept a single argument (an ``Exception`` class or instance) or no arguments to re-raise the last ``Exception``. diff --git a/hy/compiler.py b/hy/compiler.py index f3ddb81..a317779 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -753,10 +753,9 @@ class HyASTCompiler(object): expression.pop(0) return self._compile_branch(expression) - @builds("throw") @builds("raise") @checkargs(multiple=[0, 1, 3]) - def compile_throw_expression(self, expr): + def compile_raise_expression(self, expr): expr.pop(0) ret = Result() if expr: diff --git a/hy/core/language.hy b/hy/core/language.hy index 63d3bb0..f4bd753 100644 --- a/hy/core/language.hy +++ b/hy/core/language.hy @@ -381,7 +381,7 @@ (while true (def inn (str (.read from-file 1))) (if (= inn eof) - (throw (EOFError "Reached end of file" ))) + (raise (EOFError "Reached end of file" ))) (setv buff (+ buff inn)) (try (def parsed (first (tokenize buff))) diff --git a/hy/errors.py b/hy/errors.py index e8bc2a8..931656f 100644 --- a/hy/errors.py +++ b/hy/errors.py @@ -116,6 +116,6 @@ class HyMacroExpansionError(HyTypeError): class HyIOError(HyError, IOError): """ Trivial subclass of IOError and HyError, to distinguish between - IOErrors thrown by Hy itself as opposed to Hy programs. + IOErrors raised by Hy itself as opposed to Hy programs. """ pass diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index 0931840..0b77840 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -111,17 +111,6 @@ def test_ast_good_do(): can_compile("(do 1)") -def test_ast_good_throw(): - "Make sure AST can compile valid throw" - can_compile("(throw)") - can_compile("(throw Exception)") - - -def test_ast_bad_throw(): - "Make sure AST can't compile invalid throw" - cant_compile("(throw Exception Exception)") - - def test_ast_good_raise(): "Make sure AST can compile valid raise" can_compile("(raise)") diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index d70410a..0c6595a 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -115,21 +115,21 @@ (for [x (range 2) y (range 2)] (break) - (else (throw Exception))) + (else (raise Exception))) ;; OK. This next test will ensure that the else is hooked up to the ;; "inner" iteration (for [x (range 2) y (range 2)] (if (= y 1) (break)) - (else (throw Exception))) + (else (raise Exception))) ;; OK. This next test will ensure that the else is hooked up to the ;; "outer" iteration (for [x (range 2) y (range 2)] (if (= x 1) (break)) - (else (throw Exception))) + (else (raise Exception))) ;; OK. This next test will ensure that we call the else branch exactly ;; once. @@ -369,7 +369,7 @@ (catch [e [KeyError]] (assert e))) (try - (throw (KeyError)) + (raise (KeyError)) (except [[IOError]] (assert false)) (catch [e [KeyError]] (assert e))) @@ -867,7 +867,7 @@ (assert (= None (eval (quote (print "")))))) -(defmacro assert-throw [exc-type &rest body] +(defmacro assert-raise [exc-type &rest body] `(try (do (eval ~@body) @@ -897,10 +897,10 @@ (defn test-eval-failure [] "NATIVE: test eval failure modes" (import [hy.errors [HyTypeError]]) - (assert-throw HyTypeError '(eval)) - (assert-throw HyTypeError '(eval "snafu")) - (assert-throw HyTypeError '(eval 'false [])) - (assert-throw HyTypeError '(eval 'false {} 1))) + (assert-raise HyTypeError '(eval)) + (assert-raise HyTypeError '(eval "snafu")) + (assert-raise HyTypeError '(eval 'false [])) + (assert-raise HyTypeError '(eval 'false {} 1))) (defn test-import-syntax [] diff --git a/tests/native_tests/shadow.hy b/tests/native_tests/shadow.hy index ccc8631..25df215 100644 --- a/tests/native_tests/shadow.hy +++ b/tests/native_tests/shadow.hy @@ -4,7 +4,7 @@ (assert (try (x) (catch [TypeError] True) - (else (throw AssertionError)))) + (else (raise AssertionError)))) (assert (= (x 1 2 3 4) 10)) (assert (= (x 1 2 3 4 5) 15)) ; with strings @@ -25,7 +25,7 @@ (assert (try (x) (catch [TypeError] True) - (else (throw AssertionError)))) + (else (raise AssertionError)))) (assert (= (x 1) -1)) (assert (= (x 2 1) 1)) (assert (= (x 2 1 1) 0)))) @@ -45,7 +45,7 @@ (assert (try (x) (catch [TypeError] True) - (else (throw AssertionError)))) + (else (raise AssertionError)))) (assert (= (x 1) 1)) (assert (= (x 8 2) 4)) (assert (= (x 8 2 2) 2)) @@ -58,11 +58,11 @@ (assert (try (x) (catch [TypeError] True) - (else (throw AssertionError)))) + (else (raise AssertionError)))) (assert (try (x 1) (catch [TypeError] True) - (else (throw AssertionError))))) + (else (raise AssertionError))))) (for [(, x y) [[< >=] [<= >] [= !=]]] From 66c1f38fccfbef511e9e3cd273cba99fafcf671f Mon Sep 17 00:00:00 2001 From: gilch Date: Sun, 9 Aug 2015 00:41:11 -0600 Subject: [PATCH 03/53] remove catch in favor of except --- docs/language/api.rst | 10 ++--- docs/language/core.rst | 2 +- eg/lxml/parse-tumblr.hy | 2 +- hy/compiler.py | 3 +- hy/contrib/botsbuildbots.hy | 2 +- hy/core/language.hy | 8 ++-- hy/core/macros.hy | 2 +- tests/compilers/test_ast.py | 20 ---------- tests/native_tests/contrib/loop.hy | 6 +-- tests/native_tests/core.hy | 60 ++++++++++++++-------------- tests/native_tests/language.hy | 58 +++++++++++++-------------- tests/native_tests/mathematics.hy | 4 +- tests/native_tests/native_macros.hy | 2 +- tests/native_tests/py3_only_tests.hy | 2 +- tests/native_tests/shadow.hy | 10 ++--- 15 files changed, 85 insertions(+), 106 deletions(-) diff --git a/docs/language/api.rst b/docs/language/api.rst index 5891366..d4f4843 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -1300,20 +1300,20 @@ or no arguments to re-raise the last ``Exception``. try --- -The ``try`` form is used to start a ``try`` / ``catch`` block. The form is +The ``try`` form is used to start a ``try`` / ``except`` block. The form is used as follows: .. code-block:: clj (try (error-prone-function) - (catch [e ZeroDivisionError] (print "Division by zero")) + (except [e ZeroDivisionError] (print "Division by zero")) (else (print "no errors")) (finally (print "all done"))) -``try`` must contain at least one ``catch`` block, and may optionally include -an ``else`` or ``finally`` block. If an error is raised with a matching catch -block during the execution of ``error-prone-function``, that ``catch`` block +``try`` must contain at least one ``except`` block, and may optionally include +an ``else`` or ``finally`` block. If an error is raised with a matching except +block during the execution of ``error-prone-function``, that ``except`` block will be executed. If no errors are raised, the ``else`` block is executed. The ``finally`` block will be executed last regardless of whether or not an error was raised. diff --git a/docs/language/core.rst b/docs/language/core.rst index dd6d8f3..f8522da 100644 --- a/docs/language/core.rst +++ b/docs/language/core.rst @@ -1072,7 +1072,7 @@ if *from-file* ends before a complete expression can be parsed. ... (do ... (print "OHY" exp) ... (eval exp)))) - ... (catch [e EOFError] + ... (except [e EOFError] ... (print "EOF!")))) OHY ('print' 'hello') hello diff --git a/eg/lxml/parse-tumblr.hy b/eg/lxml/parse-tumblr.hy index dbc2db0..3b513a4 100644 --- a/eg/lxml/parse-tumblr.hy +++ b/eg/lxml/parse-tumblr.hy @@ -7,7 +7,7 @@ (try (import [urllib.request [urlopen]]) - (catch [ImportError] + (except [ImportError] (import [urllib2 [urlopen]]))) (defn get-rss-feed-name [tumblr] diff --git a/hy/compiler.py b/hy/compiler.py index a317779..8ce2800 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -827,7 +827,7 @@ class HyASTCompiler(object): if not len(e): raise HyTypeError(e, "Empty list not allowed in `try'") - if e[0] in (HySymbol("except"), HySymbol("catch")): + if e[0] == HySymbol("except"): handler_results += self._compile_catch_expression(e, name) handlers.append(handler_results.stmts.pop()) elif e[0] == HySymbol("else"): @@ -905,7 +905,6 @@ class HyASTCompiler(object): return accumulated @builds("except") - @builds("catch") def magic_internal_form(self, expr): raise HyTypeError(expr, "Error: `%s' can't be used like that." % (expr[0])) diff --git a/hy/contrib/botsbuildbots.hy b/hy/contrib/botsbuildbots.hy index 77742bd..0063263 100644 --- a/hy/contrib/botsbuildbots.hy +++ b/hy/contrib/botsbuildbots.hy @@ -29,5 +29,5 @@ (let [[r (requests.get "https://raw.githubusercontent.com/hylang/hy/master/AUTHORS")]] (repeat r.text))) - (catch [e ImportError] + (except [e ImportError] (repeat "Botsbuildbots requires `requests' to function.")))) diff --git a/hy/core/language.hy b/hy/core/language.hy index f4bd753..a434331 100644 --- a/hy/core/language.hy +++ b/hy/core/language.hy @@ -222,8 +222,8 @@ "Return True if char `x` parses as an integer" (try (integer? (int x)) - (catch [e ValueError] False) - (catch [e TypeError] False))) + (except [e ValueError] False) + (except [e TypeError] False))) (defn interleave [&rest seqs] "Return an iterable of the first item in each of seqs, then the second etc." @@ -414,7 +414,7 @@ (HyKeyword (+ ":" (hyify value))) (try (hyify (.__name__ value)) - (catch [] (HyKeyword (+ ":" (string value)))))))) + (except [] (HyKeyword (+ ":" (string value)))))))) (defn name [value] "Convert the given value to a string. Keyword special character will be stripped. @@ -425,7 +425,7 @@ (hyify value) (try (hyify (. value __name__)) - (catch [] (string value)))))) + (except [] (string value)))))) (def *exports* '[butlast calling-module-name coll? cons cons? cycle dec distinct disassemble diff --git a/hy/core/macros.hy b/hy/core/macros.hy index 3f58683..0cf2561 100644 --- a/hy/core/macros.hy +++ b/hy/core/macros.hy @@ -189,7 +189,7 @@ (try (if (isinstance ~g!iter types.GeneratorType) (setv ~g!message (yield (.send ~g!iter ~g!message))) (setv ~g!message (yield (next ~g!iter)))) - (catch [~g!e StopIteration] + (except [~g!e StopIteration] (do (setv ~g!return (if (hasattr ~g!e "value") (. ~g!e value) nil)) diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index 0b77840..0fe320e 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -149,26 +149,6 @@ def test_ast_bad_try(): cant_compile("(try 1 (else 1))") -def test_ast_good_catch(): - "Make sure AST can compile valid catch" - can_compile("(try 1 (catch))") - can_compile("(try 1 (catch []))") - can_compile("(try 1 (catch [Foobar]))") - can_compile("(try 1 (catch [[]]))") - can_compile("(try 1 (catch [x FooBar]))") - can_compile("(try 1 (catch [x [FooBar BarFoo]]))") - can_compile("(try 1 (catch [x [FooBar BarFoo]]))") - - -def test_ast_bad_catch(): - "Make sure AST can't compile invalid catch" - cant_compile("(catch 22)") # heh - cant_compile("(try (catch 1))") - cant_compile("(try (catch \"A\"))") - cant_compile("(try (catch [1 3]))") - cant_compile("(try (catch [x [FooBar] BarBar]))") - - def test_ast_good_except(): "Make sure AST can compile valid except" can_compile("(try 1 (except))") diff --git a/tests/native_tests/contrib/loop.hy b/tests/native_tests/contrib/loop.hy index 520b840..175111a 100644 --- a/tests/native_tests/contrib/loop.hy +++ b/tests/native_tests/contrib/loop.hy @@ -18,7 +18,7 @@ ;; non-tco-sum should fail (try (setv n (non-tco-sum 100 10000)) - (catch [e RuntimeError] + (except [e RuntimeError] (assert true)) (else (assert false))) @@ -26,7 +26,7 @@ ;; tco-sum should not fail (try (setv n (tco-sum 100 10000)) - (catch [e RuntimeError] + (except [e RuntimeError] (assert false)) (else (assert (= n 10100))))) @@ -40,7 +40,7 @@ (try (bad-recur 3) - (catch [e TypeError] + (except [e TypeError] (assert true)) (else (assert false)))) diff --git a/tests/native_tests/core.hy b/tests/native_tests/core.hy index 7e73a4b..320faef 100644 --- a/tests/native_tests/core.hy +++ b/tests/native_tests/core.hy @@ -67,11 +67,11 @@ (assert-equal -1 (dec 0)) (assert-equal 0 (dec (dec 2))) (try (do (dec "foo") (assert False)) - (catch [e [TypeError]] (assert (in "not a number" (str e))))) + (except [e [TypeError]] (assert (in "not a number" (str e))))) (try (do (dec []) (assert False)) - (catch [e [TypeError]] (assert (in "not a number" (str e))))) + (except [e [TypeError]] (assert (in "not a number" (str e))))) (try (do (dec None) (assert False)) - (catch [e [TypeError]] (assert (in "not a number" (str e)))))) + (except [e [TypeError]] (assert (in "not a number" (str e)))))) (defn test-setv [] "NATIVE: testing setv mutation" @@ -85,9 +85,9 @@ (assert-equal (x y) 9) (assert-equal (y x) 9) (try (do (setv a.b 1) (assert False)) - (catch [e [NameError]] (assert (in "name 'a' is not defined" (str e))))) + (except [e [NameError]] (assert (in "name 'a' is not defined" (str e))))) (try (do (setv b.a (fn [x] x)) (assert False)) - (catch [e [NameError]] (assert (in "name 'b' is not defined" (str e))))) + (except [e [NameError]] (assert (in "name 'b' is not defined" (str e))))) (import itertools) (setv foopermutations (fn [x] (itertools.permutations x))) (setv p (set [(, 1 3 2) (, 3 2 1) (, 2 1 3) (, 3 1 2) (, 1 2 3) (, 2 3 1)])) @@ -127,7 +127,7 @@ (setv res (list (drop 0 [1 2 3 4 5]))) (assert-equal res [1 2 3 4 5]) (try (do (list (drop -1 [1 2 3 4 5])) (assert False)) - (catch [e [ValueError]] nil)) + (except [e [ValueError]] nil)) (setv res (list (drop 6 (iter [1 2 3 4 5])))) (assert-equal res []) (setv res (list (take 5 (drop 2 (iterate inc 0))))) @@ -174,11 +174,11 @@ (assert-false (even? 1)) (assert-true (even? 0)) (try (even? "foo") - (catch [e [TypeError]] (assert (in "not a number" (str e))))) + (except [e [TypeError]] (assert (in "not a number" (str e))))) (try (even? []) - (catch [e [TypeError]] (assert (in "not a number" (str e))))) + (except [e [TypeError]] (assert (in "not a number" (str e))))) (try (even? None) - (catch [e [TypeError]] (assert (in "not a number" (str e)))))) + (except [e [TypeError]] (assert (in "not a number" (str e)))))) (defn test-every? [] "NATIVE: testing the every? function" @@ -221,9 +221,9 @@ (setv res (flatten (, 1 (, None 3)))) (assert-equal res [1 None 3]) (try (flatten "foo") - (catch [e [TypeError]] (assert (in "not a collection" (str e))))) + (except [e [TypeError]] (assert (in "not a collection" (str e))))) (try (flatten 12.34) - (catch [e [TypeError]] (assert (in "not a collection" (str e)))))) + (except [e [TypeError]] (assert (in "not a collection" (str e)))))) (defn test-float? [] "NATIVE: testing the float? function" @@ -264,11 +264,11 @@ (assert-equal 3 (inc 2)) (assert-equal 0 (inc -1)) (try (do (inc "foo") (assert False)) - (catch [e [TypeError]] (assert (in "not a number" (str e))))) + (except [e [TypeError]] (assert (in "not a number" (str e))))) (try (do (inc []) (assert False)) - (catch [e [TypeError]] (assert (in "not a number" (str e))))) + (except [e [TypeError]] (assert (in "not a number" (str e))))) (try (do (inc None) (assert False)) - (catch [e [TypeError]] (assert (in "not a number" (str e)))))) + (except [e [TypeError]] (assert (in "not a number" (str e)))))) (defn test-instance [] "NATIVE: testing instance? function" @@ -395,11 +395,11 @@ (assert-false (neg? 1)) (assert-false (neg? 0)) (try (do (neg? "foo") (assert False)) - (catch [e [TypeError]] (assert (in "not a number" (str e))))) + (except [e [TypeError]] (assert (in "not a number" (str e))))) (try (do (neg? []) (assert False)) - (catch [e [TypeError]] (assert (in "not a number" (str e))))) + (except [e [TypeError]] (assert (in "not a number" (str e))))) (try (do (neg? None) (assert False)) - (catch [e [TypeError]] (assert (in "not a number" (str e)))))) + (except [e [TypeError]] (assert (in "not a number" (str e)))))) (defn test-zero [] "NATIVE: testing the zero? function" @@ -407,11 +407,11 @@ (assert-false (zero? 1)) (assert-true (zero? 0)) (try (do (zero? "foo") (assert False)) - (catch [e [TypeError]] (assert (in "not a number" (str e))))) + (except [e [TypeError]] (assert (in "not a number" (str e))))) (try (do (zero? []) (assert False)) - (catch [e [TypeError]] (assert (in "not a number" (str e))))) + (except [e [TypeError]] (assert (in "not a number" (str e))))) (try (do (zero? None) (assert False)) - (catch [e [TypeError]] (assert (in "not a number" (str e)))))) + (except [e [TypeError]] (assert (in "not a number" (str e)))))) (defn test-none [] "NATIVE: testing for `is None`" @@ -438,7 +438,7 @@ (assert-equal (nth [1 2 4 7] 5 "some default value") "some default value") ; with default specified (try (do (nth [1 2 4 7] -1) (assert False)) - (catch [e [ValueError]] nil)) + (except [e [ValueError]] nil)) ;; now for iterators (assert-equal 2 (nth (iter [1 2 4 7]) 1)) (assert-equal 7 (nth (iter [1 2 4 7]) 3)) @@ -446,7 +446,7 @@ (assert-equal (nth (iter [1 2 4 7]) 5 "some default value") "some default value") ; with default specified (try (do (nth (iter [1 2 4 7]) -1) (assert False)) - (catch [e [ValueError]] nil)) + (except [e [ValueError]] nil)) (assert-equal 5 (nth (take 3 (drop 2 [1 2 3 4 5 6])) 2))) (defn test-numeric? [] @@ -464,11 +464,11 @@ (assert-true (odd? 1)) (assert-false (odd? 0)) (try (do (odd? "foo") (assert False)) - (catch [e [TypeError]] (assert (in "not a number" (str e))))) + (except [e [TypeError]] (assert (in "not a number" (str e))))) (try (do (odd? []) (assert False)) - (catch [e [TypeError]] (assert (in "not a number" (str e))))) + (except [e [TypeError]] (assert (in "not a number" (str e))))) (try (do (odd? None) (assert False)) - (catch [e [TypeError]] (assert (in "not a number" (str e)))))) + (except [e [TypeError]] (assert (in "not a number" (str e)))))) (defn test-partition [] "NATIVE: testing the partition function" @@ -488,11 +488,11 @@ (assert-false (pos? -1)) (assert-false (pos? 0)) (try (do (pos? "foo") (assert False)) - (catch [e [TypeError]] (assert (in "not a number" (str e))))) + (except [e [TypeError]] (assert (in "not a number" (str e))))) (try (do (pos? []) (assert False)) - (catch [e [TypeError]] (assert (in "not a number" (str e))))) + (except [e [TypeError]] (assert (in "not a number" (str e))))) (try (do (pos? None) (assert False)) - (catch [e [TypeError]] (assert (in "not a number" (str e)))))) + (except [e [TypeError]] (assert (in "not a number" (str e)))))) (defn test-remove [] "NATIVE: testing the remove function" @@ -555,7 +555,7 @@ (setv res (list (take 0 (repeat "s")))) (assert-equal res []) (try (do (list (take -1 (repeat "s"))) (assert False)) - (catch [e [ValueError]] nil)) + (except [e [ValueError]] nil)) (setv res (list (take 6 [1 2 None 4]))) (assert-equal res [1 2 None 4])) @@ -582,7 +582,7 @@ (let [[passed false]] (try (setv res (list (take-nth 0 [1 2 3 4 5 6 7]))) - (catch [ValueError] (setv passed true))) + (except [ValueError] (setv passed true))) (assert passed))) (defn test-take-while [] diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index 0c6595a..ed6df0e 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -43,25 +43,25 @@ (defn test-setv-builtin [] "NATIVE: test that setv doesn't work on builtins" (try (eval '(setv False 1)) - (catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) + (except [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) (try (eval '(setv True 0)) - (catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) + (except [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) (try (eval '(setv None 1)) - (catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) + (except [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) (try (eval '(setv false 1)) - (catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) + (except [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) (try (eval '(setv true 0)) - (catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) + (except [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) (try (eval '(setv nil 1)) - (catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) + (except [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) (try (eval '(setv null 1)) - (catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) + (except [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) (try (eval '(defn defclass [] (print "hello"))) - (catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) + (except [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) (try (eval '(defn get [] (print "hello"))) - (catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) + (except [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) (try (eval '(defn lambda [] (print "hello"))) - (catch [e [TypeError]] (assert (in "Can't assign to a builtin" (str e)))))) + (except [e [TypeError]] (assert (in "Can't assign to a builtin" (str e)))))) (defn test-setv-pairs [] @@ -72,16 +72,16 @@ (setv y 0 x 1 y x) (assert y) (try (eval '(setv a 1 b)) - (catch [e [TypeError]] (assert (in "setv needs an even number of arguments" (str e)))))) + (except [e [TypeError]] (assert (in "setv needs an even number of arguments" (str e)))))) (defn test-fn-corner-cases [] "NATIVE: tests that fn/defn handles corner cases gracefully" (try (eval '(fn "foo")) - (catch [e [Exception]] (assert (in "to (fn) must be a list" + (except [e [Exception]] (assert (in "to (fn) must be a list" (str e))))) (try (eval '(defn foo "foo")) - (catch [e [Exception]] + (except [e [Exception]] (assert (in "takes a parameter list as second" (str e)))))) (defn test-for-loop [] @@ -365,22 +365,22 @@ (try (raise (KeyError)) - (catch [[IOError]] (assert false)) - (catch [e [KeyError]] (assert e))) + (except [[IOError]] (assert false)) + (except [e [KeyError]] (assert e))) (try (raise (KeyError)) (except [[IOError]] (assert false)) - (catch [e [KeyError]] (assert e))) + (except [e [KeyError]] (assert e))) (try (get [1] 3) - (catch [IndexError] (assert true)) + (except [IndexError] (assert true)) (except [IndexError] (do))) (try (print foobar42ofthebaz) - (catch [IndexError] (assert false)) + (except [IndexError] (assert false)) (except [NameError] (do))) (try @@ -389,7 +389,7 @@ (try (get [1] 3) - (catch [e [IndexError NameError]] (assert (isinstance e IndexError)))) + (except [e [IndexError NameError]] (assert (isinstance e IndexError)))) (try (print foobar42ofthebaz) @@ -397,15 +397,15 @@ (try (print foobar42) - (catch [[IndexError NameError]] (do))) + (except [[IndexError NameError]] (do))) (try (get [1] 3) - (catch [[IndexError NameError]] (do))) + (except [[IndexError NameError]] (do))) (try (print foobar42ofthebaz) - (catch)) + (except)) (try (print foobar42ofthebaz) @@ -417,7 +417,7 @@ (try (print foobar42ofthebaz) - (catch [] + (except [] (setv foobar42ofthebaz 42) (assert (= foobar42ofthebaz 42)))) @@ -754,7 +754,7 @@ 6)) (try (assert (= x 42)) ; This ain't true - (catch [e [NameError]] (assert e))) + (except [e [NameError]] (assert e))) (assert (= y 123))) @@ -872,7 +872,7 @@ (do (eval ~@body) (assert False "we shouldn't have arrived here")) - (catch [e Exception] + (except [e Exception] (assert (instance? ~exc-type e) (.format "Expected exception of type {}, got {}: {}" (. ~exc-type --name--) @@ -891,7 +891,7 @@ ; this should fail with a name error (eval (quote x) d2) (assert False "We shouldn't have arrived here")) - (catch [e Exception] + (except [e Exception] (assert (isinstance e NameError)))))) (defn test-eval-failure [] @@ -991,7 +991,7 @@ "NATIVE: test requiring macros from python code" (try (assert (= "this won't happen" (qplah 1 2 3 4))) - (catch [NameError])) + (except [NameError])) (require tests.resources.tlib) (assert (= [1 2 3] (qplah 1 2 3)))) @@ -1159,7 +1159,7 @@ "NATIVE: test lambda lists are only parsed in defn" (try (foo [&rest spam] 1) - (catch [NameError] True) + (except [NameError] True) (else (raise AssertionError)))) (defn test-read [] @@ -1183,7 +1183,7 @@ (read stdin-buffer) (try (read stdin-buffer) - (catch [e Exception] + (except [e Exception] (assert (isinstance e EOFError))))) (defn test-read-str [] diff --git a/tests/native_tests/mathematics.hy b/tests/native_tests/mathematics.hy index e9a8945..b5514d2 100644 --- a/tests/native_tests/mathematics.hy +++ b/tests/native_tests/mathematics.hy @@ -180,14 +180,14 @@ product-of-test-matrices)) ;; Python <= 3.4 (let [[matmul-attempt (try (@ first-test-matrix second-test-matrix) - (catch [e [Exception]] e))]] + (except [e [Exception]] e))]] (assert (isinstance matmul-attempt NameError))))) (defn test-augassign-matmul [] "NATIVE: test augmented-assignment matrix multiplication" (let [[matrix first-test-matrix] [matmul-attempt (try (@= matrix second-test-matrix) - (catch [e [Exception]] e))]] + (except [e [Exception]] e))]] (if PY35 (assert (= product-of-test-matrices matrix)) (assert (isinstance matmul-attempt NameError))))) diff --git a/tests/native_tests/native_macros.hy b/tests/native_tests/native_macros.hy index 5ad2df2..a27e465 100644 --- a/tests/native_tests/native_macros.hy +++ b/tests/native_tests/native_macros.hy @@ -253,7 +253,7 @@ (yield i)) (try (yield-from (yield-from-subgenerator-test)) - (catch [e AssertionError] + (except [e AssertionError] (yield 4)))) (assert (= (list (yield-from-test)) [0 1 2 1 2 3 4]))) diff --git a/tests/native_tests/py3_only_tests.hy b/tests/native_tests/py3_only_tests.hy index 1ad58a7..2cf91ba 100644 --- a/tests/native_tests/py3_only_tests.hy +++ b/tests/native_tests/py3_only_tests.hy @@ -22,7 +22,7 @@ (let [[kwonly-foo-no-default (fn [&kwonly foo] foo)] [attempt-to-omit-default (try (kwonly-foo-no-default) - (catch [e [Exception]] e))]] + (except [e [Exception]] e))]] ;; works (assert (= (apply kwonly-foo-no-default [] {"foo" "quux"}) "quux")) ;; raises TypeError with appropriate message if not supplied diff --git a/tests/native_tests/shadow.hy b/tests/native_tests/shadow.hy index 25df215..0a89f46 100644 --- a/tests/native_tests/shadow.hy +++ b/tests/native_tests/shadow.hy @@ -3,7 +3,7 @@ (let [[x +]] (assert (try (x) - (catch [TypeError] True) + (except [TypeError] True) (else (raise AssertionError)))) (assert (= (x 1 2 3 4) 10)) (assert (= (x 1 2 3 4 5) 15)) @@ -24,7 +24,7 @@ (let [[x -]] (assert (try (x) - (catch [TypeError] True) + (except [TypeError] True) (else (raise AssertionError)))) (assert (= (x 1) -1)) (assert (= (x 2 1) 1)) @@ -44,7 +44,7 @@ (let [[x /]] (assert (try (x) - (catch [TypeError] True) + (except [TypeError] True) (else (raise AssertionError)))) (assert (= (x 1) 1)) (assert (= (x 8 2) 4)) @@ -57,11 +57,11 @@ (for [x [< <= = != >= >]] (assert (try (x) - (catch [TypeError] True) + (except [TypeError] True) (else (raise AssertionError)))) (assert (try (x 1) - (catch [TypeError] True) + (except [TypeError] True) (else (raise AssertionError))))) (for [(, x y) [[< >=] [<= >] From 33e0b4b3db14f17f2d83073062f270e31c33314f Mon Sep 17 00:00:00 2001 From: gilch Date: Sun, 9 Aug 2015 01:00:51 -0600 Subject: [PATCH 04/53] remove progn in favor of do --- docs/language/api.rst | 4 ++-- hy/compiler.py | 7 +++---- hy/contrib/meth.hy | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/language/api.rst b/docs/language/api.rst index d4f4843..45b1f68 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -289,10 +289,10 @@ conditional expression. {1: 2, 3: 6, 9: 18, 5: 10, 7: 14} -do / progn +do ---------- -``do`` and `progn` are used to evaluate each of their arguments and return the +``do`` is used to evaluate each of its arguments and return the last one. Return values from every other than the last argument are discarded. It can be used in ``lambda`` or ``list-comp`` to perform more complex logic as shown in one of the following examples. diff --git a/hy/compiler.py b/hy/compiler.py index 8ce2800..7befb1f 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -748,8 +748,7 @@ class HyASTCompiler(object): return ret @builds("do") - @builds("progn") - def compile_progn(self, expression): + def compile_do(self, expression): expression.pop(0) return self._compile_branch(expression) @@ -2371,7 +2370,7 @@ class HyASTCompiler(object): @builds("eval_and_compile") def compile_eval_and_compile(self, expression): - expression[0] = HySymbol("progn") + expression[0] = HySymbol("do") hy.importer.hy_eval(expression, compile_time_ns(self.module_name), self.module_name) @@ -2380,7 +2379,7 @@ class HyASTCompiler(object): @builds("eval_when_compile") def compile_eval_when_compile(self, expression): - expression[0] = HySymbol("progn") + expression[0] = HySymbol("do") hy.importer.hy_eval(expression, compile_time_ns(self.module_name), self.module_name) diff --git a/hy/contrib/meth.hy b/hy/contrib/meth.hy index e4b9bd0..172e127 100644 --- a/hy/contrib/meth.hy +++ b/hy/contrib/meth.hy @@ -7,7 +7,7 @@ {"methods" ~methods})]] (with-decorator deco (defn ~name ~params - (progn ~@code))))) + (do ~@code))))) ;; Some macro examples (defmacro route [name path params &rest code] From 4cdfdfbafec42ccb53a19114dd7c41fb66ecb1b3 Mon Sep 17 00:00:00 2001 From: gilch Date: Sun, 9 Aug 2015 01:09:52 -0600 Subject: [PATCH 05/53] remove defun in favor of defn --- docs/language/api.rst | 14 +++++++------- hy/contrib/botsbuildbots.hy | 2 +- hy/contrib/loop.hy | 2 +- hy/core/bootstrap.hy | 6 +++--- hy/core/macros.hy | 2 +- scripts/reformat-changelog | 12 ++++++------ tests/native_tests/native_macros.hy | 1 - 7 files changed, 19 insertions(+), 20 deletions(-) diff --git a/docs/language/api.rst b/docs/language/api.rst index 45b1f68..05d122c 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -378,10 +378,10 @@ below: .. _defn: -defn / defun +defn ------------ -``defn`` and ``defun`` macros are used to define functions. They take three +``defn`` macro is used to define functions. It takes three parameters: the *name* of the function to define, a vector of *parameters*, and the *body* of the function: @@ -488,18 +488,18 @@ Parameters may have the following keywords in front of them: Availability: Python 3. -.. _defn-alias / defun-alias: +.. _defn-alias: -defn-alias / defun-alias +defn-alias ------------------------ .. versionadded:: 0.10.0 -The ``defn-alias`` and ``defun-alias`` macros are much like `defn`_, +The ``defn-alias`` and macro is much like `defn`_, with the distinction that instead of defining a function with a single name, these can also define aliases. Other than taking a list of -symbols for function names as the first parameter, ``defn-alias`` and -``defun-alias`` are no different from ``defn`` and ``defun``. +symbols for function names as the first parameter, ``defn-alias`` +is no different from ``defn``. .. code-block:: clj diff --git a/hy/contrib/botsbuildbots.hy b/hy/contrib/botsbuildbots.hy index 0063263..98f3737 100644 --- a/hy/contrib/botsbuildbots.hy +++ b/hy/contrib/botsbuildbots.hy @@ -18,7 +18,7 @@ ;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ;; DEALINGS IN THE SOFTWARE. -(defun Botsbuildbots () (Botsbuildbots)) +(defn Botsbuildbots () (Botsbuildbots)) (defmacro Botsbuildbots [] "Build bots, repeatedly.^W^W^WPrint the AUTHORS, forever." diff --git a/hy/contrib/loop.hy b/hy/contrib/loop.hy index 2c2690a..17bb0d9 100644 --- a/hy/contrib/loop.hy +++ b/hy/contrib/loop.hy @@ -75,7 +75,7 @@ (defmacro/g! loop [bindings &rest body] ;; Use inside functions like so: - ;; (defun factorial [n] + ;; (defn factorial [n] ;; (loop [[i n] ;; [acc 1]] ;; (if (= i 0) diff --git a/hy/core/bootstrap.hy b/hy/core/bootstrap.hy index 741e354..f64c3d5 100644 --- a/hy/core/bootstrap.hy +++ b/hy/core/bootstrap.hy @@ -40,12 +40,12 @@ ret) -(defmacro-alias [defn defun] [name lambda-list &rest body] +(defmacro defn [name lambda-list &rest body] "define a function `name` with signature `lambda-list` and body `body`" (if (not (= (type name) HySymbol)) - (macro-error name "defn/defun takes a name as first argument")) + (macro-error name "defn takes a name as first argument")) (if (not (isinstance lambda-list HyList)) - (macro-error name "defn/defun takes a parameter list as second argument")) + (macro-error name "defn takes a parameter list as second argument")) `(setv ~name (fn ~lambda-list ~@body))) diff --git a/hy/core/macros.hy b/hy/core/macros.hy index 0cf2561..0ba0671 100644 --- a/hy/core/macros.hy +++ b/hy/core/macros.hy @@ -210,7 +210,7 @@ (sys.exit ~retval))))) -(defmacro-alias [defn-alias defun-alias] [names lambda-list &rest body] +(defmacro defn-alias [names lambda-list &rest body] "define one function with several names" (let [[main (first names)] [aliases (rest names)]] diff --git a/scripts/reformat-changelog b/scripts/reformat-changelog index 2c74480..cb4f2a0 100755 --- a/scripts/reformat-changelog +++ b/scripts/reformat-changelog @@ -7,13 +7,13 @@ (setv *maintainer-line* " -- Alexander Artemenko Thu, 30 Sep 2014 13:06:09 +0400") -(defun read-lines-from-file [filename] +(defn read-lines-from-file [filename] (let [[f (codecs.open filename "r" "utf-8")]] (fn [] (let [[line (.readline f) ]] line)))) -(defun get-version-number [line] +(defn get-version-number [line] (let [[match (re.search r"Changes from.*(\d+\.\d+\.\d+)$" line)]] (if match (let [[version (.group match (int 1))] @@ -26,7 +26,7 @@ (.join "." (map str numbered))))))) -(defun read-version-content [reader] +(defn read-version-content [reader] (setv line (reader)) (setv content []) (while (and line (not (get-version-number line))) @@ -35,12 +35,12 @@ [content line]) -(defun read-versions-from-file [filename] +(defn read-versions-from-file [filename] (let [[reader (read-lines-from-file filename)]] (read-versions-rec (reader) reader))) -(defun read-versions-rec [line reader] +(defn read-versions-rec [line reader] (if line (let [[version (get-version-number line)] [[content next-line] (read-version-content reader)]] @@ -50,7 +50,7 @@ (read-versions-rec next-line reader))) [])) -(defun format-deb-version [version] +(defn format-deb-version [version] (setv result [(.format "hy ({}) unstable; urgency=low" (get version "from"))]) (for [line (get version "content")] diff --git a/tests/native_tests/native_macros.hy b/tests/native_tests/native_macros.hy index a27e465..69b349c 100644 --- a/tests/native_tests/native_macros.hy +++ b/tests/native_tests/native_macros.hy @@ -227,7 +227,6 @@ (defn test-defn-alias [] (defn-alias [tda-main tda-a1 tda-a2] [] :bazinga) - (defun-alias [tda-main tda-a1 tda-a2] [] :bazinga) (assert (= (tda-main) :bazinga)) (assert (= (tda-a1) :bazinga)) (assert (= (tda-a2) :bazinga)) From 7d8ddd9ecb9f5ddf9a918601b102ec8a4ee2bd35 Mon Sep 17 00:00:00 2001 From: gilch Date: Sun, 9 Aug 2015 01:21:12 -0600 Subject: [PATCH 06/53] remove lisp-if / lisp-if-not in favor of lif / lif-not --- docs/language/api.rst | 36 +++++++++-------------- hy/core/macros.hy | 4 +-- tests/native_tests/native_macros.hy | 44 +++++++++++++---------------- 3 files changed, 36 insertions(+), 48 deletions(-) diff --git a/docs/language/api.rst b/docs/language/api.rst index 05d122c..47feb4b 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -881,47 +881,39 @@ an empty sequence, and an empty dictionary are considered ``False``; everything else is considered ``True``. -lisp-if / lif and lisp-if-not / lif-not +lif and lif-not --------------------------------------- .. versionadded:: 0.10.0 .. versionadded:: 0.11.0 - lisp-if-not / lif-not + lif-not -For those that prefer a more Lispy ``if`` clause, we have ``lisp-if``, or +For those that prefer a more Lispy ``if`` clause, we have, or ``lif``. This *only* considers ``None`` / ``nil`` to be false! All other "false-ish" Python values are considered true. Conversely, we have -``lisp-if-not`` and ``lif-not`` in parallel to ``if`` and ``if-not`` which +``lif-not`` in parallel to ``if`` and ``if-not`` which reverses the comparison. .. code-block:: clj - => (lisp-if True "true" "false") - "true" - => (lisp-if False "true" "false") - "true" - => (lisp-if 0 "true" "false") - "true" - => (lisp-if nil "true" "false") - "false" - => (lisp-if None "true" "false") - "false" - => (lisp-if-not nil "true" "false") - "true" - => (lisp-if-not None "true" "false") - "true" - => (lisp-if-not False "true" "false") - "false" - - ; Equivalent but shorter => (lif True "true" "false") "true" + => (lif False "true" "false") + "true" + => (lif 0 "true" "false") + "true" => (lif nil "true" "false") "false" + => (lif None "true" "false") + "false" + => (lif-not nil "true" "false") + "true" => (lif-not None "true" "false") "true" + => (lif-not False "true" "false") + "false" import diff --git a/hy/core/macros.hy b/hy/core/macros.hy index 0ba0671..f70f928 100644 --- a/hy/core/macros.hy +++ b/hy/core/macros.hy @@ -149,11 +149,11 @@ `(if (not ~test) ~not-branch ~yes-branch))) -(defmacro-alias [lisp-if lif] [test &rest branches] +(defmacro lif [test &rest branches] "Like `if`, but anything that is not None/nil is considered true." `(if (is-not ~test nil) ~@branches)) -(defmacro-alias [lisp-if-not lif-not] [test &rest branches] +(defmacro lif-not [test &rest branches] "Like `if-not`, but anything that is not None/nil is considered true." `(if (is ~test nil) ~@branches)) diff --git a/tests/native_tests/native_macros.hy b/tests/native_tests/native_macros.hy index 69b349c..36d6291 100644 --- a/tests/native_tests/native_macros.hy +++ b/tests/native_tests/native_macros.hy @@ -188,39 +188,35 @@ :yes))) -(defn test-lisp-if [] - "test that lisp-if works as expected" +(defn test-lif [] + "test that lif works as expected" ; nil is false - (assert (= (lisp-if None "true" "false") "false")) - (assert (= (lisp-if nil "true" "false") "false")) + (assert (= (lif None "true" "false") "false")) + (assert (= (lif nil "true" "false") "false")) ; But everything else is True! Even falsey things. - (assert (= (lisp-if True "true" "false") "true")) - (assert (= (lisp-if False "true" "false") "true")) - (assert (= (lisp-if 0 "true" "false") "true")) - (assert (= (lisp-if "some-string" "true" "false") "true")) - (assert (= (lisp-if "" "true" "false") "true")) - (assert (= (lisp-if (+ 1 2 3) "true" "false") "true")) - - ; Just to be sure, test the alias lif + (assert (= (lif True "true" "false") "true")) + (assert (= (lif False "true" "false") "true")) + (assert (= (lif 0 "true" "false") "true")) + (assert (= (lif "some-string" "true" "false") "true")) + (assert (= (lif "" "true" "false") "true")) + (assert (= (lif (+ 1 2 3) "true" "false") "true")) (assert (= (lif nil "true" "false") "false")) (assert (= (lif 0 "true" "false") "true"))) -(defn test-lisp-if-not [] - "test that lisp-if-not works as expected" +(defn test-lif-not [] + "test that lif-not works as expected" ; nil is false - (assert (= (lisp-if-not None "false" "true") "false")) - (assert (= (lisp-if-not nil "false" "true") "false")) + (assert (= (lif-not None "false" "true") "false")) + (assert (= (lif-not nil "false" "true") "false")) ; But everything else is True! Even falsey things. - (assert (= (lisp-if-not True "false" "true") "true")) - (assert (= (lisp-if-not False "false" "true") "true")) - (assert (= (lisp-if-not 0 "false" "true") "true")) - (assert (= (lisp-if-not "some-string" "false" "true") "true")) - (assert (= (lisp-if-not "" "false" "true") "true")) - (assert (= (lisp-if-not (+ 1 2 3) "false" "true") "true")) - - ; Just to be sure, test the alias lif-not + (assert (= (lif-not True "false" "true") "true")) + (assert (= (lif-not False "false" "true") "true")) + (assert (= (lif-not 0 "false" "true") "true")) + (assert (= (lif-not "some-string" "false" "true") "true")) + (assert (= (lif-not "" "false" "true") "true")) + (assert (= (lif-not (+ 1 2 3) "false" "true") "true")) (assert (= (lif-not nil "false" "true") "false")) (assert (= (lif-not 0 "false" "true") "true"))) From 8e2a892469fef2afc8cca53d45c62695ad5be740 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Mon, 10 Aug 2015 09:24:48 +0200 Subject: [PATCH 07/53] hy.contrib.alias: Move defn-alias and defmacro-alias here As discussed in #880, move defn-alias and defmacro-alias to a contrib module. Signed-off-by: Gergely Nagy --- docs/contrib/alias.rst | 60 +++++++++++++++++++++++++++++ docs/contrib/index.rst | 1 + docs/language/api.rst | 47 ---------------------- hy/contrib/alias.hy | 39 +++++++++++++++++++ hy/core/bootstrap.hy | 9 ----- hy/core/macros.hy | 10 ----- tests/native_tests/contrib/alias.hy | 8 ++++ tests/native_tests/native_macros.hy | 7 ---- 8 files changed, 108 insertions(+), 73 deletions(-) create mode 100644 docs/contrib/alias.rst create mode 100644 hy/contrib/alias.hy create mode 100644 tests/native_tests/contrib/alias.hy diff --git a/docs/contrib/alias.rst b/docs/contrib/alias.rst new file mode 100644 index 0000000..b7fed4a --- /dev/null +++ b/docs/contrib/alias.rst @@ -0,0 +1,60 @@ +============ +Alias macros +============ + +.. versionadded:: 0.12 + +The alias macro module provides the ``(defn-alias)`` and +``(defmacro-alias)``, that were in Hy core previously. + + +Macros +====== + + +.. _defn-alias: + +defn-alias +------------------------ + +The ``defn-alias`` and macro is much like `defn`_, +with the distinction that instead of defining a function with a single +name, these can also define aliases. Other than taking a list of +symbols for function names as the first parameter, ``defn-alias`` +is no different from ``defn``. + +.. code-block:: clj + + => (defn-alias [main-name alias] [] + ... (print "Hello!")) + => (main-name) + "Hello!" + => (alias) + "Hello!" + + +.. _defmacro-alias: + +defmacro-alias +-------------- + +``defmacro-alias`` is used to define macros with multiple names +(aliases). The general format is ``(defmacro-alias [names] [parameters] +expr)``. It creates multiple macros with the same parameter list and +body, under the specified list of names. + +The following example defines two macros, both of which allow the user +to write code in infix notation. + +.. code-block:: clj + + => (defmacro-alias [infix infi] [code] + ... (quasiquote ( + ... (unquote (get code 1)) + ... (unquote (get code 0)) + ... (unquote (get code 2))))) + + => (infix (1 + 1)) + 2 + => (infi (1 + 1)) + 2 diff --git a/docs/contrib/index.rst b/docs/contrib/index.rst index ebed64f..612ccdd 100644 --- a/docs/contrib/index.rst +++ b/docs/contrib/index.rst @@ -11,3 +11,4 @@ Contents: loop multi flow + alias diff --git a/docs/language/api.rst b/docs/language/api.rst index 47feb4b..13c9b5c 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -488,28 +488,6 @@ Parameters may have the following keywords in front of them: Availability: Python 3. -.. _defn-alias: - -defn-alias ------------------------- - -.. versionadded:: 0.10.0 - -The ``defn-alias`` and macro is much like `defn`_, -with the distinction that instead of defining a function with a single -name, these can also define aliases. Other than taking a list of -symbols for function names as the first parameter, ``defn-alias`` -is no different from ``defn``. - -.. code-block:: clj - - => (defn-alias [main-name alias] [] - ... (print "Hello!")) - => (main-name) - "Hello!" - => (alias) - "Hello!" - defmain ------- @@ -571,31 +549,6 @@ between the operands. => (infix (1 + 1)) 2 -.. _defmacro-alias: - -defmacro-alias --------------- - -``defmacro-alias`` is used to define macros with multiple names -(aliases). The general format is ``(defmacro-alias [names] [parameters] -expr)``. It creates multiple macros with the same parameter list and -body, under the specified list of names. - -The following example defines two macros, both of which allow the user -to write code in infix notation. - -.. code-block:: clj - - => (defmacro-alias [infix infi] [code] - ... (quasiquote ( - ... (unquote (get code 1)) - ... (unquote (get code 0)) - ... (unquote (get code 2))))) - - => (infix (1 + 1)) - 2 - => (infi (1 + 1)) - 2 .. _defmacro/g!: diff --git a/hy/contrib/alias.hy b/hy/contrib/alias.hy new file mode 100644 index 0000000..b1e0d5d --- /dev/null +++ b/hy/contrib/alias.hy @@ -0,0 +1,39 @@ +;; Copyright (c) 2014, 2015 Gergely Nagy +;; Copyright (c) 2014, 2015 Paul Tagliamonte + +;; Permission is hereby granted, free of charge, to any person obtaining a +;; copy of this software and associated documentation files (the "Software"), +;; to deal in the Software without restriction, including without limitation +;; the rights to use, copy, modify, merge, publish, distribute, sublicense, +;; and/or sell copies of the Software, and to permit persons to whom the +;; Software is furnished to do so, subject to the following conditions: + +;; The above copyright notice and this permission notice shall be included in +;; all copies or substantial portions of the Software. + +;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +;; DEALINGS IN THE SOFTWARE. + +(defmacro defmacro-alias [names lambda-list &rest body] + "define one macro with several names" + (setv ret `(do)) + (for* [name names] + (.append ret + `(defmacro ~name ~lambda-list ~@body))) + ret) + + +(defmacro defn-alias [names lambda-list &rest body] + "define one function with several names" + (let [[main (first names)] + [aliases (rest names)]] + (setv ret `(do (defn ~main ~lambda-list ~@body))) + (for* [name aliases] + (.append ret + `(setv ~name ~main))) + ret)) diff --git a/hy/core/bootstrap.hy b/hy/core/bootstrap.hy index f64c3d5..db63a06 100644 --- a/hy/core/bootstrap.hy +++ b/hy/core/bootstrap.hy @@ -31,15 +31,6 @@ `(raise (hy.errors.HyMacroExpansionError ~location ~reason))) -(defmacro defmacro-alias [names lambda-list &rest body] - "define one macro with several names" - (setv ret `(do)) - (for* [name names] - (.append ret - `(defmacro ~name ~lambda-list ~@body))) - ret) - - (defmacro defn [name lambda-list &rest body] "define a function `name` with signature `lambda-list` and body `body`" (if (not (= (type name) HySymbol)) diff --git a/hy/core/macros.hy b/hy/core/macros.hy index f70f928..d816ff9 100644 --- a/hy/core/macros.hy +++ b/hy/core/macros.hy @@ -210,16 +210,6 @@ (sys.exit ~retval))))) -(defmacro defn-alias [names lambda-list &rest body] - "define one function with several names" - (let [[main (first names)] - [aliases (rest names)]] - (setv ret `(do (defn ~main ~lambda-list ~@body))) - (for* [name aliases] - (.append ret - `(setv ~name ~main))) - ret)) - (defreader @ [expr] (let [[decorators (cut expr nil -1)] [fndef (get expr -1)]] diff --git a/tests/native_tests/contrib/alias.hy b/tests/native_tests/contrib/alias.hy new file mode 100644 index 0000000..7f3e66c --- /dev/null +++ b/tests/native_tests/contrib/alias.hy @@ -0,0 +1,8 @@ +(require hy.contrib.alias) + +(defn test-defn-alias [] + (defn-alias [tda-main tda-a1 tda-a2] [] :bazinga) + (assert (= (tda-main) :bazinga)) + (assert (= (tda-a1) :bazinga)) + (assert (= (tda-a2) :bazinga)) + (assert (= tda-main tda-a1 tda-a2))) diff --git a/tests/native_tests/native_macros.hy b/tests/native_tests/native_macros.hy index 36d6291..d16ea1d 100644 --- a/tests/native_tests/native_macros.hy +++ b/tests/native_tests/native_macros.hy @@ -221,13 +221,6 @@ (assert (= (lif-not 0 "false" "true") "true"))) -(defn test-defn-alias [] - (defn-alias [tda-main tda-a1 tda-a2] [] :bazinga) - (assert (= (tda-main) :bazinga)) - (assert (= (tda-a1) :bazinga)) - (assert (= (tda-a2) :bazinga)) - (assert (= tda-main tda-a1 tda-a2))) - (defn test-yield-from [] "NATIVE: testing yield from" (defn yield-from-test [] From dee02c59282076a780e68fd3a361bbc550ef91df Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Mon, 10 Aug 2015 09:33:23 +0200 Subject: [PATCH 08/53] docs: Minor corrections, related to the alias removals Fixed up the documentation language here and there, related to the alias removals in previous commits. Signed-off-by: Gergely Nagy --- docs/contrib/alias.rst | 2 +- docs/language/api.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/contrib/alias.rst b/docs/contrib/alias.rst index b7fed4a..eb610cf 100644 --- a/docs/contrib/alias.rst +++ b/docs/contrib/alias.rst @@ -17,7 +17,7 @@ Macros defn-alias ------------------------ -The ``defn-alias`` and macro is much like `defn`_, +The ``defn-alias`` macro is much like ``defn``, with the distinction that instead of defining a function with a single name, these can also define aliases. Other than taking a list of symbols for function names as the first parameter, ``defn-alias`` diff --git a/docs/language/api.rst b/docs/language/api.rst index 13c9b5c..d348d75 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -842,7 +842,7 @@ lif and lif-not .. versionadded:: 0.11.0 lif-not -For those that prefer a more Lispy ``if`` clause, we have, or +For those that prefer a more Lispy ``if`` clause, we have ``lif``. This *only* considers ``None`` / ``nil`` to be false! All other "false-ish" Python values are considered true. Conversely, we have ``lif-not`` in parallel to ``if`` and ``if-not`` which @@ -1223,7 +1223,7 @@ counted starting from the end of the list. Some example usage: raise ------------- -The or ``raise`` forms can be used to raise an ``Exception`` at +The ``raise`` form can be used to raise an ``Exception`` at runtime. Example usage: .. code-block:: clj From 6c076f76f7e1f81b8a947becdac05e92da6f5b2b Mon Sep 17 00:00:00 2001 From: Ryan Gonzalez Date: Thu, 30 Jul 2015 17:31:57 -0500 Subject: [PATCH 09/53] Allow 'for' and 'cond' to take a multi-expression body (closes #868) --- docs/language/api.rst | 9 ++++----- hy/core/macros.hy | 15 ++++++++------- tests/native_tests/contrib/walk.hy | 2 +- tests/native_tests/language.hy | 10 ++++++---- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/docs/language/api.rst b/docs/language/api.rst index 9b8c0b4..b18eaef 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -267,11 +267,10 @@ is only called on every other value in the list. ;; collection is a list of numerical values (for [x collection] - (do - (side-effect1 x) - (if (% x 2) - (continue)) - (side-effect2 x))) + (side-effect1 x) + (if (% x 2) + (continue)) + (side-effect2 x)) dict-comp diff --git a/hy/core/macros.hy b/hy/core/macros.hy index d816ff9..ff8f1fd 100644 --- a/hy/core/macros.hy +++ b/hy/core/macros.hy @@ -73,10 +73,11 @@ "check `cond` branch for validity, return the corresponding `if` expr" (if (not (= (type branch) HyList)) (macro-error branch "cond branches need to be a list")) - (if (!= (len branch) 2) - (macro-error branch "cond branches need two items: a test and a code branch")) - (setv (, test thebranch) branch) - `(if ~test ~thebranch)) + (if (< (len branch) 2) + (macro-error branch "cond branches need at least two items: a test and one or more code branches")) + (setv test (car branch)) + (setv thebranch (cdr branch)) + `(if ~test (do ~@thebranch))) (setv root (check-branch branch)) (setv latest-branch root) @@ -96,14 +97,14 @@ (for* [x foo] (for* [y bar] baz))" - (cond + (cond [(odd? (len args)) (macro-error args "`for' requires an even number of args.")] [(empty? body) (macro-error None "`for' requires a body to evaluate")] [(empty? args) `(do ~@body)] - [(= (len args) 2) `(for* [~@args] ~@body)] - [true + [(= (len args) 2) `(for* [~@args] (do ~@body))] + [true (let [[alist (cut args 0 nil 2)]] `(for* [(, ~@alist) (genexpr (, ~@alist) [~@args])] ~@body))])) diff --git a/tests/native_tests/contrib/walk.hy b/tests/native_tests/contrib/walk.hy index abd4360..b282fda 100644 --- a/tests/native_tests/contrib/walk.hy +++ b/tests/native_tests/contrib/walk.hy @@ -32,4 +32,4 @@ (defn test-macroexpand-all [] (assert (= (macroexpand-all '(with [a b c] (for [d c] foo))) - '(with* [a] (with* [b] (with* [c] (do (for* [d c] foo)))))))) + '(with* [a] (with* [b] (with* [c] (do (for* [d c] (do foo))))))))) diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index ed6df0e..085e503 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -86,10 +86,12 @@ (defn test-for-loop [] "NATIVE: test for loops" - (setv count 0) + (setv count1 0 count2 0) (for [x [1 2 3 4 5]] - (setv count (+ count x))) - (assert (= count 15)) + (setv count1 (+ count1 x)) + (setv count2 (+ count2 x))) + (assert (= count1 15)) + (assert (= count2 15)) (setv count 0) (for [x [1 2 3 4 5] y [1 2 3 4 5]] @@ -224,7 +226,7 @@ "NATIVE: test if cond sorta works." (cond [(= 1 2) (assert (is true false))] - [(is null null) (assert (is true true))])) + [(is null null) (setv x true) (assert x)])) (defn test-index [] From 62e31165292a276c0b5c45bb9a0f421003b0ca89 Mon Sep 17 00:00:00 2001 From: Ryan Gonzalez Date: Thu, 30 Jul 2015 17:32:14 -0500 Subject: [PATCH 10/53] Fix 'with' docstring --- hy/core/macros.hy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hy/core/macros.hy b/hy/core/macros.hy index ff8f1fd..517b7cb 100644 --- a/hy/core/macros.hy +++ b/hy/core/macros.hy @@ -32,7 +32,7 @@ (defmacro with [args &rest body] - "shorthand for nested for* loops: + "shorthand for nested with* loops: (with [[x foo] [y bar]] baz) -> (with* [x foo] (with* [y bar] From a0cb250f24d5cf59cb26fff42ba6100f355fdf19 Mon Sep 17 00:00:00 2001 From: Ryan Gonzalez Date: Mon, 3 Aug 2015 17:52:00 -0500 Subject: [PATCH 11/53] Fix multi-statement 'for' with 'else' and add test cases for 'else' --- hy/core/macros.hy | 11 ++++++++--- tests/native_tests/language.hy | 6 ++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/hy/core/macros.hy b/hy/core/macros.hy index 517b7cb..6a8709f 100644 --- a/hy/core/macros.hy +++ b/hy/core/macros.hy @@ -97,16 +97,21 @@ (for* [x foo] (for* [y bar] baz))" + (setv body (list body)) + (setv lst (get body -1)) + (setv belse (if (and (isinstance lst HyExpression) (= (get lst 0) "else")) + [(body.pop)] + [])) (cond [(odd? (len args)) (macro-error args "`for' requires an even number of args.")] [(empty? body) (macro-error None "`for' requires a body to evaluate")] - [(empty? args) `(do ~@body)] - [(= (len args) 2) `(for* [~@args] (do ~@body))] + [(empty? args) `(do ~@body ~@belse)] + [(= (len args) 2) `(for* [~@args] (do ~@body) ~@belse)] [true (let [[alist (cut args 0 nil 2)]] - `(for* [(, ~@alist) (genexpr (, ~@alist) [~@args])] ~@body))])) + `(for* [(, ~@alist) (genexpr (, ~@alist) [~@args])] (do ~@body) ~@belse))])) (defmacro -> [head &rest rest] diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index 085e503..d468533 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -95,8 +95,10 @@ (setv count 0) (for [x [1 2 3 4 5] y [1 2 3 4 5]] - (setv count (+ count x y))) - (assert (= count 150)) + (setv count (+ count x y)) + (else + (+= count 1))) + (assert (= count 151)) (assert (= (list ((fn [] (for [x [[1] [2 3]] y x] (yield y))))) (list-comp y [x [[1] [2 3]] y x]))) (assert (= (list ((fn [] (for [x [[1] [2 3]] y x z (range 5)] (yield z))))) From 439fa6eb178515965dac5240b6ced65a8cbb698a Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Mon, 10 Aug 2015 10:11:24 +0200 Subject: [PATCH 12/53] core.macros: Fix a for corner case In case for doesn't get a body, raise the appropriate, descriptive error instead of an IndexOutOfBounds one. Also updated the failing test case. Signed-off-by: Gergely Nagy --- hy/core/macros.hy | 2 ++ tests/compilers/test_ast.py | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/hy/core/macros.hy b/hy/core/macros.hy index 6a8709f..fc296a8 100644 --- a/hy/core/macros.hy +++ b/hy/core/macros.hy @@ -98,6 +98,8 @@ (for* [y bar] baz))" (setv body (list body)) + (if (empty? body) + (macro-error None "`for' requires a body to evaluate")) (setv lst (get body -1)) (setv belse (if (and (isinstance lst HyExpression) (= (get lst 0) "else")) [(body.pop)] diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index 0fe320e..4491f83 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -484,7 +484,7 @@ def test_for_compile_error(): assert(False) try: - can_compile("(fn [] (for [x]))") + can_compile("(fn [] (for [x] x))") except HyTypeError as e: assert(e.message == "`for' requires an even number of args.") else: @@ -497,6 +497,13 @@ def test_for_compile_error(): else: assert(False) + try: + can_compile("(fn [] (for [x xx] (else 1)))") + except HyTypeError as e: + assert(e.message == "`for' requires a body to evaluate") + else: + assert(False) + def test_attribute_access(): """Ensure attribute access compiles correctly""" From 2665b316cfbd47aa67dc442f89330767c4da6331 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Mon, 10 Aug 2015 10:31:45 +0200 Subject: [PATCH 13/53] .gitignore: Ignore .noseids too Signed-off-by: Gergely Nagy --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index e46a222..d3a4b8c 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ dist .coverage build/ +.noseids From 1327d5888227ce0bb8a691454d9e482ab8e1bfe1 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Mon, 10 Aug 2015 13:44:11 +0200 Subject: [PATCH 14/53] Add a few tests for various defn corner cases Closes #302. Signed-off-by: Gergely Nagy --- tests/compilers/test_ast.py | 8 ++++++++ tests/native_tests/language.hy | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index 4491f83..3b23b61 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -519,3 +519,11 @@ def test_attribute_access(): def test_cons_correct(): """Ensure cons gets compiled correctly""" can_compile("(cons a b)") + + +def test_defn(): + """Ensure that defn works correctly in various corner cases""" + cant_compile("(defn if [] 1)") + cant_compile("(defn \"hy\" [] 1)") + cant_compile("(defn :hy [] 1)") + can_compile("(defn &hy [] 1)") diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index d468533..ec771e6 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -673,6 +673,12 @@ (assert (= 43 (my-fun 42)))) +(defn test-defn-lambdakey [] + "NATIVE: test defn with a &symbol function name" + (defn &hy [] 1) + (assert (= (&hy) 1))) + + (defn test-defn-do [] "NATIVE: test defn evaluation order with do" (setv acc []) From d3520e56407d037de0f2725c793c7a24dd209a2b Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Mon, 10 Aug 2015 14:19:19 +0200 Subject: [PATCH 15/53] Trying to setv a callable should raise a nice error When trying to setv a callable, raise an error instead of showing the user an incredibly ugly backtrace. Closes #532. Signed-off-by: Gergely Nagy --- hy/compiler.py | 4 ++++ tests/compilers/test_ast.py | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/hy/compiler.py b/hy/compiler.py index 8e9893c..fac4cfa 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -2004,6 +2004,10 @@ class HyASTCompiler(object): result = self.compile(result) ld_name = self.compile(name) + if isinstance(ld_name.expr, ast.Call): + raise HyTypeError(name, + "Can't assign to a callable: `%s'" % str_name) + if result.temp_variables \ and isinstance(name, HyString) \ and '.' not in name: diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index 4491f83..c68c609 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -519,3 +519,9 @@ def test_attribute_access(): def test_cons_correct(): """Ensure cons gets compiled correctly""" can_compile("(cons a b)") + + +def test_bad_setv(): + """Ensure setv handles error cases""" + cant_compile("(setv if 1)") + cant_compile("(setv (a b) [1 2])") From 73f8a47f659680dc3c77f6819909a09f9e4aec73 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Mon, 10 Aug 2015 14:37:17 +0200 Subject: [PATCH 16/53] Mangle trailing bangs on symbols Postfixing functions with a bang, like set!, get!, etc are relatively common. However, those names are not valid in python, so in the name of python interoperability, lets mangle them to set_bang and get_bang, respectively. Closes #536. Signed-off-by: Gergely Nagy --- hy/lex/parser.py | 3 +++ tests/lex/test_lex.py | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/hy/lex/parser.py b/hy/lex/parser.py index a63be3e..22aaf26 100644 --- a/hy/lex/parser.py +++ b/hy/lex/parser.py @@ -308,6 +308,9 @@ def t_identifier(p): if p.endswith("?") and p != "?": p = "is_%s" % (p[:-1]) + if p.endswith("!") and p != "!": + p = "%s_bang" % (p[:-1]) + return p obj = ".".join([mangle(part) for part in obj.split(".")]) diff --git a/tests/lex/test_lex.py b/tests/lex/test_lex.py index cc95675..56ed52a 100644 --- a/tests/lex/test_lex.py +++ b/tests/lex/test_lex.py @@ -326,6 +326,24 @@ def test_lex_mangling_qmark(): assert entry == [HySymbol(".is_foo.bar.is_baz")] +def test_lex_mangling_bang(): + """Ensure that identifiers ending with a bang get mangled ok""" + entry = tokenize("foo!") + assert entry == [HySymbol("foo_bang")] + entry = tokenize("!") + assert entry == [HySymbol("!")] + entry = tokenize("im!foo") + assert entry == [HySymbol("im!foo")] + entry = tokenize(".foo!") + assert entry == [HySymbol(".foo_bang")] + entry = tokenize("foo.bar!") + assert entry == [HySymbol("foo.bar_bang")] + entry = tokenize("foo!.bar") + assert entry == [HySymbol("foo_bang.bar")] + entry = tokenize(".foo!.bar.baz!") + assert entry == [HySymbol(".foo_bang.bar.baz_bang")] + + def test_simple_cons(): """Check that cons gets tokenized correctly""" entry = tokenize("(a . b)")[0] From fee7d33184dd40f9e754c2253d5ef2376f66e8c2 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Mon, 10 Aug 2015 15:55:11 +0200 Subject: [PATCH 17/53] Guard against a few invalid list comprehension cases Some valid-looking list comprehensions, such as (genexpr x []) can crash Python 2.7. The AST we generate from these cannot be expressed in Python, but were valid in Hy. Added two guards to guard against this, so we raise an error instead of crashing Python. Closes #572, #591 and #634. Signed-off-by: Gergely Nagy --- hy/compiler.py | 7 +++++++ tests/compilers/test_ast.py | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/hy/compiler.py b/hy/compiler.py index 8e9893c..f9aeac4 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -1473,9 +1473,16 @@ class HyASTCompiler(object): # (list-comp expr (target iter) cond?) expr.pop(0) expression = expr.pop(0) + gen_gen = expr[0] + + if not isinstance(gen_gen, HyList): + raise HyTypeError(gen_gen, "Generator expression must be a list.") gen_res, gen = self._compile_generator_iterables(expr) + if len(gen) == 0: + raise HyTypeError(gen_gen, "Generator expression cannot be empty.") + compiled_expression = self.compile(expression) ret = compiled_expression + gen_res ret += ast.ListComp( diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index 4491f83..69e5e15 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -519,3 +519,11 @@ def test_attribute_access(): def test_cons_correct(): """Ensure cons gets compiled correctly""" can_compile("(cons a b)") + + +def test_invalid_list_comprehension(): + """Ensure that invalid list comprehensions do not break the compiler""" + cant_compile("(genexpr x [])") + cant_compile("(genexpr [x [1 2 3 4]] x)") + cant_compile("(list-comp None [])") + cant_compile("(list-comp [x [1 2 3]] x)") From 4138c660cc040aac1177086e3a236c7585e5c91f Mon Sep 17 00:00:00 2001 From: Ewald Grusk Date: Mon, 15 Jun 2015 22:42:02 +0200 Subject: [PATCH 18/53] Raise more appropriate error on missing kwarg value --- hy/compiler.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hy/compiler.py b/hy/compiler.py index 8e9893c..05a2fd5 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -459,8 +459,9 @@ class HyASTCompiler(object): try: value = next(exprs_iter) except StopIteration: - msg = "Keyword argument {kw} needs a value" - raise HyCompileError(msg.format(kw=str(expr))) + raise HyTypeError(expr, + "Keyword argument {kw} needs " + "a value.".format(kw=str(expr))) compiled_value = self.compile(value) ret += compiled_value From e407b0a605f42c7d266d9334dcc59e3558313d16 Mon Sep 17 00:00:00 2001 From: Ewald Grusk Date: Mon, 22 Jun 2015 20:18:04 +0200 Subject: [PATCH 19/53] New test: Ensure the compiler chokes on missing keyword argument values --- tests/compilers/test_ast.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index 4491f83..633c3bb 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -439,6 +439,16 @@ def test_lambda_list_keywords_mixed(): " (list x xs kwxs kwoxs))") +def test_missing_keyword_argument_value(): + """Ensure the compiler chokes on missing keyword argument values.""" + try: + can_compile("((fn [x] x) :x)") + except HyTypeError as e: + assert(e.message == "Keyword argument \ufdd0:x needs a value.") + else: + assert(False) + + def test_ast_unicode_strings(): """Ensure we handle unicode strings correctly""" From b36294336572d6c4238613bf5be3288192d40fd3 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Tue, 11 Aug 2015 10:43:00 +0200 Subject: [PATCH 20/53] compiler: Fix the kw argument needs value exception Strip the \ufdd0 prefix from the keyword argument before turning it into a string: the same representation the user entered looks better, and is printable too, thus Python2 doesn't choke on it. Signed-off-by: Gergely Nagy --- hy/compiler.py | 2 +- tests/compilers/test_ast.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hy/compiler.py b/hy/compiler.py index 05a2fd5..41119f1 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -461,7 +461,7 @@ class HyASTCompiler(object): except StopIteration: raise HyTypeError(expr, "Keyword argument {kw} needs " - "a value.".format(kw=str(expr))) + "a value.".format(kw=str(expr[1:]))) compiled_value = self.compile(value) ret += compiled_value diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index 633c3bb..dd6ae59 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -444,7 +444,7 @@ def test_missing_keyword_argument_value(): try: can_compile("((fn [x] x) :x)") except HyTypeError as e: - assert(e.message == "Keyword argument \ufdd0:x needs a value.") + assert(e.message == "Keyword argument :x needs a value.") else: assert(False) From a22e2ca4cceea247435c59fbb21fa40d01a8fe7f Mon Sep 17 00:00:00 2001 From: gilch Date: Tue, 11 Aug 2015 16:22:13 -0600 Subject: [PATCH 21/53] purged null from Hy we already have `nil`, and `null` was barely ever used. --- eg/debian/parse-rfc822.hy | 2 +- hy/compiler.py | 2 +- hy/lex/parser.py | 1 - tests/compilers/test_ast.py | 4 ++-- tests/native_tests/language.hy | 4 +--- tests/native_tests/unless.hy | 4 ++-- tests/native_tests/when.hy | 4 ++-- 7 files changed, 9 insertions(+), 12 deletions(-) diff --git a/eg/debian/parse-rfc822.hy b/eg/debian/parse-rfc822.hy index fe76870..3ba7de9 100644 --- a/eg/debian/parse-rfc822.hy +++ b/eg/debian/parse-rfc822.hy @@ -16,7 +16,7 @@ (defn parse-rfc822-stream [fd] "Parse an RFC822 stream" (setv bits {}) - (setv key null) + (setv key None) (for [line fd] (if (in ":" line) (do (setv line (.split line ":" 1)) diff --git a/hy/compiler.py b/hy/compiler.py index 41119f1..38eef63 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -81,7 +81,7 @@ def load_stdlib(): # keywords in Python 3.* def _is_hy_builtin(name, module_name): extras = ['True', 'False', 'None', - 'true', 'false', 'nil', 'null'] + 'true', 'false', 'nil'] if name in extras or keyword.iskeyword(name): return True # for non-Hy modules, check for pre-existing name in diff --git a/hy/lex/parser.py b/hy/lex/parser.py index 22aaf26..abce1d7 100644 --- a/hy/lex/parser.py +++ b/hy/lex/parser.py @@ -289,7 +289,6 @@ def t_identifier(p): "true": "True", "false": "False", "nil": "None", - "null": "None", } if obj in table: diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index dd6ae59..47c868a 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -219,8 +219,8 @@ def test_ast_good_defclass(): def test_ast_bad_defclass(): "Make sure AST can't compile invalid defclass" cant_compile("(defclass)") - cant_compile("(defclass a null)") - cant_compile("(defclass a null null)") + cant_compile("(defclass a None)") + cant_compile("(defclass a None None)") def test_ast_good_lambda(): diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index d468533..30130f4 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -54,8 +54,6 @@ (except [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) (try (eval '(setv nil 1)) (except [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) - (try (eval '(setv null 1)) - (except [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) (try (eval '(defn defclass [] (print "hello"))) (except [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) (try (eval '(defn get [] (print "hello"))) @@ -228,7 +226,7 @@ "NATIVE: test if cond sorta works." (cond [(= 1 2) (assert (is true false))] - [(is null null) (setv x true) (assert x)])) + [(is None None) (setv x true) (assert x)])) (defn test-index [] diff --git a/tests/native_tests/unless.hy b/tests/native_tests/unless.hy index 2b957fe..a55e052 100644 --- a/tests/native_tests/unless.hy +++ b/tests/native_tests/unless.hy @@ -3,8 +3,8 @@ (assert (= (unless false 1) 1)) (assert (= (unless false 1 2) 2)) (assert (= (unless false 1 3) 3)) - (assert (= (unless true 2) null)) + (assert (= (unless true 2) None)) (assert (= (unless true 2) nil)) - (assert (= (unless (!= 1 2) 42) null)) + (assert (= (unless (!= 1 2) 42) None)) (assert (= (unless (!= 1 2) 42) nil)) (assert (= (unless (!= 2 2) 42) 42))) diff --git a/tests/native_tests/when.hy b/tests/native_tests/when.hy index c281439..9c21c9f 100644 --- a/tests/native_tests/when.hy +++ b/tests/native_tests/when.hy @@ -3,8 +3,8 @@ (assert (= (when true 1) 1)) (assert (= (when true 1 2) 2)) (assert (= (when true 1 3) 3)) - (assert (= (when false 2) null)) - (assert (= (when (= 1 2) 42) null)) + (assert (= (when false 2) None)) + (assert (= (when (= 1 2) 42) None)) (assert (= (when false 2) nil)) (assert (= (when (= 1 2) 42) nil)) (assert (= (when (= 2 2) 42) 42))) From 0bf1084d8cdfeddeca119dfe2dcb2e2d5e9d6572 Mon Sep 17 00:00:00 2001 From: gilch Date: Tue, 11 Aug 2015 18:11:33 -0600 Subject: [PATCH 22/53] added xi-forms These work like Clojure's `#()` anonymous function literals. --- docs/contrib/anaphoric.rst | 28 ++++++++++++++++++++----- hy/contrib/anaphoric.hy | 24 +++++++++++++++++++++ tests/native_tests/contrib/anaphoric.hy | 22 +++++++++++++++++++ 3 files changed, 69 insertions(+), 5 deletions(-) diff --git a/docs/contrib/anaphoric.rst b/docs/contrib/anaphoric.rst index 750a470..a241cd3 100644 --- a/docs/contrib/anaphoric.rst +++ b/docs/contrib/anaphoric.rst @@ -13,14 +13,11 @@ concise and easy to read. -- Wikipedia (http://en.wikipedia.org/wiki/Anaphoric_macro) -Macros -====== - .. _ap-if: ap-if -------- +===== Usage: ``(ap-if (foo) (print it))`` @@ -31,7 +28,7 @@ true and false branches. .. _ap-each: ap-each -------- +======= Usage: ``(ap-each [1 2 3 4 5] (print it))`` @@ -228,3 +225,24 @@ Returns a function which applies several forms in series from left to right. The => (def op (ap-compose (+ it 1) (* it 3))) => (op 2) 9 + +.. _xi + +xi +== + +Usage ``(xi function body ...)`` + +Returns a function with parameters implicitly determined by the presence in the body of xi parameters. An xi symbol designates the ith parameter (1-based, e.g. x1, x2, x3, etc.), or all remaining parameters for xi itself. This is not a replacement for lambda. The xi forms cannot be nested. + +This is similar to Clojure's anonymous function literals (``#()``). + +.. code-block:: hy + + => ((xi identity [x1 x5 [x2 x3] xi x4]) 1 2 3 4 5 6 7 8) + [1, 5, [2, 3,] (6, 7, 8), 4] + => (def add-10 (xi + 10 x1)) + => (add-10 6) + 16 + + diff --git a/hy/contrib/anaphoric.hy b/hy/contrib/anaphoric.hy index 628ae9d..0584a76 100644 --- a/hy/contrib/anaphoric.hy +++ b/hy/contrib/anaphoric.hy @@ -121,3 +121,27 @@ (defmacro ap-compose [&rest forms] "Returns a function which is the composition of several forms." `(fn [var] (ap-pipe var ~@forms))) + +(defmacro xi [function &rest body] + "Returns a function with parameters implicitly determined by the presence in + the body of xi parameters. An xi symbol designates the ith parameter + (1-based, e.g. x1, x2, x3, etc.), or all remaining parameters for xi itself. + This is not a replacement for lambda. The xi forms cannot be nested. " + (setv flatbody (flatten body)) + `(lambda [;; generate all xi symbols up to the maximum found in body + ~@(genexpr (HySymbol (+ "x" + (str i))) + [i (range 1 + ;; find the maximum xi + (inc (max (genexpr (int (cdr a)) + [a flatbody] + (and (symbol? a) + (.startswith a 'x) + (.isdigit (cdr a)))) + :default 0)))]) + ;; generate the &rest paremeter only if 'xi is present in body + ~@(if (in 'xi flatbody) + '(&rest xi) + '())] + (~function ~@body))) + diff --git a/tests/native_tests/contrib/anaphoric.hy b/tests/native_tests/contrib/anaphoric.hy index 8b37905..20b7848 100644 --- a/tests/native_tests/contrib/anaphoric.hy +++ b/tests/native_tests/contrib/anaphoric.hy @@ -113,3 +113,25 @@ "NATIVE: testing anaphoric compose" (assert-equal ((ap-compose (+ it 1) (* it 3)) 2) 9) (assert-equal ((ap-compose (list (rest it)) (len it)) [4 5 6 7]) 3)) + +(defn test-xi [] + "NATIVE: testing xi forms" + ;; test ordering + (assert-equal ((xi / x1 x2) 2 4) 0.5) + (assert-equal ((xi / x2 x1) 2 4) 2) + (assert-equal ((xi identity (, x5 x4 x3 x2 x1)) 1 2 3 4 5) (, 5 4 3 2 1)) + (assert-equal ((xi identity (, x1 x2 x3 x4 x5)) 1 2 3 4 5) (, 1 2 3 4 5)) + (assert-equal ((xi identity (, x1 x5 x2 x3 x4)) 1 2 3 4 5) (, 1 5 2 3 4)) + ;; test &rest + (assert-equal ((xi sum xi) 1 2 3) 6) + (assert-equal ((xi identity (, x1 xi)) 10 1 2 3) (, 10 (, 1 2 3))) + ;; no parameters + (assert-equal ((xi list)) []) + (assert-equal ((xi identity "Hy!")) "Hy!") + (assert-equal ((xi identity "xi")) "xi") + (assert-equal ((xi + "Hy " "world!")) "Hy world!") + ;; test skipped parameters + (assert-equal ((xi identity [x3 x1]) 1 2 3) [3 1]) + ;; test nesting + (assert-equal ((xi identity [x1 (, x2 [x3] "Hy" [xi])]) 1 2 3 4 5) + [1 (, 2 [3] "Hy" [(, 4 5)])])) From 20d6349679462da99c3642f029910f4d1144c039 Mon Sep 17 00:00:00 2001 From: gilch Date: Tue, 11 Aug 2015 19:35:22 -0600 Subject: [PATCH 23/53] make xi compatible with Python2 --- hy/contrib/anaphoric.hy | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hy/contrib/anaphoric.hy b/hy/contrib/anaphoric.hy index 0584a76..df80e68 100644 --- a/hy/contrib/anaphoric.hy +++ b/hy/contrib/anaphoric.hy @@ -133,12 +133,12 @@ (str i))) [i (range 1 ;; find the maximum xi - (inc (max (genexpr (int (cdr a)) - [a flatbody] - (and (symbol? a) - (.startswith a 'x) - (.isdigit (cdr a)))) - :default 0)))]) + (inc (max (+ (list-comp (int (cdr a)) + [a flatbody] + (and (symbol? a) + (.startswith a 'x) + (.isdigit (cdr a)))) + [0]))))]) ;; generate the &rest paremeter only if 'xi is present in body ~@(if (in 'xi flatbody) '(&rest xi) From 9bff606ee9ea72a05d2370b0d32f40d35f946b39 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Tue, 11 Aug 2015 13:55:00 +0200 Subject: [PATCH 24/53] Test that assigning to a builtin fails Signed-off-by: Gergely Nagy --- tests/compilers/test_ast.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index 54dc8d4..0a6c36e 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -551,3 +551,9 @@ def test_defn(): cant_compile("(defn \"hy\" [] 1)") cant_compile("(defn :hy [] 1)") can_compile("(defn &hy [] 1)") + + +def test_setv_builtins(): + """Ensure that assigning to a builtin fails""" + cant_compile("(setv nil 42)") + cant_compile("(defn get [&rest args] 42)") From b92049d11982bbd82784c959cb0e462bd9451cb6 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Tue, 11 Aug 2015 14:03:09 +0200 Subject: [PATCH 25/53] Allow defclass to have properties/method with built-in names To allow classes to have methods that match built-in names, yet, still disallow them outside of defclass, keep an internal state whether builtins are allowed in the current context. By default, this is false. But defclass will set it to True when it compiles its body, and set it back to the previous value when it's done with that. We need to set back to the previous value to allow nested defclasses to work properly. This closes #783. Signed-off-by: Gergely Nagy --- hy/compiler.py | 8 +++++++- tests/compilers/test_ast.py | 10 +++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/hy/compiler.py b/hy/compiler.py index d71c5df..3be5f21 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -370,6 +370,7 @@ def checkargs(exact=None, min=None, max=None, even=None, multiple=None): class HyASTCompiler(object): def __init__(self, module_name): + self.allow_builtins = False self.anon_fn_count = 0 self.anon_var_count = 0 self.imports = defaultdict(set) @@ -2005,7 +2006,8 @@ class HyASTCompiler(object): start_line, start_column): str_name = "%s" % name - if _is_hy_builtin(str_name, self.module_name): + if _is_hy_builtin(str_name, self.module_name) and \ + not self.allow_builtins: raise HyTypeError(name, "Can't assign to a builtin: `%s'" % str_name) @@ -2275,6 +2277,8 @@ class HyASTCompiler(object): docstring.start_column) body += body.expr_as_stmt() + allow_builtins = self.allow_builtins + self.allow_builtins = True if expressions and isinstance(expressions[0], HyList) \ and not isinstance(expressions[0], HyExpression): expr = expressions.pop(0) @@ -2287,6 +2291,8 @@ class HyASTCompiler(object): for expression in expressions: body += self.compile(macroexpand(expression, self.module_name)) + self.allow_builtins = allow_builtins + if not body.stmts: body += ast.Pass(lineno=expressions.start_line, col_offset=expressions.start_column) diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index 0a6c36e..8660a11 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -554,6 +554,14 @@ def test_defn(): def test_setv_builtins(): - """Ensure that assigning to a builtin fails""" + """Ensure that assigning to a builtin fails, unless in a class""" cant_compile("(setv nil 42)") cant_compile("(defn get [&rest args] 42)") + can_compile("(defclass A [] (defn get [self] 42))") + can_compile(""" + (defclass A [] + (defn get [self] 42) + (defclass B [] + (defn get [self] 42)) + (defn if [self] 0)) + """) From a06c8a9af9f6164e7667a7e6d79dd91b2fb6ed8a Mon Sep 17 00:00:00 2001 From: gilch Date: Wed, 12 Aug 2015 08:45:43 -0600 Subject: [PATCH 26/53] xi parameter may appear in function position --- docs/contrib/anaphoric.rst | 2 +- hy/contrib/anaphoric.hy | 4 ++-- tests/native_tests/contrib/anaphoric.hy | 6 +++++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/contrib/anaphoric.rst b/docs/contrib/anaphoric.rst index a241cd3..db36ffc 100644 --- a/docs/contrib/anaphoric.rst +++ b/docs/contrib/anaphoric.rst @@ -231,7 +231,7 @@ Returns a function which applies several forms in series from left to right. The xi == -Usage ``(xi function body ...)`` +Usage ``(xi body ...)`` Returns a function with parameters implicitly determined by the presence in the body of xi parameters. An xi symbol designates the ith parameter (1-based, e.g. x1, x2, x3, etc.), or all remaining parameters for xi itself. This is not a replacement for lambda. The xi forms cannot be nested. diff --git a/hy/contrib/anaphoric.hy b/hy/contrib/anaphoric.hy index df80e68..dc6bb24 100644 --- a/hy/contrib/anaphoric.hy +++ b/hy/contrib/anaphoric.hy @@ -122,7 +122,7 @@ "Returns a function which is the composition of several forms." `(fn [var] (ap-pipe var ~@forms))) -(defmacro xi [function &rest body] +(defmacro xi [&rest body] "Returns a function with parameters implicitly determined by the presence in the body of xi parameters. An xi symbol designates the ith parameter (1-based, e.g. x1, x2, x3, etc.), or all remaining parameters for xi itself. @@ -143,5 +143,5 @@ ~@(if (in 'xi flatbody) '(&rest xi) '())] - (~function ~@body))) + (~@body))) diff --git a/tests/native_tests/contrib/anaphoric.hy b/tests/native_tests/contrib/anaphoric.hy index 20b7848..175231d 100644 --- a/tests/native_tests/contrib/anaphoric.hy +++ b/tests/native_tests/contrib/anaphoric.hy @@ -134,4 +134,8 @@ (assert-equal ((xi identity [x3 x1]) 1 2 3) [3 1]) ;; test nesting (assert-equal ((xi identity [x1 (, x2 [x3] "Hy" [xi])]) 1 2 3 4 5) - [1 (, 2 [3] "Hy" [(, 4 5)])])) + [1 (, 2 [3] "Hy" [(, 4 5)])]) + ;; test arg as function + (assert-equal ((xi x1 2 4) +) 6) + (assert-equal ((xi x1 2 4) -) -2) + (assert-equal ((xi x1 2 4) /) 0.5)) From 44416fc27662676849efd2eaa6b168bf3e2439fd Mon Sep 17 00:00:00 2001 From: Gregor Best Date: Wed, 12 Aug 2015 19:00:03 +0200 Subject: [PATCH 27/53] Typecheck `eval` arguments during execution instead of compilation When checking types during compilation, things like (eval '(print 1) (. foo mod)) fail, even if `(. foo mod)` is a dictionary. --- hy/compiler.py | 4 ---- hy/importer.py | 12 ++++++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/hy/compiler.py b/hy/compiler.py index d71c5df..33a861b 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -729,15 +729,11 @@ class HyASTCompiler(object): elist = [HySymbol("hy_eval")] + [expr[0]] if len(expr) >= 2: - if not isinstance(expr[1], (HyDict, HySymbol)): - raise HyTypeError(expr, "Globals must be a dictionary") elist.append(expr[1]) else: elist.append(HyExpression([HySymbol("locals")])) if len(expr) == 3: - if not isinstance(expr[2], HyString): - raise HyTypeError(expr, "Module name must be a string") elist.append(expr[2]) else: elist.append(HyString(self.module_name)) diff --git a/hy/importer.py b/hy/importer.py index 7c8cb2f..bfe33e9 100644 --- a/hy/importer.py +++ b/hy/importer.py @@ -108,6 +108,15 @@ def hy_eval(hytree, namespace, module_name): foo.start_column = 0 foo.end_column = 0 replace_hy_obj(hytree, foo) + + try: + checktype = basestring + except NameError: + checktype = str + + if not isinstance(module_name, checktype): + raise HyTypeError(foo, "Module name must be a string") + _ast, expr = hy_compile(hytree, module_name, get_expr=True) # Spoof the positions in the generated ast... @@ -119,6 +128,9 @@ def hy_eval(hytree, namespace, module_name): node.lineno = 1 node.col_offset = 1 + if not isinstance(namespace, dict): + raise HyTypeError(foo, "Globals must be a dictionary") + # Two-step eval: eval() the body of the exec call eval(ast_compile(_ast, "", "exec"), namespace) From 503b4e1f8112cb8faec32e11c899487608f2a900 Mon Sep 17 00:00:00 2001 From: Gregor Best Date: Wed, 12 Aug 2015 22:00:22 +0200 Subject: [PATCH 28/53] Tidier checks for eval failure modes --- tests/native_tests/language.hy | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index c6ae19b..097faf5 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -875,18 +875,6 @@ (assert (= None (eval (quote (print "")))))) -(defmacro assert-raise [exc-type &rest body] - `(try - (do - (eval ~@body) - (assert False "we shouldn't have arrived here")) - (except [e Exception] - (assert (instance? ~exc-type e) - (.format "Expected exception of type {}, got {}: {}" - (. ~exc-type --name--) - (. (type e) --name--) - (str e)))))) - (defn test-eval-globals [] "NATIVE: test eval with explicit global dict" (assert (= 'bar (eval (quote foo) {'foo 'bar}))) @@ -905,10 +893,11 @@ (defn test-eval-failure [] "NATIVE: test eval failure modes" (import [hy.errors [HyTypeError]]) - (assert-raise HyTypeError '(eval)) - (assert-raise HyTypeError '(eval "snafu")) - (assert-raise HyTypeError '(eval 'false [])) - (assert-raise HyTypeError '(eval 'false {} 1))) + ; yo dawg + (try (eval '(eval)) (except [e HyTypeError]) (else (assert False))) + (try (eval '(eval "snafu")) (except [e HyTypeError]) (else (assert False))) + (try (eval 'false []) (except [e HyTypeError]) (else (assert False))) + (try (eval 'false {} 1) (except [e HyTypeError]) (else (assert False)))) (defn test-import-syntax [] From 856212c9d79f80bd934a388a78340aa99a8cd19e Mon Sep 17 00:00:00 2001 From: Gregor Best Date: Thu, 13 Aug 2015 09:59:58 +0200 Subject: [PATCH 29/53] Tidier string type check for eval --- hy/importer.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/hy/importer.py b/hy/importer.py index bfe33e9..8fcbd27 100644 --- a/hy/importer.py +++ b/hy/importer.py @@ -33,6 +33,7 @@ import os import __future__ from hy._compat import PY3, PY33, MAGIC, builtins, long_type, wr_long +from hy._compat import string_types def ast_compile(ast, filename, mode): @@ -109,12 +110,7 @@ def hy_eval(hytree, namespace, module_name): foo.end_column = 0 replace_hy_obj(hytree, foo) - try: - checktype = basestring - except NameError: - checktype = str - - if not isinstance(module_name, checktype): + if not isinstance(module_name, string_types): raise HyTypeError(foo, "Module name must be a string") _ast, expr = hy_compile(hytree, module_name, get_expr=True) From 7e611947a44d04a29fb75dcb49de87f959bdfd71 Mon Sep 17 00:00:00 2001 From: Ryan Gonzalez Date: Mon, 17 Aug 2015 17:29:06 -0500 Subject: [PATCH 30/53] Allow setv to take no arguments (closes #901) --- hy/compiler.py | 11 ++++++++--- tests/native_tests/language.hy | 5 +++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/hy/compiler.py b/hy/compiler.py index d71c5df..3ebe941 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -1977,10 +1977,15 @@ class HyASTCompiler(object): @builds("def") @builds("setv") - @checkargs(min=2) def compile_def_expression(self, expression): - expression.pop(0) - if len(expression) == 2: + root = expression.pop(0) + if not expression: + result = Result() + result += ast.Name(id='None', ctx=ast.Load(), + lineno=root.start_line, + col_offset=root.start_column) + return result + elif len(expression) == 2: return self._compile_assign(expression[0], expression[1], expression.start_line, expression.start_column) diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index c6ae19b..42af302 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -34,6 +34,11 @@ (assert (= #{} (set)))) +(defn test-setv-empty [] + "NATIVE: test setv works with no arguments" + (assert (is (setv) nil))) + + (defn test-setv-get [] "NATIVE: test setv works on a get expression" (setv foo [0 1 2]) From e0f5d54a2f764edf99a967eb3866cd468a9e95f3 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Tue, 18 Aug 2015 08:51:09 +0200 Subject: [PATCH 31/53] Allow (del) without arguments To mirror the behaviour of (setv), allow an empty (del) too: one that shall return nil. Closes #905. Signed-off-by: Gergely Nagy --- hy/compiler.py | 10 ++++++++-- tests/native_tests/language.hy | 3 ++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/hy/compiler.py b/hy/compiler.py index 3ebe941..bba9624 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -1298,9 +1298,15 @@ class HyASTCompiler(object): return ret @builds("del") - @checkargs(min=1) def compile_del_expression(self, expr): - expr.pop(0) + root = expr.pop(0) + if not expr: + result = Result() + result += ast.Name(id='None', ctx=ast.Load(), + lineno=root.start_line, + col_offset=root.start_column) + return result + ld_targets, ret, _ = self._compile_collect(expr) del_targets = [] diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index 42af302..d9a6dc6 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -1092,7 +1092,8 @@ (del (get test 4)) (assert (= test [0 1 2 3])) (del (get test 2)) - (assert (= test [0 1 3]))) + (assert (= test [0 1 3])) + (assert (= (del) nil))) (defn test-macroexpand [] From ab7d1be001965d83ab155a96a30ee821d8d442bd Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Tue, 18 Aug 2015 08:57:42 +0200 Subject: [PATCH 32/53] Allow an empty (cond) For easier macro writing purposes, allow an empty (cond), that simply returns nil. Closes #904. Signed-off-by: Gergely Nagy --- hy/core/macros.hy | 39 ++++++++++++++++++---------------- tests/native_tests/language.hy | 3 ++- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/hy/core/macros.hy b/hy/core/macros.hy index fc296a8..95706af 100644 --- a/hy/core/macros.hy +++ b/hy/core/macros.hy @@ -67,26 +67,29 @@ bar (if baz quux))" - (setv branches (iter branches)) - (setv branch (next branches)) - (defn check-branch [branch] - "check `cond` branch for validity, return the corresponding `if` expr" - (if (not (= (type branch) HyList)) - (macro-error branch "cond branches need to be a list")) - (if (< (len branch) 2) - (macro-error branch "cond branches need at least two items: a test and one or more code branches")) - (setv test (car branch)) - (setv thebranch (cdr branch)) - `(if ~test (do ~@thebranch))) + (if (empty? branches) + nil + (do + (setv branches (iter branches)) + (setv branch (next branches)) + (defn check-branch [branch] + "check `cond` branch for validity, return the corresponding `if` expr" + (if (not (= (type branch) HyList)) + (macro-error branch "cond branches need to be a list")) + (if (< (len branch) 2) + (macro-error branch "cond branches need at least two items: a test and one or more code branches")) + (setv test (car branch)) + (setv thebranch (cdr branch)) + `(if ~test (do ~@thebranch))) - (setv root (check-branch branch)) - (setv latest-branch root) + (setv root (check-branch branch)) + (setv latest-branch root) - (for* [branch branches] - (setv cur-branch (check-branch branch)) - (.append latest-branch cur-branch) - (setv latest-branch cur-branch)) - root) + (for* [branch branches] + (setv cur-branch (check-branch branch)) + (.append latest-branch cur-branch) + (setv latest-branch cur-branch)) + root))) (defmacro for [args &rest body] diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index 42af302..e892941 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -231,7 +231,8 @@ "NATIVE: test if cond sorta works." (cond [(= 1 2) (assert (is true false))] - [(is None None) (setv x true) (assert x)])) + [(is None None) (setv x true) (assert x)]) + (assert (= (cond) nil))) (defn test-index [] From 73ace376787a0b174aeeab177b8709d3844b1208 Mon Sep 17 00:00:00 2001 From: Ryan Gonzalez Date: Sat, 22 Aug 2015 15:13:46 -0500 Subject: [PATCH 33/53] Fix #831 Blame dot not matching newlines --- hy/lex/lexer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hy/lex/lexer.py b/hy/lex/lexer.py index 1480413..4a770f5 100644 --- a/hy/lex/lexer.py +++ b/hy/lex/lexer.py @@ -50,7 +50,7 @@ partial_string = r'''(?x) " # start string (?: | [^"\\] # non-quote or backslash - | \\. # or escaped single character + | \\(.|\n) # or escaped single character or newline | \\x[0-9a-fA-F]{2} # or escaped raw character | \\u[0-9a-fA-F]{4} # or unicode escape | \\U[0-9a-fA-F]{8} # or long unicode escape From a2f95366fe2ac493e41b07ca4599ed90d30b49de Mon Sep 17 00:00:00 2001 From: Ryan Gonzalez Date: Sat, 22 Aug 2015 15:26:23 -0500 Subject: [PATCH 34/53] Add tests for #831 --- tests/lex/test_lex.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/lex/test_lex.py b/tests/lex/test_lex.py index 56ed52a..4d354d3 100644 --- a/tests/lex/test_lex.py +++ b/tests/lex/test_lex.py @@ -100,6 +100,12 @@ def test_lex_strings(): """ Make sure that strings are valid expressions""" objs = tokenize("\"foo\" ") assert objs == [HyString("foo")] + # Make sure backslash-escaped newlines work (see issue #831) + objs = tokenize(r""" +"a\ +bc" +""") + assert objs == [HyString("abc")] def test_lex_integers(): From f88f6e597b508c92cd0d63b3b10ec6b7cfaf65c8 Mon Sep 17 00:00:00 2001 From: Ryan Gonzalez Date: Sat, 22 Aug 2015 18:37:53 -0500 Subject: [PATCH 35/53] Reformat string literals in test_lex_strings --- tests/lex/test_lex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/lex/test_lex.py b/tests/lex/test_lex.py index 4d354d3..4413a6b 100644 --- a/tests/lex/test_lex.py +++ b/tests/lex/test_lex.py @@ -98,7 +98,7 @@ def test_lex_symbols(): def test_lex_strings(): """ Make sure that strings are valid expressions""" - objs = tokenize("\"foo\" ") + objs = tokenize('"foo"') assert objs == [HyString("foo")] # Make sure backslash-escaped newlines work (see issue #831) objs = tokenize(r""" From f18007955dfa5fe294883159a4b7835bb2aa2252 Mon Sep 17 00:00:00 2001 From: Adam Schwalm Date: Sun, 30 Aug 2015 13:14:16 -0500 Subject: [PATCH 36/53] Better error messages on invalid macro arguments --- hy/macros.py | 19 +++++++++++++++++++ tests/macros/test_macro_processor.py | 11 +++++++++++ 2 files changed, 30 insertions(+) diff --git a/hy/macros.py b/hy/macros.py index c4a935d..d5821b9 100644 --- a/hy/macros.py +++ b/hy/macros.py @@ -18,6 +18,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. +from inspect import getargspec, formatargspec from hy.models import replace_hy_obj, wrap_value from hy.models.expression import HyExpression from hy.models.string import HyString @@ -122,6 +123,16 @@ def load_macros(module_name): _import(module) +def make_emtpy_fn_copy(fn): + argspec = getargspec(fn) + formatted_args = formatargspec(*argspec) + fn_str = 'lambda {}: None'.format( + formatted_args.lstrip('(').rstrip(')')) + + empty_fn = eval(fn_str) + return empty_fn + + def macroexpand(tree, module_name): """Expand the toplevel macros for the `tree`. @@ -155,6 +166,14 @@ def macroexpand_1(tree, module_name): if m is None: m = _hy_macros[None].get(fn) if m is not None: + try: + m_copy = make_emtpy_fn_copy(m) + m_copy(*ntree[1:]) + except TypeError as e: + msg = "expanding `" + str(tree[0]) + "': " + msg += str(e).replace("()", "", 1).strip() + raise HyMacroExpansionError(tree, msg) + try: obj = wrap_value(m(*ntree[1:])) diff --git a/tests/macros/test_macro_processor.py b/tests/macros/test_macro_processor.py index 373b967..03ff61b 100644 --- a/tests/macros/test_macro_processor.py +++ b/tests/macros/test_macro_processor.py @@ -6,6 +6,7 @@ from hy.models.string import HyString from hy.models.list import HyList from hy.models.symbol import HySymbol from hy.models.expression import HyExpression +from hy.errors import HyMacroExpansionError @macro("test") @@ -35,3 +36,13 @@ def test_preprocessor_expression(): obj = HyList([HyString("one"), HyString("two")]) obj = tokenize('(shill ["one" "two"])')[0][1] assert obj == macroexpand(obj, '') + + +def test_preprocessor_exceptions(): + """ Test that macro expansion raises appropriate exceptions""" + try: + macroexpand(tokenize('(defn)')[0], __name__) + assert False + except HyMacroExpansionError as e: + assert "_hy_anon_fn_" not in str(e) + assert "TypeError" not in str(e) From 818ed5da720235f41bb1a2b8eb51b6218ca526e2 Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Mon, 31 Aug 2015 20:35:37 +0100 Subject: [PATCH 37/53] Added documentation for the (keyword?) function --- docs/language/core.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/language/core.rst b/docs/language/core.rst index f8522da..b1e4594 100644 --- a/docs/language/core.rst +++ b/docs/language/core.rst @@ -436,6 +436,25 @@ themselves as an iterator when ``(iter x)`` is called. Contrast with => (iterator? (iter {:a 1 :b 2 :c 3})) True +.. _keyword?-fn: + +keyword? +-------- + +.. versionadded:: 0.10.1 + +Usage: ``(keyword? foo)`` + +Check whether *foo* is a :ref:`keyword`. + +.. code-block:: hy + + => (keyword? :foo) + True + + => (setv foo 1) + => (keyword? foo) + False .. _list*-fn: From 48969803736913c56d1d77b0cf7daf92cd87d891 Mon Sep 17 00:00:00 2001 From: gilch Date: Wed, 2 Sep 2015 22:54:24 -0600 Subject: [PATCH 38/53] enhance partition --- docs/language/core.rst | 26 +++++++++++++++++++++----- hy/core/language.hy | 14 +++++++++++--- tests/native_tests/core.hy | 23 ++++++++++++++++++----- 3 files changed, 50 insertions(+), 13 deletions(-) diff --git a/docs/language/core.rst b/docs/language/core.rst index f8522da..0f472ea 100644 --- a/docs/language/core.rst +++ b/docs/language/core.rst @@ -671,21 +671,37 @@ Returns ``True`` if *x* is odd. Raises ``TypeError`` if => (odd? 0) False - .. _partition-fn: partition --------- -Usage: ``(partition n coll)`` +Usage: ``(partition coll [n] [step] [fillvalue])`` -Chunks coll into tuples of length *n*. The remainder, if any, is not included. +Chunks *coll* into *n*-tuples (pairs by default). .. code-block:: hy - => (list (partition 3 (range 10))) - [(0, 1, 2), (3, 4, 5), (6, 7, 8)] + => (list (partition (range 10))) ; n=2 + [(, 0 1) (, 2 3) (, 4 5) (, 6 7) (, 8 9)] +The *step* defaults to *n*, but can be more to skip elements, or less for a sliding window with overlap. + +.. code-block:: hy + + => (list (partition (range 10) 2 3)) + [(, 0 1) (, 3 4) (, 6 7)] + => (list (partition (range 5) 2 1)) + [(, 0 1) (, 1 2) (, 2 3) (, 3 4)]) + +The remainder, if any, is not included unless a *fillvalue* is specified. + +.. code-block:: hy + + => (list (partition (range 10) 3)) + [(, 0 1 2) (, 3 4 5) (, 6 7 8)] + => (list (partition (range 10) 3 :fillvalue "x")) + [(, 0 1 2) (, 3 4 5) (, 6 7 8) (, 9 "x" "x")] .. _pos?-fn: diff --git a/hy/core/language.hy b/hy/core/language.hy index a434331..7e0868d 100644 --- a/hy/core/language.hy +++ b/hy/core/language.hy @@ -314,9 +314,17 @@ (_numeric-check n) (= (% n 2) 1)) -(defn partition [n coll] - "Chunks coll into tuples of length n. The remainder, if any, is not included." - (apply zip (* n (, (iter coll))))) +(def -sentinel (object)) +(defn partition [coll &optional [n 2] step [fillvalue -sentinel]] + "Chunks coll into n-tuples (pairs by default). The remainder, if any, is not + included unless a fillvalue is specified. The step defaults to n, but can be + more to skip elements, or less for a sliding window with overlap." + (setv + step (or step n) + slices (genexpr (itertools.islice coll start nil step) [start (range n)])) + (if (is fillvalue -sentinel) + (apply zip slices) + (apply zip-longest slices {"fillvalue" fillvalue}))) (defn pos? [n] "Return true if n is > 0" diff --git a/tests/native_tests/core.hy b/tests/native_tests/core.hy index 320faef..254b563 100644 --- a/tests/native_tests/core.hy +++ b/tests/native_tests/core.hy @@ -473,14 +473,27 @@ (defn test-partition [] "NATIVE: testing the partition function" (setv ten (range 10)) - (assert-equal (list (partition 3 ten)) + ;; no remainder + (assert-equal (list (partition ten 3)) [(, 0 1 2) (, 3 4 5) (, 6 7 8)]) - (assert-equal (list (partition 2 ten)) + ;; pair by default + (assert-equal (list (partition ten)) [(, 0 1) (, 2 3) (, 4 5) (, 6 7) (, 8 9)]) - (assert-equal (list (partition 1 ten)) + ;; length 1 is valid + (assert-equal (list (partition ten 1)) [(, 0) (, 1) (, 2) (, 3) (, 4) (, 5) (, 6) (, 7) (, 8) (, 9)]) - (assert-equal (list (partition 0 ten)) []) - (assert-equal (list (partition -1 ten)) [])) + ;; tuples of length < 1 don't crash + (assert-equal (list (partition ten 0)) []) + (assert-equal (list (partition ten -1)) []) + ;; keep remainder with a fillvalue + (assert-equal (list (partition ten 3 :fillvalue "x")) + [(, 0 1 2) (, 3 4 5) (, 6 7 8) (, 9 "x" "x")]) + ;; skip elements with step > n + (assert-equal (list (partition ten 2 3)) + [(, 0 1) (, 3 4) (, 6 7)]) + ;; overlap with step < n + (assert-equal (list (partition (range 5) 2 1)) + [(, 0 1) (, 1 2) (, 2 3) (, 3 4)])) (defn test-pos [] "NATIVE: testing the pos? function" From 6f93de68d7870595c29901baddd13e8bfb0a08f6 Mon Sep 17 00:00:00 2001 From: gilch Date: Thu, 3 Sep 2015 11:45:55 -0600 Subject: [PATCH 39/53] added remaining itertools Some names have been changed. Removed redundant zipwith. Made `first` and `last` more efficient. --- docs/language/core.rst | 18 ------ hy/core/language.hy | 126 ++++++++++++++++++++++--------------- tests/native_tests/core.hy | 17 ++--- 3 files changed, 86 insertions(+), 75 deletions(-) diff --git a/docs/language/core.rst b/docs/language/core.rst index b1e4594..ebfbf3a 100644 --- a/docs/language/core.rst +++ b/docs/language/core.rst @@ -1237,21 +1237,3 @@ Returns an iterator from *coll* as long as *pred* returns ``True``. => (list (take-while neg? [ 1 2 3 -4 5])) [] -.. _zipwith-fn: - -zipwith -------- - -.. versionadded:: 0.9.13 - -Usage: ``(zipwith fn coll ...)`` - -Equivalent to ``zip``, but uses a multi-argument function instead of creating -a tuple. If ``zipwith`` is called with N collections, then *fn* must accept -N arguments. - -.. code-block:: hy - - => (import operator) - => (list (zipwith operator.add [1 2 3] [4 5 6])) - [5, 7, 9] diff --git a/hy/core/language.hy b/hy/core/language.hy index a434331..04bce24 100644 --- a/hy/core/language.hy +++ b/hy/core/language.hy @@ -27,6 +27,7 @@ (import functools) (import collections) (import [fractions [Fraction :as fraction]]) +(import operator) ; shadow not available yet (import sys) (if-python2 (import [StringIO [StringIO]]) @@ -91,41 +92,74 @@ (.add seen val)))))) (if-python2 - (do - (setv filterfalse itertools.ifilterfalse) - (setv zip_longest itertools.izip_longest) - (setv filter itertools.ifilter) - (setv map itertools.imap) - (setv zip itertools.izip) - (setv range xrange) - (setv input raw_input) - (setv reduce reduce)) - (do - (setv reduce functools.reduce) - (setv filterfalse itertools.filterfalse) - (setv zip_longest itertools.zip_longest) - ; Someone can import these directly from `hy.core.language`; - ; we'll make some duplicates. - (setv filter filter) - (setv map map) - (setv zip zip) - (setv range range) - (setv input input))) + (def + filterfalse itertools.ifilterfalse + zip-longest itertools.izip_longest + ;; not builtin in Python3 + reduce reduce + ;; hy is more like Python3 + filter itertools.ifilter + input raw_input + map itertools.imap + range xrange + zip itertools.izip) + (def + filterfalse itertools.filterfalse + zip-longest itertools.zip_longest + ;; was builtin in Python2 + reduce functools.reduce + ;; Someone can import these directly from `hy.core.language`; + ;; we'll make some duplicates. + filter filter + input input + map map + range range + zip zip)) -(setv cycle itertools.cycle) -(setv repeat itertools.repeat) -(setv drop-while itertools.dropwhile) -(setv take-while itertools.takewhile) -(setv zipwith map) -(setv remove filterfalse) +;; infinite iterators +(def + count itertools.count + cycle itertools.cycle + repeat itertools.repeat) + +;; shortest-terminating iterators +(def + *map itertools.starmap + chain itertools.chain + compress itertools.compress + drop-while itertools.dropwhile + group-by itertools.groupby + islice itertools.islice + remove filterfalse + take-while itertools.takewhile + tee itertools.tee) + +;; combinatoric iterators +(def + combinations itertools.combinations + multicombinations itertools.combinations_with_replacement + permutations itertools.permutations + product itertools.product) + +;; also from itertools, but not in Python2, and without func option until 3.3 +(defn accumulate [iterable &optional [func operator.add]] + "accumulate(iterable[, func]) --> accumulate object + + Return series of accumulated sums (or other binary function results)." + (setv it (iter iterable) + total (next it)) + (yield total) + (for* [element it] + (setv total (func total element)) + (yield total))) (defn drop [count coll] "Drop `count` elements from `coll` and yield back the rest" - (itertools.islice coll count nil)) + (islice coll count nil)) (defn drop-last [n coll] "Return a sequence of all but the last n elements in coll." - (let [[iters (itertools.tee coll)]] + (let [[iters (tee coll)]] (map first (apply zip [(get iters 0) (drop n (get iters 1))])))) @@ -196,7 +230,7 @@ (defn first [coll] "Return first item from `coll`" - (nth coll 0)) + (next (iter coll) nil)) (defn identity [x] "Returns the argument unchanged" @@ -227,11 +261,11 @@ (defn interleave [&rest seqs] "Return an iterable of the first item in each of seqs, then the second etc." - (itertools.chain.from_iterable (apply zip seqs))) + (chain.from-iterable (apply zip seqs))) (defn interpose [item seq] "Return an iterable of the elements of seq separated by item" - (drop 1 (interleave (itertools.repeat item) seq))) + (drop 1 (interleave (repeat item) seq))) (defn iterable? [x] "Return true if x is iterable" @@ -249,7 +283,7 @@ (defn last [coll] "Return last item from `coll`" - (get (list coll) -1)) + (get (tuple coll) -1)) (defn list* [hd &rest tl] "Return a dotted list construed from the elements of the argument" @@ -355,7 +389,7 @@ (defn take [count coll] "Take `count` elements from `coll`, or the whole set if the total number of entries in `coll` is less than `count`." - (itertools.islice coll nil count)) + (islice coll nil count)) (defn take-nth [n coll] "Return every nth member of coll @@ -389,18 +423,10 @@ (else (if parsed (break))))) parsed) - (defn read-str [input] "Reads and tokenizes first line of input" (read :from-file (StringIO input))) - -(defn zipwith [func &rest lists] - "Zip the contents of several lists and map a function to the result" - (do - (import functools) - (map (functools.partial (fn [f args] (apply f args)) func) (apply zip lists)))) - (defn hyify [text] "Convert text to match hy identifier" (.replace (string text) "_" "-")) @@ -428,11 +454,13 @@ (except [] (string value)))))) (def *exports* - '[butlast calling-module-name coll? cons cons? cycle dec distinct disassemble - drop drop-last drop-while empty? even? every? first filter filterfalse - flatten float? fraction gensym identity inc input instance? integer integer? - integer-char? interleave interpose iterable? iterate iterator? keyword - keyword? last list* macroexpand macroexpand-1 map merge-with name neg? nil? - none? nth numeric? odd? partition pos? range read read-str remove repeat - repeatedly rest reduce second some string string? symbol? take take-nth - take-while zero? zip zip_longest zipwith]) + '[*map accumulate butlast calling-module-name chain coll? combinations + compress cons cons? count cycle dec distinct disassemble drop drop-last + drop-while empty? even? every? first filter filterfalse flatten float? + fraction gensym group-by identity inc input instance? integer integer? + integer-char? interleave interpose islice iterable? iterate iterator? + keyword keyword? last list* macroexpand macroexpand-1 map merge-with + multicombinations name neg? nil? none? nth numeric? odd? partition + permutations pos? product range read read-str remove repeat repeatedly rest + reduce second some string string? symbol? take take-nth take-while tee zero? + zip zip-longest]) diff --git a/tests/native_tests/core.hy b/tests/native_tests/core.hy index 320faef..abc54aa 100644 --- a/tests/native_tests/core.hy +++ b/tests/native_tests/core.hy @@ -596,14 +596,6 @@ (setv res (list (take-while (fn [x] (not (none? x))) [1 2 3 4 None 5 6 None 7]))) (assert-equal res [1 2 3 4])) -(defn test-zipwith [] - "NATIVE: testing the zipwith function" - (import operator) - (setv res (zipwith operator.add [1 2 3] [3 2 1])) - (assert-equal (list res) [4 4 4]) - (setv res (zipwith operator.sub [3 7 9] [1 2 4])) - (assert-equal (list res) [2 5 5])) - (defn test-doto [] "NATIVE: testing doto macro" (setv collection []) @@ -629,3 +621,12 @@ "NATIVE: testing import of __init__.hy" (import tests.resources.bin) (assert (in "_null_fn_for_import_test" (dir tests.resources.bin)))) + +(defn test-accumulate [] + "NATIVE: testing the accumulate function" + (assert-equal (list (accumulate ["a" "b" "c"])) + ["a" "ab" "abc"]) + (assert-equal (list (accumulate [1 2 3 4 5])) + [1 3 6 10 15]) + (assert-equal (list (accumulate [1 -2 -3 -4 -5] -)) + [1 3 6 10 15])) From b73bc4c5f9ad8d65a6fe2df93f478e1add40cf28 Mon Sep 17 00:00:00 2001 From: gilch Date: Thu, 3 Sep 2015 11:56:17 -0600 Subject: [PATCH 40/53] remove redundant filterfalse removed `filterfalse` in favor of the shorter `remove` --- hy/core/language.hy | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/hy/core/language.hy b/hy/core/language.hy index 04bce24..a8e2cbb 100644 --- a/hy/core/language.hy +++ b/hy/core/language.hy @@ -93,7 +93,7 @@ (if-python2 (def - filterfalse itertools.ifilterfalse + remove itertools.ifilterfalse zip-longest itertools.izip_longest ;; not builtin in Python3 reduce reduce @@ -104,7 +104,7 @@ range xrange zip itertools.izip) (def - filterfalse itertools.filterfalse + remove itertools.filterfalse zip-longest itertools.zip_longest ;; was builtin in Python2 reduce functools.reduce @@ -130,7 +130,6 @@ drop-while itertools.dropwhile group-by itertools.groupby islice itertools.islice - remove filterfalse take-while itertools.takewhile tee itertools.tee) @@ -456,11 +455,10 @@ (def *exports* '[*map accumulate butlast calling-module-name chain coll? combinations compress cons cons? count cycle dec distinct disassemble drop drop-last - drop-while empty? even? every? first filter filterfalse flatten float? - fraction gensym group-by identity inc input instance? integer integer? - integer-char? interleave interpose islice iterable? iterate iterator? - keyword keyword? last list* macroexpand macroexpand-1 map merge-with - multicombinations name neg? nil? none? nth numeric? odd? partition - permutations pos? product range read read-str remove repeat repeatedly rest - reduce second some string string? symbol? take take-nth take-while tee zero? - zip zip-longest]) + drop-while empty? even? every? first filter flatten float? fraction gensym + group-by identity inc input instance? integer integer? integer-char? + interleave interpose islice iterable? iterate iterator? keyword keyword? + last list* macroexpand macroexpand-1 map merge-with multicombinations name + neg? nil? none? nth numeric? odd? partition permutations pos? product range + read read-str remove repeat repeatedly rest reduce second some string + string? symbol? take take-nth take-while tee zero? zip zip-longest]) From 2670f92731af67c156a9fbca3c0d59ec918d7c5b Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Thu, 3 Sep 2015 21:41:31 +0100 Subject: [PATCH 41/53] Add @timmartin to the AUTHORS file --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index c8e422f..631d741 100644 --- a/AUTHORS +++ b/AUTHORS @@ -64,3 +64,4 @@ * Adrià Garriga Alonso * Antony Woods * Matthew Egan Odendahl +* Tim Martin From f9c3966e6598bb4fc5b0fa81cb8c01b4df9c4734 Mon Sep 17 00:00:00 2001 From: Adam Schwalm Date: Sat, 12 Sep 2015 21:57:47 -0500 Subject: [PATCH 42/53] Remove hard-coded references to 'fn' and 'setv' in errors --- hy/compiler.py | 6 ++++-- tests/native_tests/language.hy | 17 +++++++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/hy/compiler.py b/hy/compiler.py index 9bad831..f3cc9ac 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -1994,7 +1994,8 @@ class HyASTCompiler(object): expression.start_column) elif len(expression) % 2 != 0: raise HyTypeError(expression, - "setv needs an even number of arguments") + "`{}' needs an even number of arguments".format( + root)) else: result = Result() exprs = [] @@ -2152,7 +2153,8 @@ class HyASTCompiler(object): arglist = expression.pop(0) if not isinstance(arglist, HyList): raise HyTypeError(expression, - "First argument to (fn) must be a list") + "First argument to `{}' must be a list".format( + called_as)) (ret, args, defaults, stararg, kwonlyargs, kwonlydefaults, kwargs) = self._parse_lambda_list(arglist) diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index e9dbc27..af03baf 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -75,18 +75,31 @@ (setv y 0 x 1 y x) (assert y) (try (eval '(setv a 1 b)) - (except [e [TypeError]] (assert (in "setv needs an even number of arguments" (str e)))))) + (except [e [TypeError]] (assert (in "`setv' needs an even number of arguments" (str e)))))) (defn test-fn-corner-cases [] "NATIVE: tests that fn/defn handles corner cases gracefully" (try (eval '(fn "foo")) - (except [e [Exception]] (assert (in "to (fn) must be a list" + (except [e [Exception]] (assert (in "to `fn' must be a list" (str e))))) (try (eval '(defn foo "foo")) (except [e [Exception]] (assert (in "takes a parameter list as second" (str e)))))) + +(defn test-alias-names-in-errors [] + "NATIVE: tests that native aliases show the correct names in errors" + (try (eval '(lambda)) + (except [e [Exception]] (assert (in "lambda" (str e))))) + (try (eval '(fn)) + (except [e [Exception]] (assert (in "fn" (str e))))) + (try (eval '(setv 1 2 3)) + (except [e [Exception]] (assert (in "setv" (str e))))) + (try (eval '(def 1 2 3)) + (except [e [Exception]] (assert (in "def" (str e)))))) + + (defn test-for-loop [] "NATIVE: test for loops" (setv count1 0 count2 0) From e05514bb4e3f8906d613cbf5200ac89c8a54b88d Mon Sep 17 00:00:00 2001 From: Paul Tagliamonte Date: Tue, 15 Sep 2015 02:20:58 -0400 Subject: [PATCH 43/53] Add hex and octel support to Hy integers This allows us to parse things like 0xDEADBEEF or 0o080. Filed as issue #937 --- hy/models/integer.py | 16 ++++++++++++++-- tests/native_tests/language.hy | 15 +++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/hy/models/integer.py b/hy/models/integer.py index 3eb1809..f54727b 100644 --- a/hy/models/integer.py +++ b/hy/models/integer.py @@ -19,7 +19,7 @@ # DEALINGS IN THE SOFTWARE. from hy.models import HyObject, _wrappers -from hy._compat import long_type +from hy._compat import long_type, str_type import sys @@ -32,7 +32,19 @@ class HyInteger(HyObject, long_type): """ def __new__(cls, number, *args, **kwargs): - number = long_type(number) + if isinstance(number, str_type): + bases = {"0x": 16, "0o": 8, "0b": 2} + for leader, base in bases.items(): + if number.startswith(leader): + # We've got a string, known leader, set base. + number = long_type(number, base=base) + break + else: + # We've got a string, no known leader; base 10. + number = long_type(number, base=10) + else: + # We've got a non-string; convert straight. + number = long_type(number) return super(HyInteger, cls).__new__(cls, number) diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index e9dbc27..c3ea42e 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -12,6 +12,21 @@ (assert (isinstance sys.argv list))) +(defn test-hex [] + "NATIVE: test hex" + (assert (= 0x80 128))) + + +(defn test-octal [] + "NATIVE: test octal" + (assert (= 0o1232 666))) + + +(defn test-binary [] + "NATIVE: test binary" + (assert (= 0b1011101 93))) + + (defn test-fractions [] "NATIVE: test fractions" (assert (= 1/2 (fraction 1 2)))) From fc5d74ba857c744705e65e282ba21773689a3dbf Mon Sep 17 00:00:00 2001 From: Paul Tagliamonte Date: Tue, 15 Sep 2015 12:18:56 -0400 Subject: [PATCH 44/53] Add in documentation for non-base 10 numbers --- docs/language/api.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/language/api.rst b/docs/language/api.rst index b18eaef..c116d82 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -31,6 +31,21 @@ languages. that symbols with dashes will shadow their underscore equivalents, and vice versa. +Notes on Syntax +=============== + +integers +-------- + +.. versionadded:: 0.11.1 + +In addition to regular numbers, standard notation from Python 3 for non-base 10 +integers is used. ``0x`` for Hex, ``0o`` for Octal, ``0b`` for Binary. + +.. code-block:: clj + + (print 0x80 0b11101 0o102 30) + Built-Ins ========= From 49a294e98dde35b373f018ba8fee0f87e0310b3f Mon Sep 17 00:00:00 2001 From: Michel Alexandre Salim Date: Wed, 23 Sep 2015 00:39:08 +0700 Subject: [PATCH 45/53] Update style-guide.rst Re-indent `fib` examples to match that in original blog post - "Indentation shall be two spaces, except where matching the indentation of the previous line" --- docs/style-guide.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 4222805..92eddd3 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -61,8 +61,8 @@ Layout & Indentation ;; Good (and preferred) (defn fib [n] (if (<= n 2) - n - (+ (fib (- n 1)) (fib (- n 2))))) + n + (+ (fib (- n 1)) (fib (- n 2))))) ;; Still okay (defn fib [n] @@ -89,8 +89,8 @@ Layout & Indentation ;; Good (and preferred) (defn fib [n] (if (<= n 2) - n - (+ (fib (- n 1)) (fib (- n 2))))) + n + (+ (fib (- n 1)) (fib (- n 2))))) ;; Hysterically ridiculous (defn fib [n] From b1f497ae7224911cc4712a4b8939cd75845a2942 Mon Sep 17 00:00:00 2001 From: "Zack M. Davis" Date: Wed, 23 Sep 2015 22:44:19 -0700 Subject: [PATCH 46/53] =?UTF-8?q?fix=20CI=20by=20moving=20exclude=5Flines?= =?UTF-8?q?=20.coveragerc=20setting=20to=20a=20new=20[report]=20=C2=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On 21 September, all the continuous integration checks on a revision of pull-request #920 were observed to fail with a "CoverageException: Unrecognized option '[run] exclude_lines=' in config file .coveragerc". One can only imagine that this was due to it trying to use the new version 4.0 of Ned Batchelder's ever-popular coverage.py (just released on 20 September), with which our existing .coveragerc would seem to have not been compatible. --- .coveragerc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.coveragerc b/.coveragerc index a238df8..1e3173b 100644 --- a/.coveragerc +++ b/.coveragerc @@ -6,6 +6,8 @@ omit = */site-packages/nose/* */pypy/* + +[report] exclude_lines = # Have to re-enable the standard pragma pragma: no cover \ No newline at end of file From c89865aaf0cdc61c0f6933cb3b08c1e11061e603 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Mon, 10 Aug 2015 10:58:13 +0200 Subject: [PATCH 47/53] defclass should add an implicit nil to --init-- functions To make it easier to write --init-- functions, defclass will now check any (setv) expressions (and its property list), to find any --init-- declarations, and append a nil to the end. Signed-off-by: Gergely Nagy --- hy/compiler.py | 28 ++++++++++++++++++++++------ tests/native_tests/defclass.hy | 14 ++++++++++++++ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/hy/compiler.py b/hy/compiler.py index f3cc9ac..8800082 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -2259,6 +2259,22 @@ class HyASTCompiler(object): @builds("defclass") @checkargs(min=1) def compile_class_expression(self, expressions): + def rewire_init(expr): + new_args = [] + if expr[0] == HySymbol("setv"): + pairs = expr[1:] + while len(pairs) > 0: + k, v = (pairs.pop(0), pairs.pop(0)) + if k == HySymbol("__init__"): + v.append(HySymbol("None")) + new_args.append(k) + new_args.append(v) + expr = HyExpression([ + HySymbol("setv") + ] + new_args).replace(expr) + + return expr + expressions.pop(0) # class class_name = expressions.pop(0) @@ -2291,14 +2307,14 @@ class HyASTCompiler(object): if expressions and isinstance(expressions[0], HyList) \ and not isinstance(expressions[0], HyExpression): expr = expressions.pop(0) - body += self.compile( - HyExpression([ - HySymbol("setv") - ] + expr).replace(expr) - ) + expr = HyExpression([ + HySymbol("setv") + ] + expr).replace(expr) + body += self.compile(rewire_init(expr)) for expression in expressions: - body += self.compile(macroexpand(expression, self.module_name)) + expr = rewire_init(macroexpand(expression, self.module_name)) + body += self.compile(expr) self.allow_builtins = allow_builtins diff --git a/tests/native_tests/defclass.hy b/tests/native_tests/defclass.hy index 3da9aa4..84b1dc6 100644 --- a/tests/native_tests/defclass.hy +++ b/tests/native_tests/defclass.hy @@ -109,3 +109,17 @@ (assert (= a.y 2)) (assert foo 2) (assert (.greet a) "hello")) + +(defn test-defclass-implicit-nil-for-init [] + "NATIVE: test that defclass adds an implicit nil to --init--" + (defclass A [] + [--init-- (fn [self] (setv self.x 1) 42)]) + (defclass B [] + (defn --init-- [self] + (setv self.x 2) + 42)) + + (setv a (A)) + (setv b (B)) + (assert (= a.x 1)) + (assert (= b.x 2))) From 26f342d58006a6b2058b68d35bf22c7c4cb0f43e Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Mon, 10 Aug 2015 13:17:40 +0200 Subject: [PATCH 48/53] Teach apply about symbol mangling apply now mangles strings and keywords according to the Hy mangling rules (by using the same function, now imported from hy.lex.parser). With this change, if the dict passed to apply has keywords, strings or quoted symbols, they'll get mangled, to turn them into proper keys. This only works for the cases where the keys are directly in the apply params. A previously deffed dict, or key through a variable will not be mangled. This closes #219. Signed-off-by: Gergely Nagy --- docs/language/api.rst | 8 ++++++-- hy/compiler.py | 18 +++++++++++++++++- hy/lex/parser.py | 33 +++++++++++++++++---------------- tests/native_tests/language.hy | 5 ++++- 4 files changed, 44 insertions(+), 20 deletions(-) diff --git a/docs/language/api.rst b/docs/language/api.rst index c116d82..c22d3e2 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -115,8 +115,10 @@ it appends it as the last argument. The following code demonstrates this: apply ----- -``apply`` is used to apply an optional list of arguments and an optional -dictionary of kwargs to a function. +``apply`` is used to apply an optional list of arguments and an +optional dictionary of kwargs to a function. The symbol mangling +transformations will be applied to all keys in the dictionary of +kwargs, provided the dictionary and its keys are defined in-place. Usage: ``(apply fn-name [args] [kwargs])`` @@ -142,6 +144,8 @@ Examples: (apply total-purchase [] {"price" 10 "amount" 15 "vat" 1.05}) ;=> 165.375 + (apply total-purchase [] {:price 10 :amount 15 :vat 1.05}) + ;=> 165.375 and --- diff --git a/hy/compiler.py b/hy/compiler.py index f3cc9ac..e7489de 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -38,6 +38,8 @@ from hy.models.cons import HyCons from hy.errors import HyCompileError, HyTypeError +from hy.lex.parser import hy_symbol_mangle + import hy.macros from hy._compat import ( str_type, long_type, PY27, PY33, PY3, PY34, PY35, raise_empty) @@ -1640,7 +1642,21 @@ class HyASTCompiler(object): ret = stargs + ret if expr: - kwargs = self.compile(expr.pop(0)) + kwargs = expr.pop(0) + if isinstance(kwargs, HyDict): + new_kwargs = [] + for k, v in kwargs.items(): + if isinstance(k, HySymbol): + pass + elif isinstance(k, HyString): + k = HyString(hy_symbol_mangle(str_type(k))).replace(k) + elif isinstance(k, HyKeyword): + sym = hy_symbol_mangle(str_type(k)[2:]) + k = HyString(sym).replace(k) + new_kwargs += [k, v] + kwargs = HyDict(new_kwargs).replace(kwargs) + + kwargs = self.compile(kwargs) if PY35: kwargs_expr = kwargs.force_expr ret.expr.keywords.append( diff --git a/hy/lex/parser.py b/hy/lex/parser.py index abce1d7..5492584 100644 --- a/hy/lex/parser.py +++ b/hy/lex/parser.py @@ -45,6 +45,22 @@ pg = ParserGenerator( ) +def hy_symbol_mangle(p): + if p.startswith("*") and p.endswith("*") and p not in ("*", "**"): + p = p[1:-1].upper() + + if "-" in p and p != "-": + p = p.replace("-", "_") + + if p.endswith("?") and p != "?": + p = "is_%s" % (p[:-1]) + + if p.endswith("!") and p != "!": + p = "%s_bang" % (p[:-1]) + + return p + + def set_boundaries(fun): @wraps(fun) def wrapped(p): @@ -297,22 +313,7 @@ def t_identifier(p): if obj.startswith(":"): return HyKeyword(obj) - def mangle(p): - if p.startswith("*") and p.endswith("*") and p not in ("*", "**"): - p = p[1:-1].upper() - - if "-" in p and p != "-": - p = p.replace("-", "_") - - if p.endswith("?") and p != "?": - p = "is_%s" % (p[:-1]) - - if p.endswith("!") and p != "!": - p = "%s_bang" % (p[:-1]) - - return p - - obj = ".".join([mangle(part) for part in obj.split(".")]) + obj = ".".join([hy_symbol_mangle(part) for part in obj.split(".")]) return HySymbol(obj) diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index 497139b..126dd57 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -309,7 +309,10 @@ (assert (= (apply sumit [] {"a" 1 "b" 1 "c" 2}) 4)) (assert (= (apply sumit ((fn [] [1 1])) {"c" 1}) 3)) (defn noargs [] [1 2 3]) - (assert (= (apply noargs) [1 2 3]))) + (assert (= (apply noargs) [1 2 3])) + (defn sumit-mangle [an-a a-b a-c a-d] (+ an-a a-b a-c a-d)) + (def Z "a_d") + (assert (= (apply sumit-mangle [] {"an-a" 1 :a-b 2 'a-c 3 Z 4}) 10))) (defn test-apply-with-methods [] From 02138655259a7acef8d8e8e8ba2f62a307475958 Mon Sep 17 00:00:00 2001 From: Matthew Egan Odendahl Date: Thu, 24 Sep 2015 22:47:16 -0600 Subject: [PATCH 49/53] Add link to stable docs The language cleanup is confusing the new people. This is at least the third time. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f23d078..ec9834d 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,8 @@ Project ------- * Code: https://github.com/hylang/hy -* Docs: http://hylang.org/ +* Docs (latest): http://hylang.org/ +* Docs (stable): http://docs.hylang.org/en/stable/ * Quickstart: http://hylang.org/en/latest/quickstart.html * Bug reports: We have no bugs! Your bugs are your own! (https://github.com/hylang/hy/issues) * License: MIT (Expat) From a026a6a960152fcc6c0af0a37adf257cd9800ab9 Mon Sep 17 00:00:00 2001 From: Matthew Egan Odendahl Date: Thu, 24 Sep 2015 23:04:17 -0600 Subject: [PATCH 50/53] further clarification --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ec9834d..54ab956 100644 --- a/README.md +++ b/README.md @@ -40,8 +40,8 @@ Project ------- * Code: https://github.com/hylang/hy -* Docs (latest): http://hylang.org/ -* Docs (stable): http://docs.hylang.org/en/stable/ +* Docs (latest, for use with bleeding-edge github version): http://hylang.org/ +* Docs (stable, for use with the PyPI version): http://docs.hylang.org/en/stable/ * Quickstart: http://hylang.org/en/latest/quickstart.html * Bug reports: We have no bugs! Your bugs are your own! (https://github.com/hylang/hy/issues) * License: MIT (Expat) From 3a1af0c2195d4325d2472328140acdf3e1029cc2 Mon Sep 17 00:00:00 2001 From: Jakub Wilk Date: Tue, 29 Sep 2015 22:57:36 +0200 Subject: [PATCH 51/53] Fix typos --- NEWS | 6 +++--- hy/cmdline.py | 2 +- tests/native_tests/py3_only_tests.hy | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/NEWS b/NEWS index d482bcb..be48b1f 100644 --- a/NEWS +++ b/NEWS @@ -7,7 +7,7 @@ Changes from 0.10.1 * yield-from support for Python 2 * with-decorator can now be applied to classes. * assert now accepts an optional assertion message. - * Comparision operators can now be used with map, filter, and reduce. + * Comparison operators can now be used with map, filter, and reduce. * new last function * new drop-last function * new lisp-if-not/lif-not macro @@ -73,7 +73,7 @@ Changes from 0.10.0 [ Misc. Fixes ] * Symbols like true, false, none can't be assigned * Set sys.argv default to [''] like Python does - * REPL displays the the python version and platform at startup + * REPL displays the python version and platform at startup * Dockerfile added for https://registry.hub.docker.com/_/hylang/ [ Contrib changes ] @@ -328,7 +328,7 @@ Changes from Hy 0.9.7 expand things in quotes. * kwapply now works with symbols as well as raw dicts. (ND) * Try / Except will now return properly again. (PT) - * Bare-names sprinked around the AST won't show up anymore (ND) + * Bare-names sprinkled around the AST won't show up anymore (ND) [ Language Changes ] diff --git a/hy/cmdline.py b/hy/cmdline.py index dce1a07..142eb00 100644 --- a/hy/cmdline.py +++ b/hy/cmdline.py @@ -289,7 +289,7 @@ def cmdline_handler(scriptname, argv): parser.add_argument('args', nargs=argparse.REMAINDER, help=argparse.SUPPRESS) - # stash the hy exectuable in case we need it later + # stash the hy executable in case we need it later # mimics Python sys.executable hy.executable = argv[0] diff --git a/tests/native_tests/py3_only_tests.hy b/tests/native_tests/py3_only_tests.hy index 2cf91ba..a6e359b 100644 --- a/tests/native_tests/py3_only_tests.hy +++ b/tests/native_tests/py3_only_tests.hy @@ -1,4 +1,4 @@ -;; Tests where the emited code relies on Python 3. +;; Tests where the emitted code relies on Python 3. ;; Conditionally included in nosetests runs. (import [hy._compat [PY33]]) From 9f88e07e1d9b2fc957344736fe75ba8960f97a80 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Mon, 17 Aug 2015 09:07:32 +0200 Subject: [PATCH 52/53] Drop a set of brackets from let. This changes let to use a flat list of symbol-value pairs instead of a vector of vectors. One side effect is that (let [[a 1] z]) is not expressible now, and one will explicitly need to set a nil value for z, such as: (let [a 1 z nil]). Closes #713. Signed-off-by: Gergely Nagy --- docs/language/api.rst | 48 +++++------ docs/language/core.rst | 2 +- docs/language/internals.rst | 16 ++-- docs/style-guide.rst | 4 +- eg/twisted/get-page.hy | 2 +- hy/contrib/alias.hy | 4 +- hy/contrib/anaphoric.hy | 23 +++--- hy/contrib/botsbuildbots.hy | 4 +- hy/contrib/curry.hy | 4 +- hy/contrib/loop.hy | 6 +- hy/contrib/meth.hy | 10 +-- hy/core/bootstrap.hy | 20 ++--- hy/core/language.hy | 49 +++++------ hy/core/macros.hy | 37 ++++++--- hy/core/shadow.hy | 6 +- scripts/reformat-changelog | 40 ++++----- tests/compilers/test_ast.py | 12 ++- tests/native_tests/contrib/anaphoric.hy | 6 +- tests/native_tests/contrib/meth.hy | 16 ++-- tests/native_tests/contrib/walk.hy | 6 +- tests/native_tests/core.hy | 2 +- tests/native_tests/defclass.hy | 2 +- tests/native_tests/language.hy | 103 ++++++++++++------------ tests/native_tests/mathematics.hy | 44 +++++----- tests/native_tests/native_macros.hy | 8 +- tests/native_tests/py3_only_tests.hy | 16 ++-- tests/native_tests/shadow.hy | 20 ++--- 27 files changed, 254 insertions(+), 256 deletions(-) diff --git a/docs/language/api.rst b/docs/language/api.rst index c22d3e2..d44e0d4 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -213,17 +213,17 @@ Examples of usage: .. code-block:: clj - =>(let [[collection {}]] + =>(let [collection {}] ... (assoc collection "Dog" "Bark") ... (print collection)) {u'Dog': u'Bark'} - =>(let [[collection {}]] + =>(let [collection {}] ... (assoc collection "Dog" "Bark" "Cat" "Meow") ... (print collection)) {u'Cat': u'Meow', u'Dog': u'Bark'} - =>(let [[collection [1 2 3 4]]] + =>(let [collection [1 2 3 4]] ... (assoc collection 2 None) ... (print collection)) [1, 2, None, 4] @@ -463,8 +463,8 @@ Parameters may have the following keywords in front of them: .. code-block:: clj => (defn zig-zag-sum [&rest numbers] - (let [[odd-numbers (list-comp x [x numbers] (odd? x))] - [even-numbers (list-comp x [x numbers] (even? x))]] + (let [odd-numbers (list-comp x [x numbers] (odd? x)) + even-numbers (list-comp x [x numbers] (even? x))] (- (sum odd-numbers) (sum even-numbers)))) => (zig-zag-sum) @@ -486,7 +486,7 @@ Parameters may have the following keywords in front of them: .. code-block:: clj => (defn compare [a b &kwonly keyfn [reverse false]] - ... (let [[result (keyfn a b)]] + ... (let [result (keyfn a b)] ... (if (not reverse) ... result ... (- result)))) @@ -786,8 +786,8 @@ list. Example usage: .. code-block:: clj - => (let [[animals {"dog" "bark" "cat" "meow"}] - ... [numbers ["zero" "one" "two" "three"]]] + => (let [animals {"dog" "bark" "cat" "meow"} + ... numbers ["zero" "one" "two" "three"]] ... (print (get animals "dog")) ... (print (get numbers 2))) bark @@ -988,30 +988,24 @@ example showcases this behaviour: .. code-block:: clj - => (let [[x 5]] (print x) - ... (let [[x 6]] (print x)) + => (let [x 5] (print x) + ... (let [x 6] (print x)) ... (print x)) 5 6 5 -The ``let`` macro takes two parameters: a vector defining *variables* and the -*body* which gets executed. *variables* is a vector where each element is either -a single variable or a vector defining a variable value pair. In the case of a -single variable, it is assigned value ``None``; otherwise, the supplied value is -used. - -.. code-block:: clj - - => (let [x [y 5]] (print x y)) - None 5 +The ``let`` macro takes two parameters: a vector defining *variables* +and the *body* which gets executed. *variables* is a vector of +variable and value pairs. Note that the variable assignments are executed one by one, from left to right. The following example takes advantage of this: .. code-block:: clj - => (let [[x 5] [y (+ x 1)]] (print x y)) + => (let [x 5 + y (+ x 1)] (print x y)) 5 6 @@ -1050,15 +1044,15 @@ to modify variables through nested ``let`` or ``fn`` scopes: .. code-block:: clj - (let [[x 0]] + (let [x 0] (for [y (range 10)] - (let [[z (inc y)]] + (let [z (inc y)] (nonlocal x) ; allow the setv to "jump scope" to resolve x (setv x (+ x y)))) x) (defn some-function [] - (let [[x 0]] + (let [x 0] (register-some-callback (fn [stuff] (nonlocal x) @@ -1467,9 +1461,9 @@ expands to: .. code-block:: hy - (let [[a (gensym) - [b (gensym) - [c (gensym)]] + (let [a (gensym) + b (gensym) + c (gensym)] ...) .. seealso:: diff --git a/docs/language/core.rst b/docs/language/core.rst index 3f752f8..6890684 100644 --- a/docs/language/core.rst +++ b/docs/language/core.rst @@ -1103,7 +1103,7 @@ if *from-file* ends before a complete expression can be parsed. => (with [[f (open "example.hy")]] ... (try ... (while true - ... (let [[exp (read f)]] + ... (let [exp (read f)] ... (do ... (print "OHY" exp) ... (eval exp)))) diff --git a/docs/language/internals.rst b/docs/language/internals.rst index faa69fb..9fb1d53 100644 --- a/docs/language/internals.rst +++ b/docs/language/internals.rst @@ -381,7 +381,7 @@ A first pass might be something like: .. code-block:: hy (defmacro nif [expr pos-form zero-form neg-form] - `(let [[obscure-name ~expr]] + `(let [obscure-name ~expr] (cond [(pos? obscure-name) ~pos-form] [(zero? obscure-name) ~zero-form] [(neg? obscure-name) ~neg-form]))) @@ -396,8 +396,8 @@ such an occasion. A much better version of ``nif`` would be: .. code-block:: hy (defmacro nif [expr pos-form zero-form neg-form] - (let [[g (gensym)]] - `(let [[~g ~expr]] + (let [g (gensym)] + `(let [~g ~expr] (cond [(pos? ~g) ~pos-form] [(zero? ~g) ~zero-form] [(neg? ~g) ~neg-form])))) @@ -415,9 +415,9 @@ expands to: .. code-block:: hy - (let [[a (gensym) - [b (gensym) - [c (gensym)]] + (let [a (gensym) + b (gensym) + c (gensym)] ...) so our re-written ``nif`` would look like: @@ -426,7 +426,7 @@ so our re-written ``nif`` would look like: (defmacro nif [expr pos-form zero-form neg-form] (with-gensyms [g] - `(let [[~g ~expr]] + `(let [~g ~expr] (cond [(pos? ~g) ~pos-form] [(zero? ~g) ~zero-form] [(neg? ~g) ~neg-form])))) @@ -440,7 +440,7 @@ Our final version of ``nif``, built with ``defmacro/g!`` becomes: .. code-block:: hy (defmacro/g! nif [expr pos-form zero-form neg-form] - `(let [[~g!res ~expr]] + `(let [~g!res ~expr] (cond [(pos? ~g!res) ~pos-form] [(zero? ~g!res) ~zero-form] [(neg? ~g!res) ~neg-form])))) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 92eddd3..2e3bd12 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -105,8 +105,8 @@ Layout & Indentation .. code-block:: clj - (let [[foo (bar)] - [qux (baz)]] + (let [foo (bar)] + qux (baz)] (foo qux)) diff --git a/eg/twisted/get-page.hy b/eg/twisted/get-page.hy index 0796820..3f102be 100644 --- a/eg/twisted/get-page.hy +++ b/eg/twisted/get-page.hy @@ -27,7 +27,7 @@ (reactor.stop)) (defn get-page [url] - (let [[d (getPage url)]] + (let [d (getPage url)] (d.addCallback get-page-size) (d.addErrback log-error) (d.addCallback finish))) diff --git a/hy/contrib/alias.hy b/hy/contrib/alias.hy index b1e0d5d..56c4a7c 100644 --- a/hy/contrib/alias.hy +++ b/hy/contrib/alias.hy @@ -30,8 +30,8 @@ (defmacro defn-alias [names lambda-list &rest body] "define one function with several names" - (let [[main (first names)] - [aliases (rest names)]] + (let [main (first names) + aliases (rest names)] (setv ret `(do (defn ~main ~lambda-list ~@body))) (for* [name aliases] (.append ret diff --git a/hy/contrib/anaphoric.hy b/hy/contrib/anaphoric.hy index dc6bb24..019d6db 100644 --- a/hy/contrib/anaphoric.hy +++ b/hy/contrib/anaphoric.hy @@ -26,7 +26,7 @@ (defmacro ap-if (test-form &rest args) - `(let [[it ~test-form]] (if it ~@args))) + `(let [it ~test-form] (if it ~@args))) (defmacro ap-each [lst &rest body] @@ -37,7 +37,7 @@ (defmacro ap-each-while [lst form &rest body] "Evalutate the body form for each element in the list while the predicate form evaluates to True." - `(let [[p (lambda [it] ~form)]] + `(let [p (lambda [it] ~form)] (for [it ~lst] (if (p it) ~@body @@ -46,8 +46,9 @@ (defmacro ap-map [form lst] "Yield elements evaluated in the form for each element in the list." - (let [[v (gensym 'v)] [f (gensym 'f)]] - `(let [[~f (lambda [it] ~form)]] + (let [v (gensym 'v) + f (gensym 'f)] + `(let [~f (lambda [it] ~form)] (for [~v ~lst] (yield (~f ~v)))))) @@ -55,7 +56,7 @@ (defmacro ap-map-when [predfn rep lst] "Yield elements evaluated for each element in the list when the predicate function returns True." - `(let [[f (lambda [it] ~rep)]] + `(let [f (lambda [it] ~rep)] (for [it ~lst] (if (~predfn it) (yield (f it)) @@ -64,7 +65,7 @@ (defmacro ap-filter [form lst] "Yield elements returned when the predicate form evaluates to True." - `(let [[pred (lambda [it] ~form)]] + `(let [pred (lambda [it] ~form)] (for [val ~lst] (if (pred val) (yield val))))) @@ -85,7 +86,7 @@ (defmacro ap-first [predfn lst] "Yield the first element that passes `predfn`" (with-gensyms [n] - `(let [[~n None]] + `(let [~n None] (ap-each ~lst (when ~predfn (setv ~n it) (break))) ~n))) @@ -93,7 +94,7 @@ (defmacro ap-last [predfn lst] "Yield the last element that passes `predfn`" (with-gensyms [n] - `(let [[~n None]] + `(let [~n None] (ap-each ~lst (none? ~n) (when ~predfn (setv ~n it))) @@ -103,10 +104,10 @@ (defmacro ap-reduce [form lst &optional [initial-value None]] "Anaphoric form of reduce, `acc' and `it' can be used for a form" (if (none? initial-value) - `(let [[acc (car ~lst)]] + `(let [acc (car ~lst)] (ap-each (cdr ~lst) (setv acc ~form)) acc) - `(let [[acc ~initial-value]] + `(let [acc ~initial-value] (ap-each ~lst (setv acc ~form)) acc))) @@ -115,7 +116,7 @@ "Pushes a value through several forms. (Anaphoric version of -> and ->>)" (if (empty? forms) var - `(ap-pipe (let [[it ~var]] ~(first forms)) ~@(rest forms)))) + `(ap-pipe (let [it ~var] ~(first forms)) ~@(rest forms)))) (defmacro ap-compose [&rest forms] diff --git a/hy/contrib/botsbuildbots.hy b/hy/contrib/botsbuildbots.hy index 98f3737..fcea8c6 100644 --- a/hy/contrib/botsbuildbots.hy +++ b/hy/contrib/botsbuildbots.hy @@ -26,8 +26,8 @@ (do (import [requests]) - (let [[r (requests.get - "https://raw.githubusercontent.com/hylang/hy/master/AUTHORS")]] + (let [r (requests.get + "https://raw.githubusercontent.com/hylang/hy/master/AUTHORS")] (repeat r.text))) (except [e ImportError] (repeat "Botsbuildbots requires `requests' to function.")))) diff --git a/hy/contrib/curry.hy b/hy/contrib/curry.hy index f1c0162..ae3e4da 100644 --- a/hy/contrib/curry.hy +++ b/hy/contrib/curry.hy @@ -2,8 +2,8 @@ (defn curry [func] - (let [[sig (.getargspec inspect func)] - [count (len sig.args)]] + (let [sig (.getargspec inspect func) + count (len sig.args)] (fn [&rest args] (if (< (len args) count) diff --git a/hy/contrib/loop.hy b/hy/contrib/loop.hy index 17bb0d9..a9b98f6 100644 --- a/hy/contrib/loop.hy +++ b/hy/contrib/loop.hy @@ -58,7 +58,7 @@ (defmacro/g! fnr [signature &rest body] - (let [[new-body (recursive-replace 'recur g!recur-fn body)]] + (let [new-body (recursive-replace 'recur g!recur-fn body)] `(do (import [hy.contrib.loop [--trampoline--]]) (with-decorator @@ -85,7 +85,7 @@ ;; If recur is used in a non-tail-call position, None is returned, which ;; causes chaos. Fixing this to detect if recur is in a tail-call position ;; and erroring if not is a giant TODO. - (let [[fnargs (map (fn [x] (first x)) bindings)] - [initargs (map second bindings)]] + (let [fnargs (map (fn [x] (first x)) bindings) + initargs (map second bindings)] `(do (defnr ~g!recur-fn [~@fnargs] ~@body) (~g!recur-fn ~@initargs)))) diff --git a/hy/contrib/meth.hy b/hy/contrib/meth.hy index 172e127..c52e03c 100644 --- a/hy/contrib/meth.hy +++ b/hy/contrib/meth.hy @@ -3,11 +3,11 @@ (defmacro route-with-methods [name path methods params &rest code] "Same as route but with an extra methods array to specify HTTP methods" - `(let [[deco (apply app.route [~path] - {"methods" ~methods})]] - (with-decorator deco - (defn ~name ~params - (do ~@code))))) + `(let [deco (apply app.route [~path] + {"methods" ~methods})] + (with-decorator deco + (defn ~name ~params + (do ~@code))))) ;; Some macro examples (defmacro route [name path params &rest code] diff --git a/hy/core/bootstrap.hy b/hy/core/bootstrap.hy index db63a06..6667ed8 100644 --- a/hy/core/bootstrap.hy +++ b/hy/core/bootstrap.hy @@ -42,22 +42,14 @@ (defmacro let [variables &rest body] "Execute `body` in the lexical context of `variables`" - (setv macroed_variables []) (if (not (isinstance variables HyList)) (macro-error variables "let lexical context must be a list")) - (for* [variable variables] - (if (isinstance variable HyList) - (do - (if (!= (len variable) 2) - (macro-error variable "let variable assignments must contain two items")) - (.append macroed-variables `(setv ~(get variable 0) ~(get variable 1)))) - (if (isinstance variable HySymbol) - (.append macroed-variables `(setv ~variable None)) - (macro-error variable "let lexical context element must be a list or symbol")))) - `((fn [] - ~@macroed-variables - ~@body))) - + (if (= (len variables) 0) + `((fn [] + ~@body)) + `((fn [] + (setv ~@variables) + ~@body)))) (defmacro if-python2 [python2-form python3-form] "If running on python2, execute python2-form, else, execute python3-form" diff --git a/hy/core/language.hy b/hy/core/language.hy index 3b71dc0..6c5a675 100644 --- a/hy/core/language.hy +++ b/hy/core/language.hy @@ -84,12 +84,13 @@ (defn distinct [coll] "Return a generator from the original collection with duplicates removed" - (let [[seen (set)] [citer (iter coll)]] - (for* [val citer] - (if (not_in val seen) - (do - (yield val) - (.add seen val)))))) + (let [seen (set) + citer (iter coll)] + (for* [val citer] + (if (not_in val seen) + (do + (yield val) + (.add seen val)))))) (if-python2 (def @@ -158,7 +159,7 @@ (defn drop-last [n coll] "Return a sequence of all but the last n elements in coll." - (let [[iters (tee coll)]] + (let [iters (tee coll)] (map first (apply zip [(get iters 0) (drop n (get iters 1))])))) @@ -210,7 +211,7 @@ (setv _gensym_lock (Lock)) (defn gensym [&optional [g "G"]] - (let [[new_symbol None]] + (let [new_symbol None] (global _gensym_counter) (global _gensym_lock) (.acquire _gensym_lock) @@ -310,15 +311,16 @@ from the latter (left-to-right) will be combined with the mapping in the result by calling (f val-in-result val-in-latter)." (if (any maps) - (let [[merge-entry (fn [m e] - (let [[k (get e 0)] [v (get e 1)]] - (if (in k m) - (assoc m k (f (get m k) v)) - (assoc m k v))) - m)] - [merge2 (fn [m1 m2] - (reduce merge-entry (.items m2) (or m1 {})))]] - (reduce merge2 maps)))) + (let [merge-entry (fn [m e] + (let [k (get e 0) + v (get e 1)] + (if (in k m) + (assoc m k (f (get m k) v)) + (assoc m k v))) + m) + merge2 (fn [m1 m2] + (reduce merge-entry (.items m2) (or m1 {})))] + (reduce merge2 maps)))) (defn neg? [n] "Return true if n is < 0" @@ -402,12 +404,13 @@ "Return every nth member of coll raises ValueError for (not (pos? n))" (if (pos? n) - (let [[citer (iter coll)] [skip (dec n)]] - (for* [val citer] - (yield val) - (for* [_ (range skip)] - (next citer)))) - (raise (ValueError "n must be positive")))) + (let [citer (iter coll) + skip (dec n)] + (for* [val citer] + (yield val) + (for* [_ (range skip)] + (next citer)))) + (raise (ValueError "n must be positive")))) (defn zero? [n] "Return true if n is 0" diff --git a/hy/core/macros.hy b/hy/core/macros.hy index 95706af..9eb1521 100644 --- a/hy/core/macros.hy +++ b/hy/core/macros.hy @@ -39,7 +39,7 @@ baz))" (if (not (empty? args)) - (let [[primary (.pop args 0)]] + (let [primary (.pop args 0)] (if (isinstance primary HyList) ;;; OK. if we have a list, we can go ahead and unpack that ;;; as the argument to with. @@ -115,7 +115,7 @@ [(empty? args) `(do ~@body ~@belse)] [(= (len args) 2) `(for* [~@args] (do ~@body) ~@belse)] [true - (let [[alist (cut args 0 nil 2)]] + (let [alist (cut args 0 nil 2)] `(for* [(, ~@alist) (genexpr (, ~@alist) [~@args])] (do ~@body) ~@belse))])) @@ -138,7 +138,7 @@ (if (isinstance expression HyExpression) `(~(first expression) ~f ~@(rest expression)) `(~expression ~f))) - `(let [[~f ~form]] + `(let [~f ~form] ~@(map build-form expressions) ~f)) @@ -180,14 +180,25 @@ (defmacro with-gensyms [args &rest body] - `(let ~(HyList (map (fn [x] `[~x (gensym '~x)]) args)) - ~@body)) + (setv syms []) + (for* [arg args] + (.extend syms `[~arg (gensym '~arg)])) + `(let ~syms + ~@body)) (defmacro defmacro/g! [name args &rest body] - (let [[syms (list (distinct (filter (fn [x] (and (hasattr x "startswith") (.startswith x "g!"))) (flatten body))))]] + (let [syms (list + (distinct + (filter (fn [x] + (and (hasattr x "startswith") + (.startswith x "g!"))) + (flatten body)))) + gensyms []] + (for* [sym syms] + (.extend gensyms `[~sym (gensym (cut '~sym 2))])) `(defmacro ~name [~@args] - (let ~(HyList (map (fn [x] `[~x (gensym (cut '~x 2))]) syms)) - ~@body)))) + (let ~gensyms + ~@body)))) (if-python2 @@ -211,9 +222,9 @@ (defmacro defmain [args &rest body] "Write a function named \"main\" and do the if __main__ dance" - (let [[retval (gensym)] - [mainfn `(fn [~@args] - ~@body)]] + (let [retval (gensym) + mainfn `(fn [~@args] + ~@body)] `(when (= --name-- "__main__") (import sys) (setv ~retval (apply ~mainfn sys.argv)) @@ -222,6 +233,6 @@ (defreader @ [expr] - (let [[decorators (cut expr nil -1)] - [fndef (get expr -1)]] + (let [decorators (cut expr nil -1) + fndef (get expr -1)] `(with-decorator ~@decorators ~fndef))) diff --git a/hy/core/shadow.hy b/hy/core/shadow.hy index b475e37..b63219d 100644 --- a/hy/core/shadow.hy +++ b/hy/core/shadow.hy @@ -26,7 +26,7 @@ (defn + [&rest args] "Shadow + operator for when we need to import / map it against something" - (let [[count (len args)]] + (let [count (len args)] (if (zero? count) (raise (TypeError "Need at least 1 argument to add/concatenate")) (if (= count 1) @@ -36,7 +36,7 @@ (defn - [&rest args] "Shadow - operator for when we need to import / map it against something" - (let [[count (len args)]] + (let [count (len args)] (if (= count 0) (raise (TypeError "Need at least 1 argument to subtract")) (if (= count 1) @@ -53,7 +53,7 @@ (defn / [&rest args] "Shadow / operator for when we need to import / map it against something" - (let [[count (len args)]] + (let [count (len args)] (if (= count 0) (raise (TypeError "Need at least 1 argument to divide")) (if (= count 1) diff --git a/scripts/reformat-changelog b/scripts/reformat-changelog index cb4f2a0..899a9fa 100755 --- a/scripts/reformat-changelog +++ b/scripts/reformat-changelog @@ -8,22 +8,22 @@ " -- Alexander Artemenko Thu, 30 Sep 2014 13:06:09 +0400") (defn read-lines-from-file [filename] - (let [[f (codecs.open filename "r" "utf-8")]] - (fn [] (let [[line (.readline f) ]] - line)))) + (let [f (codecs.open filename "r" "utf-8")] + (fn [] (let [line (.readline f) ] + line)))) (defn get-version-number [line] - (let [[match (re.search r"Changes from.*(\d+\.\d+\.\d+)$" line)]] - (if match - (let [[version (.group match (int 1))] - [numbered (list (map int (.split version "."))) ] - [explicit-mapping {"0.9.12" "0.10.0" - "0.8.2" "0.9.0"}]] - (assoc numbered 2 (+ (get numbered 2) 1)) - (.get explicit-mapping - version - (.join "." (map str numbered))))))) + (let [match (re.search r"Changes from.*(\d+\.\d+\.\d+)$" line)] + (if match + (let [version (.group match (int 1)) + numbered (list (map int (.split version "."))) + explicit-mapping {"0.9.12" "0.10.0" + "0.8.2" "0.9.0"}] + (assoc numbered 2 (+ (get numbered 2) 1)) + (.get explicit-mapping + version + (.join "." (map str numbered))))))) (defn read-version-content [reader] @@ -36,14 +36,14 @@ (defn read-versions-from-file [filename] - (let [[reader (read-lines-from-file filename)]] - (read-versions-rec (reader) - reader))) + (let [reader (read-lines-from-file filename)] + (read-versions-rec (reader) + reader))) (defn read-versions-rec [line reader] (if line - (let [[version (get-version-number line)] - [[content next-line] (read-version-content reader)]] + (let [version (get-version-number line) + [content next-line] (read-version-content reader)] (+ [{"from" version "content" content}] @@ -61,6 +61,6 @@ (defmain [&rest args] - (let ((versions (read-versions-from-file "NEWS"))) + (let [versions (read-versions-from-file "NEWS")] (for [version versions] - (print (.encode (format-deb-version version) "utf-8"))))) + (print (.encode (format-deb-version version) "utf-8"))))) diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index 8660a11..adb0fa6 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -321,20 +321,18 @@ def test_ast_invalid_for(): def test_ast_valid_let(): "Make sure AST can compile valid let" - can_compile("(let [])") can_compile("(let [a b])") - can_compile("(let [[a 1]])") - can_compile("(let [[a 1] b])") + can_compile("(let [a 1])") + can_compile("(let [a 1 b nil])") def test_ast_invalid_let(): "Make sure AST can't compile invalid let" cant_compile("(let 1)") cant_compile("(let [1])") - cant_compile("(let [[a 1 2]])") - cant_compile("(let [[]])") - cant_compile("(let [[a]])") - cant_compile("(let [[1]])") + cant_compile("(let [a 1 2])") + cant_compile("(let [a])") + cant_compile("(let [1])") def test_ast_expression_basics(): diff --git a/tests/native_tests/contrib/anaphoric.hy b/tests/native_tests/contrib/anaphoric.hy index 175231d..9e0ddd9 100644 --- a/tests/native_tests/contrib/anaphoric.hy +++ b/tests/native_tests/contrib/anaphoric.hy @@ -55,7 +55,7 @@ [3 6 9]) (assert-equal (list (ap-map (* it 3) [])) []) - (assert-equal (let [[v 1] [f 1]] (list (ap-map (it v f) [(fn [a b] (+ a b))]))) + (assert-equal (let [v 1 f 1] (list (ap-map (it v f) [(fn [a b] (+ a b))]))) [2])) (defn test-ap-map-when [] @@ -79,9 +79,9 @@ (defn test-ap-dotimes [] "NATIVE: testing anaphoric dotimes" - (assert-equal (let [[n []]] (ap-dotimes 3 (.append n 3)) n) + (assert-equal (let [n []] (ap-dotimes 3 (.append n 3)) n) [3 3 3]) - (assert-equal (let [[n []]] (ap-dotimes 3 (.append n it)) n) + (assert-equal (let [n []] (ap-dotimes 3 (.append n it)) n) [0 1 2])) (defn test-ap-first [] diff --git a/tests/native_tests/contrib/meth.hy b/tests/native_tests/contrib/meth.hy index d07adeb..e1d42da 100644 --- a/tests/native_tests/contrib/meth.hy +++ b/tests/native_tests/contrib/meth.hy @@ -9,44 +9,44 @@ f))) (defn test_route [] - (let [[app (FakeMeth)]] + (let [app (FakeMeth)] (route get-index "/" [] (str "Hy world!")) (setv app-rules (getattr app "rules")) (assert (in "/" app-rules)) - (let [[(, rule-fun rule-opt) (get app-rules "/")]] + (let [(, rule-fun rule-opt) (get app-rules "/")] (assert (not (empty? rule-opt))) (assert (in "GET" (get rule-opt "methods"))) (assert (= (getattr rule-fun "__name__") "get_index")) (assert (= "Hy world!" (rule-fun)))))) (defn test_post_route [] - (let [[app (FakeMeth)]] + (let [app (FakeMeth)] (post-route get-index "/" [] (str "Hy world!")) (setv app-rules (getattr app "rules")) (assert (in "/" app-rules)) - (let [[(, rule-fun rule-opt) (get app-rules "/")]] + (let [(, rule-fun rule-opt) (get app-rules "/")] (assert (not (empty? rule-opt))) (assert (in "POST" (get rule-opt "methods"))) (assert (= (getattr rule-fun "__name__") "get_index")) (assert (= "Hy world!" (rule-fun)))))) (defn test_put_route [] - (let [[app (FakeMeth)]] + (let [app (FakeMeth)] (put-route get-index "/" [] (str "Hy world!")) (setv app-rules (getattr app "rules")) (assert (in "/" app-rules)) - (let [[(, rule-fun rule-opt) (get app-rules "/")]] + (let [(, rule-fun rule-opt) (get app-rules "/")] (assert (not (empty? rule-opt))) (assert (in "PUT" (get rule-opt "methods"))) (assert (= (getattr rule-fun "__name__") "get_index")) (assert (= "Hy world!" (rule-fun)))))) (defn test_delete_route [] - (let [[app (FakeMeth)]] + (let [app (FakeMeth)] (delete-route get-index "/" [] (str "Hy world!")) (setv app-rules (getattr app "rules")) (assert (in "/" app-rules)) - (let [[(, rule-fun rule-opt) (get app-rules "/")]] + (let [(, rule-fun rule-opt) (get app-rules "/")] (assert (not (empty? rule-opt))) (assert (in "DELETE" (get rule-opt "methods"))) (assert (= (getattr rule-fun "__name__") "get_index")) diff --git a/tests/native_tests/contrib/walk.hy b/tests/native_tests/contrib/walk.hy index b282fda..18b43b2 100644 --- a/tests/native_tests/contrib/walk.hy +++ b/tests/native_tests/contrib/walk.hy @@ -15,17 +15,17 @@ walk-form))) (defn test-walk [] - (let [[acc '()]] + (let [acc '()] (assert (= (walk (partial collector acc) identity walk-form) [nil nil])) (assert (= acc walk-form))) - (let [[acc []]] + (let [acc []] (assert (= (walk identity (partial collector acc) walk-form) nil)) (assert (= acc [walk-form])))) (defn test-walk-iterators [] - (let [[acc []]] + (let [acc []] (assert (= (walk (fn [x] (* 2 x)) (fn [x] x) (drop 1 [1 [2 [3 [4]]]])) [[2 [3 [4]] 2 [3 [4]]]])))) diff --git a/tests/native_tests/core.hy b/tests/native_tests/core.hy index 4c8d0a3..9e3ad52 100644 --- a/tests/native_tests/core.hy +++ b/tests/native_tests/core.hy @@ -592,7 +592,7 @@ (setv res (list (take-nth 3 [1 2 3 None 5 6]))) (assert-equal res [1 None]) ;; using 0 should raise ValueError - (let [[passed false]] + (let [passed false] (try (setv res (list (take-nth 0 [1 2 3 4 5 6 7]))) (except [ValueError] (setv passed true))) diff --git a/tests/native_tests/defclass.hy b/tests/native_tests/defclass.hy index 84b1dc6..8be6d57 100644 --- a/tests/native_tests/defclass.hy +++ b/tests/native_tests/defclass.hy @@ -36,7 +36,7 @@ (+ self.x value))]) (assert (= B.x 42)) (assert (= (.y (B) 5) 47)) - (let [[b (B)]] + (let [b (B)] (setv B.x 0) (assert (= (.y b 1) 1)))) diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index 126dd57..c4f29d3 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -355,7 +355,7 @@ (try (do) (except [IOError]) (except)) ;; Test correct (raise) - (let [[passed false]] + (let [passed false] (try (try (raise IndexError) @@ -365,7 +365,7 @@ (assert passed)) ;; Test incorrect (raise) - (let [[passed false]] + (let [passed false] (try (raise) ;; Python 2 raises TypeError @@ -374,16 +374,15 @@ (setv passed true))) (assert passed)) - ;; Test (finally) - (let [[passed false]] + (let [passed false] (try (do) (finally (setv passed true))) (assert passed)) ;; Test (finally) + (raise) - (let [[passed false]] + (let [passed false] (try (raise Exception) (except) @@ -392,8 +391,8 @@ ;; Test (finally) + (raise) + (else) - (let [[passed false] - [not-elsed true]] + (let [passed false + not-elsed true] (try (raise Exception) (except) @@ -460,13 +459,13 @@ (setv foobar42ofthebaz 42) (assert (= foobar42ofthebaz 42)))) - (let [[passed false]] + (let [passed false] (try (try (do) (except) (else (bla))) (except [NameError] (setv passed true))) (assert passed)) - (let [[x 0]] + (let [x 0] (try (raise IOError) (except [IOError] @@ -474,7 +473,7 @@ (else (setv x 44))) (assert (= x 45))) - (let [[x 0]] + (let [x 0] (try (raise KeyError) (except [] @@ -482,7 +481,7 @@ (else (setv x 44))) (assert (= x 45))) - (let [[x 0]] + (let [x 0] (try (try (raise KeyError) @@ -561,7 +560,7 @@ (defn test-yield-in-try [] "NATIVE: test yield in try" (defn gen [] - (let [[x 1]] + (let [x 1] (try (yield x) (finally (print x))))) (setv output (list (gen))) @@ -631,13 +630,13 @@ (defn test-for-else [] "NATIVE: test for else" - (let [[x 0]] + (let [x 0] (for* [a [1 2]] (setv x (+ x a)) (else (setv x (+ x 50)))) (assert (= x 53))) - (let [[x 0]] + (let [x 0] (for* [a [1 2]] (setv x (+ x a)) (else)) @@ -751,28 +750,28 @@ (defn test-let [] "NATIVE: test let works rightish" ;; TODO: test sad paths for let - (assert (= (let [[x 1] [y 2] [z 3]] (+ x y z)) 6)) - (assert (= (let [[x 1] a [y 2] b] (if a 1 2)) 2)) - (assert (= (let [x] x) nil)) - (assert (= (let [[x "x not bound"]] (setv x "x bound by setv") x) + (assert (= (let [x 1 y 2 z 3] (+ x y z)) 6)) + (assert (= (let [x 1 a nil y 2 b nil] (if a 1 2)) 2)) + (assert (= (let [x nil] x) nil)) + (assert (= (let [x "x not bound"] (setv x "x bound by setv") x) "x bound by setv")) - (assert (= (let [[x "let nests scope correctly"]] - (let [y] x)) + (assert (= (let [x "let nests scope correctly"] + (let [y nil] x)) "let nests scope correctly")) - (assert (= (let [[x 999999]] - (let [[x "x being rebound"]] x)) + (assert (= (let [x 999999] + (let [x "x being rebound"] x)) "x being rebound")) - (assert (= (let [[x "x not being rebound"]] - (let [[x 2]] nil) + (assert (= (let [x "x not being rebound"] + (let [x 2] nil) x) "x not being rebound")) - (assert (= (let [[x (set [3 2 1 3 2])] [y x] [z y]] z) (set [1 2 3]))) + (assert (= (let [x (set [3 2 1 3 2]) y x z y] z) (set [1 2 3]))) (import math) - (let [[cos math.cos] - [foo-cos (fn [x] (cos x))]] + (let [cos math.cos + foo-cos (fn [x] (cos x))] (assert (= (cos math.pi) -1.0)) (assert (= (foo-cos (- math.pi)) -1.0)) - (let [[cos (fn [_] "cos has been locally rebound")]] + (let [cos (fn [_] "cos has been locally rebound")] (assert (= (cos cos) "cos has been locally rebound")) (assert (= (-> math.pi (/ 3) foo-cos (round 2)) 0.5))) (setv cos (fn [_] "cos has been rebound by setv")) @@ -792,9 +791,9 @@ (defn test-let-scope [] "NATIVE: test let works rightish" (setv y 123) - (assert (= (let [[x 1] - [y 2] - [z 3]] + (assert (= (let [x 1 + y 2 + z 3] (+ x y z)) 6)) (try @@ -805,31 +804,31 @@ (defn test-symbol-utf-8 [] "NATIVE: test symbol encoded" - (let [[♥ "love"] - [⚘ "flower"]] + (let [♥ "love" + ⚘ "flower"] (assert (= (+ ⚘ ♥) "flowerlove")))) (defn test-symbol-dash [] "NATIVE: test symbol encoded" - (let [[♥-♥ "doublelove"] - [-_- "what?"]] + (let [♥-♥ "doublelove" + -_- "what?"] (assert (= ♥-♥ "doublelove")) (assert (= -_- "what?")))) (defn test-symbol-question-mark [] "NATIVE: test foo? -> is_foo behavior" - (let [[foo? "nachos"]] + (let [foo? "nachos"] (assert (= is_foo "nachos")))) (defn test-and [] "NATIVE: test the and function" - (let [[and123 (and 1 2 3)] - [and-false (and 1 False 3)] - [and-true (and)] - [and-single (and 1)]] + (let [and123 (and 1 2 3) + and-false (and 1 False 3) + and-true (and) + and-single (and 1)] (assert (= and123 3)) (assert (= and-false False)) (assert (= and-true True)) @@ -842,11 +841,11 @@ (defn test-or [] "NATIVE: test the or function" - (let [[or-all-true (or 1 2 3 True "string")] - [or-some-true (or False "hello")] - [or-none-true (or False False)] - [or-false (or)] - [or-single (or 1)]] + (let [or-all-true (or 1 2 3 True "string") + or-some-true (or False "hello") + or-none-true (or False False) + or-false (or) + or-single (or 1)] (assert (= or-all-true 1)) (assert (= or-some-true "hello")) (assert (= or-none-true False)) @@ -861,12 +860,12 @@ (defn test-if-return-branching [] "NATIVE: test the if return branching" ; thanks, algernon - (assert (= 1 (let [[x 1] - [y 2]] + (assert (= 1 (let [x 1 + y 2] (if true 2) 1))) - (assert (= 1 (let [[x 1] [y 2]] + (assert (= 1 (let [x 1 y 2] (do) (do) ((fn [] 1)))))) @@ -915,9 +914,9 @@ (defn test-eval-globals [] "NATIVE: test eval with explicit global dict" (assert (= 'bar (eval (quote foo) {'foo 'bar}))) - (assert (= 1 (let [[d {}]] (eval '(setv x 1) d) (eval (quote x) d)))) - (let [[d1 {}] - [d2 {}]] + (assert (= 1 (let [d {}] (eval '(setv x 1) d) (eval (quote x) d)))) + (let [d1 {} + d2 {}] (eval '(setv x 1) d1) (try (do @@ -998,7 +997,7 @@ (defn test-if-let-mixing [] "NATIVE: test that we can now mix if and let" - (assert (= 0 (if true (let [[x 0]] x) 42)))) + (assert (= 0 (if true (let [x 0] x) 42)))) (defn test-if-in-if [] "NATIVE: test that we can use if in if" diff --git a/tests/native_tests/mathematics.hy b/tests/native_tests/mathematics.hy index dfe41ae..58c5457 100644 --- a/tests/native_tests/mathematics.hy +++ b/tests/native_tests/mathematics.hy @@ -69,73 +69,73 @@ (defn test-augassign-add [] "NATIVE: test augassign add" - (let [[x 1]] + (let [x 1] (+= x 41) (assert (= x 42)))) (defn test-augassign-sub [] "NATIVE: test augassign sub" - (let [[x 1]] + (let [x 1] (-= x 41) (assert (= x -40)))) (defn test-augassign-mult [] "NATIVE: test augassign mult" - (let [[x 1]] + (let [x 1] (*= x 41) (assert (= x 41)))) (defn test-augassign-div [] "NATIVE: test augassign div" - (let [[x 42]] + (let [x 42] (/= x 2) (assert (= x 21)))) (defn test-augassign-floordiv [] "NATIVE: test augassign floordiv" - (let [[x 42]] + (let [x 42] (//= x 2) (assert (= x 21)))) (defn test-augassign-mod [] "NATIVE: test augassign mod" - (let [[x 42]] + (let [x 42] (%= x 2) (assert (= x 0)))) (defn test-augassign-pow [] "NATIVE: test augassign pow" - (let [[x 2]] + (let [x 2] (**= x 3) (assert (= x 8)))) (defn test-augassign-lshift [] "NATIVE: test augassign lshift" - (let [[x 2]] + (let [x 2] (<<= x 2) (assert (= x 8)))) (defn test-augassign-rshift [] "NATIVE: test augassign rshift" - (let [[x 8]] + (let [x 8] (>>= x 1) (assert (= x 4)))) (defn test-augassign-bitand [] "NATIVE: test augassign bitand" - (let [[x 8]] + (let [x 8] (&= x 1) (assert (= x 0)))) (defn test-augassign-bitor [] "NATIVE: test augassign bitand" - (let [[x 0]] + (let [x 0] (|= x 2) (assert (= x 2)))) (defn test-augassign-bitxor [] "NATIVE: test augassign bitand" - (let [[x 1]] + (let [x 1] (^= x 1) (assert (= x 0)))) @@ -147,13 +147,13 @@ (defclass HyTestMatrix [list] [--matmul-- (fn [self other] - (let [[n (len self)] - [m (len (. other [0]))] - [result []]] + (let [n (len self) + m (len (. other [0])) + result []] (for [i (range m)] - (let [[result-row []]] + (let [result-row []] (for [j (range n)] - (let [[dot-product 0]] + (let [dot-product 0] (for [k (range (len (. self [0])))] (+= dot-product (* (. self [i] [k]) (. other [k] [j])))) @@ -179,15 +179,15 @@ (assert (= (@ first-test-matrix second-test-matrix) product-of-test-matrices)) ;; Python <= 3.4 - (let [[matmul-attempt (try (@ first-test-matrix second-test-matrix) - (except [e [Exception]] e))]] + (let [matmul-attempt (try (@ first-test-matrix second-test-matrix) + (except [e [Exception]] e))] (assert (isinstance matmul-attempt NameError))))) (defn test-augassign-matmul [] "NATIVE: test augmented-assignment matrix multiplication" - (let [[matrix first-test-matrix] - [matmul-attempt (try (@= matrix second-test-matrix) - (except [e [Exception]] e))]] + (let [matrix first-test-matrix + matmul-attempt (try (@= matrix second-test-matrix) + (except [e [Exception]] e))] (if PY35 (assert (= product-of-test-matrices matrix)) (assert (isinstance matmul-attempt NameError))))) diff --git a/tests/native_tests/native_macros.hy b/tests/native_tests/native_macros.hy index d16ea1d..0d10609 100644 --- a/tests/native_tests/native_macros.hy +++ b/tests/native_tests/native_macros.hy @@ -107,8 +107,8 @@ (import [astor.codegen [to_source]]) (import [hy.importer [import_buffer_to_ast]]) (setv macro1 "(defmacro nif [expr pos zero neg] - (let [[g (gensym)]] - `(let [[~g ~expr]] + (let [g (gensym)] + `(let [~g ~expr] (cond [(pos? ~g) ~pos] [(zero? ~g) ~zero] [(neg? ~g) ~neg])))) @@ -133,7 +133,7 @@ (import [hy.importer [import_buffer_to_ast]]) (setv macro1 "(defmacro nif [expr pos zero neg] (with-gensyms [a] - `(let [[~a ~expr]] + `(let [~a ~expr] (cond [(pos? ~a) ~pos] [(zero? ~a) ~zero] [(neg? ~a) ~neg])))) @@ -155,7 +155,7 @@ (import [astor.codegen [to_source]]) (import [hy.importer [import_buffer_to_ast]]) (setv macro1 "(defmacro/g! nif [expr pos zero neg] - `(let [[~g!res ~expr]] + `(let [~g!res ~expr] (cond [(pos? ~g!res) ~pos] [(zero? ~g!res) ~zero] [(neg? ~g!res) ~neg]))) diff --git a/tests/native_tests/py3_only_tests.hy b/tests/native_tests/py3_only_tests.hy index a6e359b..5626296 100644 --- a/tests/native_tests/py3_only_tests.hy +++ b/tests/native_tests/py3_only_tests.hy @@ -15,14 +15,14 @@ (defn test-kwonly [] "NATIVE: test keyword-only arguments" ;; keyword-only with default works - (let [[kwonly-foo-default-false (fn [&kwonly [foo false]] foo)]] + (let [kwonly-foo-default-false (fn [&kwonly [foo false]] foo)] (assert (= (apply kwonly-foo-default-false) false)) (assert (= (apply kwonly-foo-default-false [] {"foo" true}) true))) ;; keyword-only without default ... - (let [[kwonly-foo-no-default (fn [&kwonly foo] foo)] - [attempt-to-omit-default (try - (kwonly-foo-no-default) - (except [e [Exception]] e))]] + (let [kwonly-foo-no-default (fn [&kwonly foo] foo) + attempt-to-omit-default (try + (kwonly-foo-no-default) + (except [e [Exception]] e))] ;; works (assert (= (apply kwonly-foo-no-default [] {"foo" "quux"}) "quux")) ;; raises TypeError with appropriate message if not supplied @@ -30,9 +30,9 @@ (assert (in "missing 1 required keyword-only argument: 'foo'" (. attempt-to-omit-default args [0])))) ;; keyword-only with other arg types works - (let [[function-of-various-args - (fn [a b &rest args &kwonly foo &kwargs kwargs] - (, a b args foo kwargs))]] + (let [function-of-various-args + (fn [a b &rest args &kwonly foo &kwargs kwargs] + (, a b args foo kwargs))] (assert (= (apply function-of-various-args [1 2 3 4] {"foo" 5 "bar" 6 "quux" 7}) (, 1 2 (, 3 4) 5 {"bar" 6 "quux" 7}))))) diff --git a/tests/native_tests/shadow.hy b/tests/native_tests/shadow.hy index 0a89f46..0ca08ba 100644 --- a/tests/native_tests/shadow.hy +++ b/tests/native_tests/shadow.hy @@ -1,6 +1,6 @@ (defn test-shadow-addition [] "NATIVE: test shadow addition" - (let [[x +]] + (let [x +] (assert (try (x) (except [TypeError] True) @@ -21,7 +21,7 @@ (defn test-shadow-subtraction [] "NATIVE: test shadow subtraction" - (let [[x -]] + (let [x -] (assert (try (x) (except [TypeError] True) @@ -33,7 +33,7 @@ (defn test-shadow-multiplication [] "NATIVE: test shadow multiplication" - (let [[x *]] + (let [x *] (assert (= (x) 1)) (assert (= (x 3) 3)) (assert (= (x 3 3) 9)))) @@ -41,7 +41,7 @@ (defn test-shadow-division [] "NATIVE: test shadow division" - (let [[x /]] + (let [x /] (assert (try (x) (except [TypeError] True) @@ -71,12 +71,12 @@ [1 1] [2 2]]] (assert (= (apply x args) (not (apply y args)))))) - (let [[s-lt <] - [s-gt >] - [s-le <=] - [s-ge >=] - [s-eq =] - [s-ne !=]] + (let [s-lt < + s-gt > + s-le <= + s-ge >= + s-eq = + s-ne !=] (assert (apply s-lt [1 2 3])) (assert (not (apply s-lt [3 2 1]))) (assert (apply s-gt [3 2 1])) From 32f5d5dea77b40297f45df8c051fcddedbc33524 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Thu, 13 Aug 2015 16:37:56 +0200 Subject: [PATCH 53/53] Drop a set of brackets from with. This changes with syntax from (with [[x (expr)] (expr)] ...) to (with [x (expr) (expr)] ...). Should have no ill side effects apart from the syntax change. Closes #852. Signed-off-by: Gergely Nagy --- docs/language/api.rst | 8 +++--- docs/language/core.rst | 2 +- docs/tutorial.rst | 2 +- hy/core/macros.hy | 20 +++++++------- scripts/update-coreteam.hy | 2 +- tests/native_tests/contrib/walk.hy | 4 +-- tests/native_tests/language.hy | 6 ++--- tests/native_tests/with_test.hy | 42 +++++++++++++++--------------- 8 files changed, 43 insertions(+), 43 deletions(-) diff --git a/docs/language/api.rst b/docs/language/api.rst index d44e0d4..91bf707 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -1363,18 +1363,18 @@ manner. The archetypical example of using ``with`` is when processing files. .. code-block:: clj - (with [[arg (expr)]] block) + (with [arg (expr)] block) - (with [[(expr)]] block) + (with [(expr)] block) - (with [[arg (expr)] [(expr)]] block) + (with [arg (expr) (expr)] block) The following example will open the ``NEWS`` file and print its content to the screen. The file is automatically closed after it has been processed. .. code-block:: clj - (with [[f (open "NEWS")]] (print (.read f))) + (with [f (open "NEWS")] (print (.read f))) with-decorator diff --git a/docs/language/core.rst b/docs/language/core.rst index 6890684..58b00d9 100644 --- a/docs/language/core.rst +++ b/docs/language/core.rst @@ -1100,7 +1100,7 @@ if *from-file* ends before a complete expression can be parsed. => ; assuming "example.hy" contains: => ; (print "hello") => ; (print "hyfriends!") - => (with [[f (open "example.hy")]] + => (with [f (open "example.hy")] ... (try ... (while true ... (let [exp (read f)] diff --git a/docs/tutorial.rst b/docs/tutorial.rst index 1eedea1..fc5e3ff 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -335,7 +335,7 @@ Python's context managers (``with`` statements) are used like this: .. code-block:: clj - (with [[f (open "/tmp/data.in")]] + (with [f (open "/tmp/data.in")] (print (.read f))) which is equivalent to:: diff --git a/hy/core/macros.hy b/hy/core/macros.hy index 9eb1521..54446e9 100644 --- a/hy/core/macros.hy +++ b/hy/core/macros.hy @@ -33,21 +33,21 @@ (defmacro with [args &rest body] "shorthand for nested with* loops: - (with [[x foo] [y bar]] baz) -> + (with [x foo y bar] baz) -> (with* [x foo] (with* [y bar] baz))" (if (not (empty? args)) - (let [primary (.pop args 0)] - (if (isinstance primary HyList) - ;;; OK. if we have a list, we can go ahead and unpack that - ;;; as the argument to with. - `(with* [~@primary] (with ~args ~@body)) - ;;; OK, let's just give it away. This may not be something we - ;;; can do, but that's really the programmer's problem. - `(with* [~primary] (with ~args ~@body)))) - `(do ~@body))) + (do + (if (>= (len args) 2) + (do + (setv p1 (.pop args 0) + p2 (.pop args 0) + primary [p1 p2]) + `(with* [~@primary] (with ~args ~@body))) + `(with* [~@args] ~@body))) + `(do ~@body))) (defmacro car [thing] diff --git a/scripts/update-coreteam.hy b/scripts/update-coreteam.hy index 394bbd8..e817aea 100644 --- a/scripts/update-coreteam.hy +++ b/scripts/update-coreteam.hy @@ -33,5 +33,5 @@ (setv filename (os.path.abspath (os.path.join os.path.pardir "docs" "coreteam.rst"))) -(with [[fobj (open filename "w+")]] +(with [fobj (open filename "w+")] (fobj.write (+ (.join "\n" result) "\n"))) diff --git a/tests/native_tests/contrib/walk.hy b/tests/native_tests/contrib/walk.hy index 18b43b2..d853a45 100644 --- a/tests/native_tests/contrib/walk.hy +++ b/tests/native_tests/contrib/walk.hy @@ -31,5 +31,5 @@ [[2 [3 [4]] 2 [3 [4]]]])))) (defn test-macroexpand-all [] - (assert (= (macroexpand-all '(with [a b c] (for [d c] foo))) - '(with* [a] (with* [b] (with* [c] (do (for* [d c] (do foo))))))))) + (assert (= (macroexpand-all '(with [a 1 b 2 c 3] (for [d c] foo))) + '(with* [a 1] (with* [b 2] (with* [c 3] (do (for* [d c] (do foo))))))))) diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index c4f29d3..7821e8d 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -607,14 +607,14 @@ (defn test-context [] "NATIVE: test with" - (with [[fd (open "README.md" "r")]] (assert fd)) - (with [[(open "README.md" "r")]] (do))) + (with [fd (open "README.md" "r")] (assert fd)) + (with [(open "README.md" "r")] (do))) (defn test-with-return [] "NATIVE: test that with returns stuff" (defn read-file [filename] - (with [[fd (open filename "r")]] (.read fd))) + (with [fd (open filename "r")] (.read fd))) (assert (!= 0 (len (read-file "README.md"))))) diff --git a/tests/native_tests/with_test.hy b/tests/native_tests/with_test.hy index 7b51923..fd9933e 100644 --- a/tests/native_tests/with_test.hy +++ b/tests/native_tests/with_test.hy @@ -11,31 +11,31 @@ (defn test-single-with [] "NATIVE: test a single with" - (with [[t (WithTest 1)]] - (assert (= t 1)))) + (with [t (WithTest 1)] + (assert (= t 1)))) (defn test-two-with [] "NATIVE: test two withs" - (with [[t1 (WithTest 1)] - [t2 (WithTest 2)]] - (assert (= t1 1)) - (assert (= t2 2)))) + (with [t1 (WithTest 1) + t2 (WithTest 2)] + (assert (= t1 1)) + (assert (= t2 2)))) (defn test-thrice-with [] "NATIVE: test three withs" - (with [[t1 (WithTest 1)] - [t2 (WithTest 2)] - [t3 (WithTest 3)]] - (assert (= t1 1)) - (assert (= t2 2)) - (assert (= t3 3)))) + (with [t1 (WithTest 1) + t2 (WithTest 2) + t3 (WithTest 3)] + (assert (= t1 1)) + (assert (= t2 2)) + (assert (= t3 3)))) -(defn test-quince-with [] - "NATIVE: test four withs, one with no args" - (with [[t1 (WithTest 1)] - [t2 (WithTest 2)] - [t3 (WithTest 3)] - [(WithTest 4)]] - (assert (= t1 1)) - (assert (= t2 2)) - (assert (= t3 3)))) + (defn test-quince-with [] + "NATIVE: test four withs, one with no args" + (with [t1 (WithTest 1) + t2 (WithTest 2) + t3 (WithTest 3) + _ (WithTest 4)] + (assert (= t1 1)) + (assert (= t2 2)) + (assert (= t3 3))))