Merge branch 'master' into pr/864

This commit is contained in:
Tuukka Turto 2015-10-03 21:05:46 +03:00
commit 5a34285b08
60 changed files with 1625 additions and 888 deletions

View File

@ -6,6 +6,8 @@ omit =
*/site-packages/nose/*
*/pypy/*
[report]
exclude_lines =
# Have to re-enable the standard pragma
pragma: no cover

1
.gitignore vendored
View File

@ -7,3 +7,4 @@
dist
.coverage
build/
.noseids

View File

@ -63,3 +63,5 @@
* Nicolas Pénet <z.nicolas@gmail.com>
* Adrià Garriga Alonso <adria@monkingme.com>
* Antony Woods <antony@teamwoods.org>
* Matthew Egan Odendahl <github.gilch@xoxy.net>
* Tim Martin <tim@asymptotic.co.uk>

6
NEWS
View File

@ -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 ]

View File

@ -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)

60
docs/contrib/alias.rst Normal file
View File

@ -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

View File

@ -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

View File

@ -11,3 +11,4 @@ Contents:
loop
multi
flow
alias

View File

@ -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)`

View File

@ -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.
@ -334,6 +352,18 @@ For example:
=> (counter [1 2 3 4 5 2 3] 2)
2
They can be used to assign multiple variables at once:
.. code-block:: hy
=> (setv a 1 b 2)
(1L, 2L)
=> a
1L
=> b
2L
=>
defclass
--------
@ -345,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:
@ -353,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")
@ -366,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:
@ -430,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)
@ -453,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))))
@ -476,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
-------
@ -559,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!:
@ -645,7 +631,7 @@ del
=> (setv test (list (range 10)))
=> test
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
=> (del (slice test 2 4)) ;; remove items from 2 to 4 excluded
=> (del (cut test 2 4)) ;; remove items from 2 to 4 excluded
=> test
[0, 1, 4, 5, 6, 7, 8, 9]
=> (setv dic {"foo" "bar"})
@ -800,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
@ -823,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
@ -869,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
@ -1010,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
@ -1072,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)
@ -1231,77 +1203,77 @@ expression.
{1, 3, 5}
slice
cut
-----
``slice`` can be used to take a subset of a list and create a new list from it.
The form takes at least one parameter specifying the list to slice. Two
``cut`` can be used to take a subset of a list and create a new list from it.
The form takes at least one parameter specifying the list to cut. Two
optional parameters can be used to give the start and end position of the
subset. If they are not supplied, the default value of ``None`` will be used
instead. The third optional parameter is used to control step between the elements.
``slice`` follows the same rules as its Python counterpart. Negative indices are
``cut`` follows the same rules as its Python counterpart. Negative indices are
counted starting from the end of the list. Some example usage:
.. code-block:: clj
=> (def collection (range 10))
=> (slice collection)
=> (cut collection)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
=> (slice collection 5)
=> (cut collection 5)
[5, 6, 7, 8, 9]
=> (slice collection 2 8)
=> (cut collection 2 8)
[2, 3, 4, 5, 6, 7]
=> (slice collection 2 8 2)
=> (cut collection 2 8 2)
[2, 4, 6]
=> (slice collection -4 -2)
=> (cut collection -4 -2)
[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.
@ -1391,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
@ -1451,6 +1423,25 @@ will be 4 (``1+1 + 1+1``).
8
#@
~~
.. versionadded:: 0.12.0
The :ref:`reader macro<reader-macros>` ``#@`` can be used as a shorthand
for ``with-decorator``. With ``#@``, the previous example becomes:
.. code-block:: clj
=> #@(inc-decorator (defn addition [a b] (+ a b)))
=> (addition 1 1)
4
=> #@(inc2-decorator inc-decorator
... (defn addition [a b] (+ a b)))
=> (addition 1 1)
8
.. _with-gensyms:
with-gensyms
@ -1470,9 +1461,9 @@ expands to:
.. code-block:: hy
(let [[a (gensym)
[b (gensym)
[c (gensym)]]
(let [a (gensym)
b (gensym)
c (gensym)]
...)
.. seealso::

View File

@ -207,6 +207,26 @@ Returns ``True`` if *x* is a float.
False
.. _fraction-fn:
fraction
--------
Returns a Python object of type ``fractions.Fraction``.
.. code-block:: hy
=> (fraction 1 2)
Fraction(1, 2)
Note that Hy has a built-in fraction literal that does the same thing:
.. code-block:: hy
=> 1/2
Fraction(1, 2)
.. _even?-fn:
even?
@ -416,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<HyKeyword>`.
.. code-block:: hy
=> (keyword? :foo)
True
=> (setv foo 1)
=> (keyword? foo)
False
.. _list*-fn:
@ -651,6 +690,37 @@ Returns ``True`` if *x* is odd. Raises ``TypeError`` if
=> (odd? 0)
False
.. _partition-fn:
partition
---------
Usage: ``(partition coll [n] [step] [fillvalue])``
Chunks *coll* into *n*-tuples (pairs by default).
.. code-block:: hy
=> (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:
@ -1030,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
@ -1045,6 +1115,21 @@ if *from-file* ends before a complete expression can be parsed.
hyfriends!
EOF!
read-str
--------
Usage: ``(read-str "string")``
This is essentially a wrapper around `read` which reads expressions from a
string:
.. code-block:: hy
=> (read-str "(print 1)")
(u'print' 1L)
=> (eval (read-str "(print 1)"))
1
=>
.. _remove-fn:
@ -1168,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]

View File

@ -184,7 +184,7 @@ expressions are made of Python lists wrapped in a
- ``(cons something some-list)`` is ``((type some-list) (+ [something]
some-list))`` (if ``some-list`` inherits from ``list``).
- ``(get (cons a b) 0)`` is ``a``
- ``(slice (cons a b) 1)`` is ``b``
- ``(cut (cons a b) 1)`` is ``b``
Hy supports a dotted-list syntax, where ``'(a . b)`` means ``(cons 'a
'b)`` and ``'(a b . c)`` means ``(cons 'a (cons 'b 'c))``. If the
@ -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]))))

View File

@ -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))

View File

@ -200,6 +200,10 @@ Hy. Let's experiment with this in the hy interpreter::
(1, 2, 3)
=> #{3 1 2}
{1, 2, 3}
=> 1/2
Fraction(1, 2)
Notice the last two lines: Hy has a fraction literal like Clojure.
If you are familiar with other Lisps, you may be interested that Hy
supports the Common Lisp method of quoting:
@ -331,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::
@ -473,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::
@ -498,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
=====================

View File

@ -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))

View File

@ -7,7 +7,7 @@
(try
(import [urllib.request [urlopen]])
(catch [ImportError]
(except [ImportError]
(import [urllib2 [urlopen]])))
(defn get-rss-feed-name [tumblr]
@ -20,6 +20,6 @@
(for [post (.xpath (get-rss-feed tumblr) "//item/title")]
(print post.text)))
(if (slice argv 2)
(if (cut argv 2)
(print-posts (get argv 2))
(print-posts "this-plt-life"))

View File

@ -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)))

View File

@ -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]

View File

@ -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
@ -723,6 +727,9 @@ class HyASTCompiler(object):
def compile_eval(self, expr):
expr.pop(0)
if not isinstance(expr[0], (HyExpression, HySymbol)):
raise HyTypeError(expr, "expression expected as first argument")
elist = [HySymbol("hy_eval")] + [expr[0]]
if len(expr) >= 2:
elist.append(expr[1])
@ -741,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:
@ -821,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"):
@ -899,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]))
@ -1293,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 = []
@ -1307,9 +1317,9 @@ class HyASTCompiler(object):
col_offset=expr.start_column,
targets=del_targets)
@builds("slice")
@builds("cut")
@checkargs(min=1, max=4)
def compile_slice_expression(self, expr):
def compile_cut_expression(self, expr):
expr.pop(0) # index
val = self.compile(expr.pop(0)) # target
@ -1469,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(
@ -1593,7 +1610,7 @@ class HyASTCompiler(object):
# We then pass the other arguments to the function
expr[0] = HyExpression(
[HySymbol("slice"), tempvar, HyInteger(1)]
[HySymbol("cut"), tempvar, HyInteger(1)]
).replace(expr[0])
ret += self.compile(call)
@ -1625,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(
@ -1669,17 +1700,78 @@ class HyASTCompiler(object):
@builds("and")
@builds("or")
@checkargs(min=2)
def compile_logical_or_and_and_operator(self, expression):
ops = {"and": ast.And,
"or": ast.Or}
ops = {"and": (ast.And, "True"),
"or": (ast.Or, "None")}
operator = expression.pop(0)
values, ret, _ = self._compile_collect(expression)
opnode, default = ops[operator]
root_line, root_column = operator.start_line, operator.start_column
if len(expression) == 0:
return ast.Name(id=default,
ctx=ast.Load(),
lineno=root_line,
col_offset=root_column)
elif len(expression) == 1:
return self.compile(expression[0])
ret = Result()
values = list(map(self.compile, expression))
has_stmt = any(value.stmts for value in values)
if has_stmt:
# Compile it to an if...else sequence
var = self.get_anon_var()
name = ast.Name(id=var,
ctx=ast.Store(),
lineno=root_line,
col_offset=root_column)
expr_name = ast.Name(id=var,
ctx=ast.Load(),
lineno=root_line,
col_offset=root_column)
ret += ast.BoolOp(op=ops[operator](),
lineno=operator.start_line,
col_offset=operator.start_column,
values=values)
def make_assign(value, node=None):
if node is None:
line, column = root_line, root_column
else:
line, column = node.lineno, node.col_offset
return ast.Assign(targets=[ast.Name(id=var,
ctx=ast.Store(),
lineno=line,
col_offset=column)],
value=value,
lineno=line,
col_offset=column)
root = []
current = root
for i, value in enumerate(values):
if value.stmts:
node = value.stmts[0]
current.extend(value.stmts)
else:
node = value.expr
current.append(make_assign(value.force_expr, value.force_expr))
if i == len(values)-1:
# Skip a redundant 'if'.
break
if operator == "and":
cond = expr_name
elif operator == "or":
cond = ast.UnaryOp(op=ast.Not(),
operand=expr_name,
lineno=node.lineno,
col_offset=node.col_offset)
current.append(ast.If(test=cond,
body=[],
lineno=node.lineno,
col_offset=node.col_offset,
orelse=[]))
current = current[-1].body
ret = sum(root, ret)
ret += Result(expr=expr_name, temp_variables=[expr_name, name])
else:
ret += ast.BoolOp(op=opnode(),
lineno=root_line,
col_offset=root_column,
values=[value.force_expr for value in values])
return ret
@builds("=")
@ -1714,8 +1806,6 @@ class HyASTCompiler(object):
col_offset=e.start_column)
@builds("%")
@builds("/")
@builds("//")
@builds("**")
@builds("<<")
@builds(">>")
@ -1757,11 +1847,14 @@ class HyASTCompiler(object):
@builds("+")
@builds("*")
@builds("/")
@builds("//")
def compile_maths_expression_mul(self, expression):
if len(expression) > 2:
return self.compile_maths_expression(expression)
else:
id_op = {"+": HyInteger(0), "*": HyInteger(1)}
id_op = {"+": HyInteger(0), "*": HyInteger(1), "/": HyInteger(1),
"//": HyInteger(1)}
op = expression.pop(0)
arg = expression.pop(0) if expression else id_op[op]
@ -1903,23 +1996,52 @@ class HyASTCompiler(object):
@builds("def")
@builds("setv")
@checkargs(2)
def compile_def_expression(self, expression):
return self._compile_assign(expression[1], expression[2],
expression.start_line,
expression.start_column)
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,
"`{}' needs an even number of arguments".format(
root))
else:
result = Result()
exprs = []
for tgt, target in zip(expression[::2], expression[1::2]):
item = self._compile_assign(tgt, target,
tgt.start_line, tgt.start_column)
result += item
exprs.append(item.force_expr)
result += ast.Tuple(elts=exprs, lineno=expression.start_line,
col_offset=expression.start_column,
ctx=ast.Load())
return result
def _compile_assign(self, name, result,
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:
@ -2047,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)
@ -2151,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:
@ -2169,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
@ -2179,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=[],
@ -2286,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)
@ -2295,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)

39
hy/contrib/alias.hy Normal file
View File

@ -0,0 +1,39 @@
;; Copyright (c) 2014, 2015 Gergely Nagy
;; Copyright (c) 2014, 2015 Paul Tagliamonte <paultag@debian.org>
;; 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))

View File

@ -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)))

View File

@ -0,0 +1,33 @@
;; Copyright (c) 2014, 2015 Gergely Nagy
;; 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.
(defn Botsbuildbots () (Botsbuildbots))
(defmacro Botsbuildbots []
"Build bots, repeatedly.^W^W^WPrint the AUTHORS, forever."
`(try
(do
(import [requests])
(let [r (requests.get
"https://raw.githubusercontent.com/hylang/hy/master/AUTHORS")]
(repeat r.text)))
(except [e ImportError]
(repeat "Botsbuildbots requires `requests' to function."))))

View File

@ -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)

View File

@ -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))))

View File

@ -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]

View File

@ -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"

View File

@ -26,7 +26,12 @@
(import itertools)
(import functools)
(import collections)
(import [fractions [Fraction :as fraction]])
(import operator) ; shadow not available yet
(import sys)
(if-python2
(import [StringIO [StringIO]])
(import [io [StringIO]]))
(import [hy._compat [long-type]]) ; long for python2, int for python3
(import [hy.models.cons [HyCons]]
[hy.models.symbol [HySymbol]]
@ -79,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))]))))
@ -173,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)
@ -192,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"
@ -218,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"
@ -245,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"
@ -273,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"
@ -310,6 +349,18 @@
(_numeric-check n)
(= (% n 2) 1))
(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"
(_numeric_check n)
@ -347,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"
@ -373,7 +425,7 @@
(while true
(def inn (str (.readline from-file)))
(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)))
@ -381,13 +433,9 @@
(else (if parsed (break)))))
parsed)
(defun Botsbuildbots () (Botsbuildbots))
(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 read-str [input]
"Reads and tokenizes first line of input"
(read :from-file (StringIO input)))
(defn hyify [text]
"Convert text to match hy identifier"
@ -402,26 +450,26 @@
(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.
String will be used as is. Even objects with the __name__ magic will work"
(if (and (string? value) (value.startswith *keyword-prefix*))
(hyify (slice value 2))
(hyify (cut value 2))
(if (string? value)
(hyify value)
(try
(hyify (. value __name__))
(catch [] (string value))))))
(except [] (string value))))))
(def *exports* '[Botsbuildbots
butlast calling-module-name coll? cons cons? cycle
dec distinct disassemble drop drop-last drop-while empty? even?
every? first filter filterfalse flatten float? 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? pos? range read remove repeat repeatedly
rest reduce second some string string? symbol? take take-nth
take-while zero? zip zip_longest zipwith])
(def *exports*
'[*map accumulate butlast calling-module-name chain coll? combinations
compress cons cons? count cycle dec distinct disassemble drop drop-last
drop-while empty? even? every? first filter flatten float? fraction gensym
group-by identity inc input instance? integer integer? integer-char?
interleave interpose islice iterable? iterate iterator? keyword keyword?
last list* macroexpand macroexpand-1 map merge-with multicombinations name
neg? nil? none? nth numeric? odd? partition permutations pos? product range
read read-str remove repeat repeatedly rest reduce second some string
string? symbol? take take-nth take-while tee zero? zip zip-longest])

View File

@ -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]
@ -57,7 +57,7 @@
(defmacro cdr [thing]
"Get all the elements of a thing, except the first"
`(slice ~thing 1))
`(cut ~thing 1))
(defmacro cond [&rest branches]
@ -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 (slice 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 (slice '~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,24 +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))
(defmacro Botsbuildbots []
"Build bots, repeatedly.^W^W^WPrint the AUTHORS, forever."
`(try
(do
(import [requests])
(let [[r (requests.get
"https://raw.githubusercontent.com/hylang/hy/master/AUTHORS")]]
(repeat r.text)))
(catch [e ImportError]
(repeat "Botsbuildbots requires `requests' to function."))))
(defreader @ [expr]
(let [decorators (cut expr nil -1)
fndef (get expr -1)]
`(with-decorator ~@decorators ~fndef)))

View File

@ -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)
@ -67,7 +67,7 @@
(raise (TypeError "Need at least 2 arguments to compare"))
(reduce operator.and_
(list-comp (op x y)
[(, x y) (zip args (slice args 1))]))))
[(, x y) (zip args (cut args 1))]))))
(defn < [&rest args]
"Shadow < operator for when we need to import / map it against something"
(comp-op operator.lt args))

View File

@ -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

View File

@ -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, "<eval_body>", "exec"), namespace)

View File

@ -35,3 +35,7 @@ def tokenize(buf):
pos = e.getsourcepos()
raise LexException("Could not identify the next token.",
pos.lineno, pos.colno)
except LexException as e:
if e.source is None:
e.source = buf
raise

View File

@ -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

View File

@ -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):
@ -266,6 +282,14 @@ def t_identifier(p):
except ValueError:
pass
if '/' in obj:
try:
lhs, rhs = obj.split('/')
return HyExpression([HySymbol('fraction'), HyInteger(lhs),
HyInteger(rhs)])
except ValueError:
pass
try:
return HyFloat(obj)
except ValueError:
@ -281,7 +305,6 @@ def t_identifier(p):
"true": "True",
"false": "False",
"nil": "None",
"null": "None",
}
if obj in table:
@ -290,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)

View File

@ -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
@ -60,7 +61,7 @@ def macro(name):
def reader(name):
"""Decorator to define a macro called `name`.
"""Decorator to define a reader macro called `name`.
This stores the macro `name` in the namespace for the module where it is
defined.
@ -68,7 +69,7 @@ def reader(name):
If the module where it is defined is in `hy.core`, then the macro is stored
in the default `None` namespace.
This function is called from the `defmacro` special form in the compiler.
This function is called from the `defreader` special form in the compiler.
"""
def _(fn):
@ -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("<lambda>()", "", 1).strip()
raise HyMacroExpansionError(tree, msg)
try:
obj = wrap_value(m(*ntree[1:]))
@ -176,14 +195,15 @@ def reader_macroexpand(char, tree, module_name):
"""Expand the reader macro "char" with argument `tree`."""
load_macros(module_name)
if char not in _hy_reader[module_name]:
raise HyTypeError(
char,
"`{0}' is not a reader macro in module '{1}'".format(
reader_macro = _hy_reader[module_name].get(char)
if reader_macro is None:
try:
reader_macro = _hy_reader[None][char]
except KeyError:
raise HyTypeError(
char,
module_name,
),
)
"`{0}' is not a defined reader macro.".format(char)
)
expr = _hy_reader[module_name][char](tree)
expr = reader_macro(tree)
return replace_hy_obj(wrap_value(expr), tree)

View File

@ -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)

View File

@ -7,26 +7,26 @@
(setv *maintainer-line*
" -- Alexander Artemenko <svetlyak.40wt@gmail.com> 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")))))

View File

@ -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")))

View File

@ -20,6 +20,7 @@ from .native_tests.contrib.meth import * # noqa
from .native_tests.contrib.walk import * # noqa
from .native_tests.contrib.multi import * # noqa
from .native_tests.contrib.curry import * # noqa
from .native_tests.contrib.botsbuildbots import * # noqa
if PY3:
from .native_tests.py3_only_tests import * # noqa

View File

@ -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():
@ -291,18 +260,18 @@ def test_ast_bad_get():
cant_compile("(get 1)")
def test_ast_good_slice():
"Make sure AST can compile valid slice"
can_compile("(slice x)")
can_compile("(slice x y)")
can_compile("(slice x y z)")
can_compile("(slice x y z t)")
def test_ast_good_cut():
"Make sure AST can compile valid cut"
can_compile("(cut x)")
can_compile("(cut x y)")
can_compile("(cut x y z)")
can_compile("(cut x y z t)")
def test_ast_bad_slice():
"Make sure AST can't compile invalid slice"
cant_compile("(slice)")
cant_compile("(slice 1 2 3 4 5)")
def test_ast_bad_cut():
"Make sure AST can't compile invalid cut"
cant_compile("(cut)")
cant_compile("(cut 1 2 3 4 5)")
def test_ast_good_take():
@ -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))
""")

View File

@ -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():
@ -108,6 +114,13 @@ def test_lex_integers():
assert objs == [HyInteger(42)]
def test_lex_fractions():
""" Make sure that fractions are valid expressions"""
objs = tokenize("1/2")
assert objs == [HyExpression([HySymbol("fraction"), HyInteger(1),
HyInteger(2)])]
def test_lex_expression_float():
""" Make sure expressions can produce floats """
objs = tokenize("(foo 2.)")
@ -319,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]

View File

@ -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)

View File

@ -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)))

View File

@ -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))

View File

@ -0,0 +1,5 @@
(import [hy.contrib.botsbuildbots [*]])
(require hy.contrib.botsbuildbots)
(defn test-botsbuildbots []
(assert (> (len (first (Botsbuildbots))) 50)))

View File

@ -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))))

View File

@ -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"))

View File

@ -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)))))))))

View File

@ -67,11 +67,11 @@
(assert-equal -1 (dec 0))
(assert-equal 0 (dec (dec 2)))
(try (do (dec "foo") (assert False))
(catch [e [TypeError]] (assert (in "not a number" (str e)))))
(except [e [TypeError]] (assert (in "not a number" (str e)))))
(try (do (dec []) (assert False))
(catch [e [TypeError]] (assert (in "not a number" (str e)))))
(except [e [TypeError]] (assert (in "not a number" (str e)))))
(try (do (dec None) (assert False))
(catch [e [TypeError]] (assert (in "not a number" (str e))))))
(except [e [TypeError]] (assert (in "not a number" (str e))))))
(defn test-setv []
"NATIVE: testing setv mutation"
@ -85,9 +85,9 @@
(assert-equal (x y) 9)
(assert-equal (y x) 9)
(try (do (setv a.b 1) (assert False))
(catch [e [NameError]] (assert (in "name 'a' is not defined" (str e)))))
(except [e [NameError]] (assert (in "name 'a' is not defined" (str e)))))
(try (do (setv b.a (fn [x] x)) (assert False))
(catch [e [NameError]] (assert (in "name 'b' is not defined" (str e)))))
(except [e [NameError]] (assert (in "name 'b' is not defined" (str e)))))
(import itertools)
(setv foopermutations (fn [x] (itertools.permutations x)))
(setv p (set [(, 1 3 2) (, 3 2 1) (, 2 1 3) (, 3 1 2) (, 1 2 3) (, 2 3 1)]))
@ -127,7 +127,7 @@
(setv res (list (drop 0 [1 2 3 4 5])))
(assert-equal res [1 2 3 4 5])
(try (do (list (drop -1 [1 2 3 4 5])) (assert False))
(catch [e [ValueError]] nil))
(except [e [ValueError]] nil))
(setv res (list (drop 6 (iter [1 2 3 4 5]))))
(assert-equal res [])
(setv res (list (take 5 (drop 2 (iterate inc 0)))))
@ -174,11 +174,11 @@
(assert-false (even? 1))
(assert-true (even? 0))
(try (even? "foo")
(catch [e [TypeError]] (assert (in "not a number" (str e)))))
(except [e [TypeError]] (assert (in "not a number" (str e)))))
(try (even? [])
(catch [e [TypeError]] (assert (in "not a number" (str e)))))
(except [e [TypeError]] (assert (in "not a number" (str e)))))
(try (even? None)
(catch [e [TypeError]] (assert (in "not a number" (str e))))))
(except [e [TypeError]] (assert (in "not a number" (str e))))))
(defn test-every? []
"NATIVE: testing the every? function"
@ -221,9 +221,9 @@
(setv res (flatten (, 1 (, None 3))))
(assert-equal res [1 None 3])
(try (flatten "foo")
(catch [e [TypeError]] (assert (in "not a collection" (str e)))))
(except [e [TypeError]] (assert (in "not a collection" (str e)))))
(try (flatten 12.34)
(catch [e [TypeError]] (assert (in "not a collection" (str e))))))
(except [e [TypeError]] (assert (in "not a collection" (str e))))))
(defn test-float? []
"NATIVE: testing the float? function"
@ -264,11 +264,11 @@
(assert-equal 3 (inc 2))
(assert-equal 0 (inc -1))
(try (do (inc "foo") (assert False))
(catch [e [TypeError]] (assert (in "not a number" (str e)))))
(except [e [TypeError]] (assert (in "not a number" (str e)))))
(try (do (inc []) (assert False))
(catch [e [TypeError]] (assert (in "not a number" (str e)))))
(except [e [TypeError]] (assert (in "not a number" (str e)))))
(try (do (inc None) (assert False))
(catch [e [TypeError]] (assert (in "not a number" (str e))))))
(except [e [TypeError]] (assert (in "not a number" (str e))))))
(defn test-instance []
"NATIVE: testing instance? function"
@ -395,11 +395,11 @@
(assert-false (neg? 1))
(assert-false (neg? 0))
(try (do (neg? "foo") (assert False))
(catch [e [TypeError]] (assert (in "not a number" (str e)))))
(except [e [TypeError]] (assert (in "not a number" (str e)))))
(try (do (neg? []) (assert False))
(catch [e [TypeError]] (assert (in "not a number" (str e)))))
(except [e [TypeError]] (assert (in "not a number" (str e)))))
(try (do (neg? None) (assert False))
(catch [e [TypeError]] (assert (in "not a number" (str e))))))
(except [e [TypeError]] (assert (in "not a number" (str e))))))
(defn test-zero []
"NATIVE: testing the zero? function"
@ -407,11 +407,11 @@
(assert-false (zero? 1))
(assert-true (zero? 0))
(try (do (zero? "foo") (assert False))
(catch [e [TypeError]] (assert (in "not a number" (str e)))))
(except [e [TypeError]] (assert (in "not a number" (str e)))))
(try (do (zero? []) (assert False))
(catch [e [TypeError]] (assert (in "not a number" (str e)))))
(except [e [TypeError]] (assert (in "not a number" (str e)))))
(try (do (zero? None) (assert False))
(catch [e [TypeError]] (assert (in "not a number" (str e))))))
(except [e [TypeError]] (assert (in "not a number" (str e))))))
(defn test-none []
"NATIVE: testing for `is None`"
@ -438,7 +438,7 @@
(assert-equal (nth [1 2 4 7] 5 "some default value")
"some default value") ; with default specified
(try (do (nth [1 2 4 7] -1) (assert False))
(catch [e [ValueError]] nil))
(except [e [ValueError]] nil))
;; now for iterators
(assert-equal 2 (nth (iter [1 2 4 7]) 1))
(assert-equal 7 (nth (iter [1 2 4 7]) 3))
@ -446,7 +446,7 @@
(assert-equal (nth (iter [1 2 4 7]) 5 "some default value")
"some default value") ; with default specified
(try (do (nth (iter [1 2 4 7]) -1) (assert False))
(catch [e [ValueError]] nil))
(except [e [ValueError]] nil))
(assert-equal 5 (nth (take 3 (drop 2 [1 2 3 4 5 6])) 2)))
(defn test-numeric? []
@ -464,11 +464,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))
;; no remainder
(assert-equal (list (partition ten 3))
[(, 0 1 2) (, 3 4 5) (, 6 7 8)])
;; pair by default
(assert-equal (list (partition ten))
[(, 0 1) (, 2 3) (, 4 5) (, 6 7) (, 8 9)])
;; length 1 is valid
(assert-equal (list (partition ten 1))
[(, 0) (, 1) (, 2) (, 3) (, 4) (, 5) (, 6) (, 7) (, 8) (, 9)])
;; 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"
@ -476,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"
@ -543,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]))
@ -567,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 []
@ -584,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 [])
@ -617,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]))

View File

@ -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)))

View File

@ -12,6 +12,26 @@
(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))))
(defn test-lists []
"NATIVE: test lists work right"
(assert (= [1 2 3 4] (+ [1 2] [3 4]))))
@ -29,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])
@ -38,46 +63,73 @@
(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 []
"NATIVE: test that setv works on pairs of arguments"
(assert (= (setv a 1 b 2) (, 1 2)))
(assert (= a 1))
(assert (= b 2))
(setv y 0 x 1 y x)
(assert y)
(try (eval '(setv a 1 b))
(except [e [TypeError]] (assert (in "`setv' needs an even number of arguments" (str e))))))
(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)))))
@ -98,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.
@ -207,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 []
@ -256,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 []
@ -299,7 +355,7 @@
(try (do) (except [IOError]) (except))
;; Test correct (raise)
(let [[passed false]]
(let [passed false]
(try
(try
(raise IndexError)
@ -309,7 +365,7 @@
(assert passed))
;; Test incorrect (raise)
(let [[passed false]]
(let [passed false]
(try
(raise)
;; Python 2 raises TypeError
@ -318,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)
@ -336,8 +391,8 @@
;; Test (finally) + (raise) + (else)
(let [[passed false]
[not-elsed true]]
(let [passed false
not-elsed true]
(try
(raise Exception)
(except)
@ -348,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
@ -372,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)
@ -380,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)
@ -400,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]
@ -418,7 +473,7 @@
(else (setv x 44)))
(assert (= x 45)))
(let [[x 0]]
(let [x 0]
(try
(raise KeyError)
(except []
@ -426,7 +481,7 @@
(else (setv x 44)))
(assert (= x 45)))
(let [[x 0]]
(let [x 0]
(try
(try
(raise KeyError)
@ -505,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)))
@ -519,11 +574,11 @@
(assert (= (car [1 2 3 4 5]) 1)))
(defn test-slice []
"NATIVE: test slice"
(assert (= (slice [1 2 3 4 5] 1) [2 3 4 5]))
(assert (= (slice [1 2 3 4 5] 1 3) [2 3]))
(assert (= (slice [1 2 3 4 5]) [1 2 3 4 5])))
(defn test-cut []
"NATIVE: test cut"
(assert (= (cut [1 2 3 4 5] 1) [2 3 4 5]))
(assert (= (cut [1 2 3 4 5] 1 3) [2 3]))
(assert (= (cut [1 2 3 4 5]) [1 2 3 4 5])))
(defn test-take []
@ -552,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")))))
@ -575,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))
@ -652,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 [])
@ -689,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"))
@ -730,65 +791,81 @@
(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)]]
(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-false False))
(assert (= and-true True))
(assert (= and-single 1)))
; short circuiting
(setv a 1)
(and 0 (setv a 2))
(assert (= a 1)))
(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)]]
(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))))
(assert (= or-none-true False))
(assert (= or-false nil))
(assert (= or-single 1)))
; short circuiting
(setv a 1)
(or 1 (setv a 2))
(assert (= a 1)))
(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))))))
@ -833,21 +910,32 @@
(assert (= 27 (eval (+ (quote (*)) (* [(quote 3)] 3)))))
(assert (= None (eval (quote (print ""))))))
(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]])
; 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 []
"NATIVE: test the import syntax."
@ -909,7 +997,7 @@
(defn test-if-let-mixing []
"NATIVE: test that we can now mix if and let"
(assert (= 0 (if true (let [[x 0]] x) 42))))
(assert (= 0 (if true (let [x 0] x) 42))))
(defn test-if-in-if []
"NATIVE: test that we can use if in if"
@ -936,7 +1024,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))))
@ -1024,7 +1112,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 []
@ -1104,7 +1193,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 []
@ -1128,9 +1217,13 @@
(read stdin-buffer)
(try
(read stdin-buffer)
(catch [e Exception]
(except [e Exception]
(assert (isinstance e EOFError)))))
(defn test-read-str []
"NATIVE: test read-str"
(assert (= (read-str "(print 1)") '(print 1))))
(defn test-keyword-creation []
"NATIVE: Test keyword creation"
(assert (= (keyword "foo") :foo))

View File

@ -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)))))

View File

@ -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,13 +241,10 @@
(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])))
(defn test-botsbuildbots []
(assert (> (len (first (Botsbuildbots))) 50)))
(defn test-defmain []
"NATIVE: make sure defmain is clean"

View File

@ -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})))))

View File

@ -1,3 +1,6 @@
(import [functools [wraps]])
(defn test-reader-macro []
"Test a basic redaer macro"
(defreader ^ [expr]
@ -34,3 +37,34 @@
(assert (= (, 1 2 3) a)))
(defn test-builtin-decorator-reader []
(defn increment-arguments [func]
"Increments each argument passed to the decorated function."
#@((wraps func)
(defn wrapper [&rest args &kwargs kwargs]
(apply func
(map inc args)
(dict-comp k (inc v) [[k v] (.items kwargs)])))))
#@(increment-arguments
(defn foo [&rest args &kwargs kwargs]
"Bar."
(, args kwargs)))
;; The decorator did what it was supposed to
(assert (= (, (, 2 3 4) {"quux" 5 "baz" 6})
(foo 1 2 3 :quux 4 :baz 5)))
;; @wraps preserved the doctstring and __name__
(assert (= "foo" (. foo --name--)))
(assert (= "Bar." (. foo --doc--)))
;; We can use the #@ reader macro to apply more than one decorator
#@(increment-arguments
increment-arguments
(defn double-foo [&rest args &kwargs kwargs]
"Bar."
(, args kwargs)))
(assert (= (, (, 3 4 5) {"quux" 6 "baz" 7})
(double-foo 1 2 3 :quux 4 :baz 5))))

View File

@ -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]))

View File

@ -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)))

View File

@ -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)))

View File

@ -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"

View File

@ -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))))