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 diff --git a/.gitignore b/.gitignore index e46a222..d3a4b8c 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ dist .coverage build/ +.noseids 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 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/README.md b/README.md index f23d078..54ab956 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,8 @@ Project ------- * Code: https://github.com/hylang/hy -* Docs: http://hylang.org/ +* 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) diff --git a/docs/contrib/alias.rst b/docs/contrib/alias.rst new file mode 100644 index 0000000..eb610cf --- /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`` 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/anaphoric.rst b/docs/contrib/anaphoric.rst index 750a470..db36ffc 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 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/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/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 3c0cd68..0594dec 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 ========= @@ -63,10 +78,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). -> @@ -100,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])`` @@ -127,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 --- @@ -194,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] @@ -267,11 +286,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 @@ -289,10 +307,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. @@ -357,7 +375,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 +385,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") @@ -378,10 +399,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: @@ -442,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) @@ -465,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)))) @@ -488,28 +509,6 @@ Parameters may have the following keywords in front of them: Availability: Python 3. -.. _defn-alias / defun-alias: - -defn-alias / defun-alias ------------------------- - -.. versionadded:: 0.10.0 - -The ``defn-alias`` and ``defun-alias`` macros are 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``. - -.. code-block:: clj - - => (defn-alias [main-name alias] [] - ... (print "Hello!")) - => (main-name) - "Hello!" - => (alias) - "Hello!" - defmain ------- @@ -571,31 +570,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!: @@ -812,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 @@ -835,7 +809,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 @@ -881,47 +855,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 ``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 @@ -1022,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 @@ -1084,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) @@ -1275,45 +1235,45 @@ 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 ``raise`` form 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``. 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. @@ -1403,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 @@ -1501,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 dd6d8f3..58b00d9 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: @@ -671,21 +690,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: @@ -1065,14 +1100,14 @@ 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)]] + ... (let [exp (read f)] ... (do ... (print "OHY" exp) ... (eval exp)))) - ... (catch [e EOFError] + ... (except [e EOFError] ... (print "EOF!")))) OHY ('print' 'hello') hello @@ -1218,21 +1253,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/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 4222805..2e3bd12 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] @@ -105,8 +105,8 @@ Layout & Indentation .. code-block:: clj - (let [[foo (bar)] - [qux (baz)]] + (let [foo (bar)] + qux (baz)] (foo qux)) diff --git a/docs/tutorial.rst b/docs/tutorial.rst index 6b32b30..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:: @@ -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/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/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/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/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/hy/compiler.py b/hy/compiler.py index f3ddb81..4574a3f 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) @@ -81,7 +83,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 @@ -370,6 +372,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) @@ -459,8 +462,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[1:]))) compiled_value = self.compile(value) ret += compiled_value @@ -728,15 +732,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)) @@ -748,15 +748,13 @@ 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) - @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: @@ -828,7 +826,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"): @@ -906,7 +904,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])) @@ -1300,9 +1297,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 = [] @@ -1476,9 +1479,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( @@ -1632,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( @@ -1972,16 +1996,22 @@ 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) 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 = [] @@ -2000,13 +2030,18 @@ 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) 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: @@ -2134,7 +2169,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) @@ -2238,15 +2274,31 @@ class HyASTCompiler(object): @builds("defclass") @checkargs(min=1) - def compile_class_expression(self, expression): - expression.pop(0) # class + 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) - class_name = expression.pop(0) + return expr - if expression: - base_list = expression.pop(0) + expressions.pop(0) # class + + class_name = expressions.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 +2308,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 +2318,29 @@ 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() + 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) + expr = HyExpression([ + HySymbol("setv") + ] + expr).replace(expr) + body += self.compile(rewire_init(expr)) + + for expression in expressions: + expr = rewire_init(macroexpand(expression, self.module_name)) + body += self.compile(expr) + + self.allow_builtins = allow_builtins 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=[], @@ -2373,7 +2423,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) @@ -2382,7 +2432,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/alias.hy b/hy/contrib/alias.hy new file mode 100644 index 0000000..56c4a7c --- /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/contrib/anaphoric.hy b/hy/contrib/anaphoric.hy index 628ae9d..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,9 +116,33 @@ "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] "Returns a function which is the composition of several forms." `(fn [var] (ap-pipe var ~@forms))) + +(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. + 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 (+ (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) + '())] + (~@body))) + diff --git a/hy/contrib/botsbuildbots.hy b/hy/contrib/botsbuildbots.hy index 77742bd..fcea8c6 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." @@ -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))) - (catch [e ImportError] + (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 2c2690a..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 @@ -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) @@ -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 e4b9bd0..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 - (progn ~@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 741e354..6667ed8 100644 --- a/hy/core/bootstrap.hy +++ b/hy/core/bootstrap.hy @@ -31,42 +31,25 @@ `(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-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))) (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 b04c115..361abe5 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]]) @@ -83,49 +84,82 @@ (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 - (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 + remove 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 + remove 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 + 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))])))) @@ -177,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) @@ -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" @@ -222,16 +256,16 @@ "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." - (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" @@ -277,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" @@ -314,9 +349,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" @@ -355,18 +398,19 @@ (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 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" @@ -381,7 +425,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))) @@ -389,18 +433,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) "_" "-")) @@ -414,7 +450,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 +461,7 @@ (hyify value) (try (hyify (. value __name__)) - (catch [] (string value)))))) + (except [] (string value)))))) (defn xor [a b] "Perform exclusive or between two parameters" @@ -433,11 +469,12 @@ (and b (not a)))) (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 xor 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 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 xor tee zero? zip zip-longest]) diff --git a/hy/core/macros.hy b/hy/core/macros.hy index 3f58683..54446e9 100644 --- a/hy/core/macros.hy +++ b/hy/core/macros.hy @@ -32,22 +32,22 @@ (defmacro with [args &rest body] - "shorthand for nested for* loops: - (with [[x foo] [y bar]] baz) -> + "shorthand for nested with* loops: + (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] @@ -67,25 +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 two items: a test and a code branch")) - (setv (, test thebranch) branch) - `(if ~test ~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] @@ -96,16 +100,23 @@ (for* [x foo] (for* [y bar] baz))" - (cond + (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)] + [])) + (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 - (let [[alist (cut args 0 nil 2)]] - `(for* [(, ~@alist) (genexpr (, ~@alist) [~@args])] ~@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])] (do ~@body) ~@belse))])) (defmacro -> [head &rest rest] @@ -127,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)) @@ -149,11 +160,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)) @@ -169,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 @@ -189,7 +211,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)) @@ -200,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)) @@ -210,17 +232,7 @@ (sys.exit ~retval))))) -(defmacro-alias [defn-alias defun-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)]] + (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/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/hy/importer.py b/hy/importer.py index 7c8cb2f..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): @@ -108,6 +109,10 @@ def hy_eval(hytree, namespace, module_name): foo.start_column = 0 foo.end_column = 0 replace_hy_obj(hytree, foo) + + 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) # Spoof the positions in the generated ast... @@ -119,6 +124,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) 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 diff --git a/hy/lex/parser.py b/hy/lex/parser.py index a63be3e..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): @@ -289,7 +305,6 @@ def t_identifier(p): "true": "True", "false": "False", "nil": "None", - "null": "None", } if obj in table: @@ -298,19 +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]) - - 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/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/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/scripts/reformat-changelog b/scripts/reformat-changelog index 2c74480..899a9fa 100755 --- a/scripts/reformat-changelog +++ b/scripts/reformat-changelog @@ -7,26 +7,26 @@ (setv *maintainer-line* " -- Alexander Artemenko Thu, 30 Sep 2014 13:06:09 +0400") -(defun read-lines-from-file [filename] - (let [[f (codecs.open filename "r" "utf-8")]] - (fn [] (let [[line (.readline f) ]] - line)))) +(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] - (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 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))))))) -(defun read-version-content [reader] +(defn read-version-content [reader] (setv line (reader)) (setv content []) (while (and line (not (get-version-number line))) @@ -35,22 +35,22 @@ [content line]) -(defun read-versions-from-file [filename] - (let [[reader (read-lines-from-file filename)]] - (read-versions-rec (reader) - reader))) +(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)]] + (let [version (get-version-number line) + [content next-line] (read-version-content reader)] (+ [{"from" version "content" content}] (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")] @@ -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/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/compilers/test_ast.py b/tests/compilers/test_ast.py index 0931840..adb0fa6 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)") @@ -160,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))") @@ -250,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(): @@ -352,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(): @@ -470,6 +437,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 :x needs a value.") + else: + assert(False) + + def test_ast_unicode_strings(): """Ensure we handle unicode strings correctly""" @@ -515,7 +492,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: @@ -528,6 +505,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""" @@ -543,3 +527,39 @@ 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)") + + +def test_bad_setv(): + """Ensure setv handles error cases""" + cant_compile("(setv if 1)") + cant_compile("(setv (a b) [1 2])") + + +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)") + + +def test_setv_builtins(): + """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)) + """) diff --git a/tests/lex/test_lex.py b/tests/lex/test_lex.py index cc95675..4413a6b 100644 --- a/tests/lex/test_lex.py +++ b/tests/lex/test_lex.py @@ -98,8 +98,14 @@ 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""" +"a\ +bc" +""") + assert objs == [HyString("abc")] def test_lex_integers(): @@ -326,6 +332,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] 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) 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/contrib/anaphoric.hy b/tests/native_tests/contrib/anaphoric.hy index 8b37905..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 [] @@ -113,3 +113,29 @@ "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)])]) + ;; 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)) 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/contrib/meth.hy b/tests/native_tests/contrib/meth.hy index 446bb81..e1d42da 100644 --- a/tests/native_tests/contrib/meth.hy +++ b/tests/native_tests/contrib/meth.hy @@ -2,52 +2,51 @@ (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)]] + (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 abd4360..d853a45 100644 --- a/tests/native_tests/contrib/walk.hy +++ b/tests/native_tests/contrib/walk.hy @@ -15,21 +15,21 @@ 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]]]])))) (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)))))))) + (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/core.hy b/tests/native_tests/core.hy index 7e73a4b..9e3ad52 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,23 +464,36 @@ (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" (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" @@ -488,11 +501,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 +568,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])) @@ -579,10 +592,10 @@ (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]))) - (catch [ValueError] (setv passed true))) + (except [ValueError] (setv passed true))) (assert passed))) (defn test-take-while [] @@ -596,14 +609,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 +634,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])) diff --git a/tests/native_tests/defclass.hy b/tests/native_tests/defclass.hy index badb74c..8be6d57 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,12 +31,12 @@ (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)]] + (let [b (B)] (setv B.x 0) (assert (= (.y b 1) 1)))) @@ -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,40 @@ (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")) + +(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))) diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index eafde20..5e94be2 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)))) @@ -34,6 +49,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]) @@ -43,25 +63,23 @@ (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))))) - (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,29 +90,46 @@ (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-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 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]] - (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))))) @@ -115,21 +150,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. @@ -224,7 +259,8 @@ "NATIVE: test if cond sorta works." (cond [(= 1 2) (assert (is true false))] - [(is null null) (assert (is true true))])) + [(is None None) (setv x true) (assert x)]) + (assert (= (cond) nil))) (defn test-index [] @@ -273,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 [] @@ -316,7 +355,7 @@ (try (do) (except [IOError]) (except)) ;; Test correct (raise) - (let [[passed false]] + (let [passed false] (try (try (raise IndexError) @@ -326,7 +365,7 @@ (assert passed)) ;; Test incorrect (raise) - (let [[passed false]] + (let [passed false] (try (raise) ;; Python 2 raises TypeError @@ -335,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) @@ -353,8 +391,8 @@ ;; Test (finally) + (raise) + (else) - (let [[passed false] - [not-elsed true]] + (let [passed false + not-elsed true] (try (raise Exception) (except) @@ -365,22 +403,22 @@ (try (raise (KeyError)) - (catch [[IOError]] (assert false)) - (catch [e [KeyError]] (assert e))) + (except [[IOError]] (assert false)) + (except [e [KeyError]] (assert e))) (try - (throw (KeyError)) + (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 +427,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 +435,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,17 +455,17 @@ (try (print foobar42ofthebaz) - (catch [] + (except [] (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] @@ -435,7 +473,7 @@ (else (setv x 44))) (assert (= x 45))) - (let [[x 0]] + (let [x 0] (try (raise KeyError) (except [] @@ -443,7 +481,7 @@ (else (setv x 44))) (assert (= x 45))) - (let [[x 0]] + (let [x 0] (try (try (raise KeyError) @@ -522,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))) @@ -569,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"))))) @@ -592,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)) @@ -669,6 +707,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 []) @@ -706,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")) @@ -747,44 +791,44 @@ (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 (assert (= x 42)) ; This ain't true - (catch [e [NameError]] (assert e))) + (except [e [NameError]] (assert e))) (assert (= y 123))) (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)) @@ -797,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)) @@ -815,9 +859,9 @@ (defn test-xor [] "NATIVE: test the xor macro" - (let [[xor-both-true (xor true true)] - [xor-both-false (xor false false)] - [xor-true-false (xor true false)]] + (let [xor-both-true (xor true true) + xor-both-false (xor false false) + xor-true-false (xor true false)] (assert (= xor-both-true false)) (assert (= xor-both-false false)) (assert (= xor-true-false true)))) @@ -825,12 +869,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)))))) @@ -876,40 +920,29 @@ (assert (= None (eval (quote (print "")))))) -(defmacro assert-throw [exc-type &rest body] - `(try - (do - (eval ~@body) - (assert False "we shouldn't have arrived here")) - (catch [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}))) - (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 ; 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 [] "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))) + ; 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 [] @@ -973,7 +1006,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" @@ -1000,7 +1033,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)))) @@ -1088,7 +1121,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 [] @@ -1168,7 +1202,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 [] @@ -1192,7 +1226,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..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)))) @@ -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] @@ -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) - (catch [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) - (catch [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 5ad2df2..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]))) @@ -188,51 +188,39 @@ :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"))) -(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)) - (assert (= tda-main tda-a1 tda-a2))) - (defn test-yield-from [] "NATIVE: testing yield from" (defn yield-from-test [] @@ -253,7 +241,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..5626296 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]]) @@ -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) - (catch [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 ccc8631..0ca08ba 100644 --- a/tests/native_tests/shadow.hy +++ b/tests/native_tests/shadow.hy @@ -1,10 +1,10 @@ (defn test-shadow-addition [] "NATIVE: test shadow addition" - (let [[x +]] + (let [x +] (assert (try (x) - (catch [TypeError] True) - (else (throw AssertionError)))) + (except [TypeError] True) + (else (raise AssertionError)))) (assert (= (x 1 2 3 4) 10)) (assert (= (x 1 2 3 4 5) 15)) ; with strings @@ -21,11 +21,11 @@ (defn test-shadow-subtraction [] "NATIVE: test shadow subtraction" - (let [[x -]] + (let [x -] (assert (try (x) - (catch [TypeError] True) - (else (throw AssertionError)))) + (except [TypeError] True) + (else (raise AssertionError)))) (assert (= (x 1) -1)) (assert (= (x 2 1) 1)) (assert (= (x 2 1 1) 0)))) @@ -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,11 +41,11 @@ (defn test-shadow-division [] "NATIVE: test shadow division" - (let [[x /]] + (let [x /] (assert (try (x) - (catch [TypeError] True) - (else (throw AssertionError)))) + (except [TypeError] True) + (else (raise AssertionError)))) (assert (= (x 1) 1)) (assert (= (x 8 2) 4)) (assert (= (x 8 2 2) 2)) @@ -57,12 +57,12 @@ (for [x [< <= = != >= >]] (assert (try (x) - (catch [TypeError] True) - (else (throw AssertionError)))) + (except [TypeError] True) + (else (raise AssertionError)))) (assert (try (x 1) - (catch [TypeError] True) - (else (throw AssertionError))))) + (except [TypeError] True) + (else (raise AssertionError))))) (for [(, x y) [[< >=] [<= >] [= !=]]] @@ -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])) 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))) 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..fd9933e 100644 --- a/tests/native_tests/with_test.hy +++ b/tests/native_tests/with_test.hy @@ -1,44 +1,41 @@ (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" - (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))))