Merge branch 'master' into multimethod

Conflicts:
	docs/contrib/multi.rst
This commit is contained in:
Tuukka Turto 2016-11-03 14:06:13 +02:00
commit 77bc767907
52 changed files with 599 additions and 204 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
/hy/version.py
*.pyc *.pyc
*swp *swp
*hy*egg* *hy*egg*

View File

@ -4,6 +4,8 @@ matrix:
include: include:
- python: 3.5 - python: 3.5
env: TOXENV=py35 env: TOXENV=py35
- python: 3.6-dev
env: TOXENV=py36
env: env:
- TOXENV=py27 - TOXENV=py27
- TOXENV=py33 - TOXENV=py33

View File

@ -69,3 +69,4 @@
* Andrew Savchyn <dev@scorpil.com> * Andrew Savchyn <dev@scorpil.com>
* Lev Kravinsky <kravinskylev@gmail.com> * Lev Kravinsky <kravinskylev@gmail.com>
* Luna Lunapiena <lunacodes@gmail.com> * Luna Lunapiena <lunacodes@gmail.com>
* Jakub Wilk <jwilk@jwilk.net>

View File

@ -7,7 +7,9 @@ helps in making Hy more awesome.
Pull requests are great! We love them; here is a quick guide: Pull requests are great! We love them; here is a quick guide:
- `Fork the repo`_ and create a topic branch for a feature/fix. Avoid - `Fork the repo`_ and create a topic branch for a feature/fix. Avoid
making changes directly on the master branch. making changes directly on the master branch. If you would like to
contribute but don't know how to begin, the `good-first-bug`_ label
of the `issue tracker`_ is the place to go.
(If you're new to Git: `Start Here`_) (If you're new to Git: `Start Here`_)
- All incoming features should be accompanied with tests. - All incoming features should be accompanied with tests.
@ -36,7 +38,7 @@ Pull requests are great! We love them; here is a quick guide:
+ Try sticking to the 50 character limit for the first line of Git + Try sticking to the 50 character limit for the first line of Git
commit messages. commit messages.
+ For more detail/explainations, follow this up with a blank line and + For more detail/explanations, follow this up with a blank line and
continue describing the commit in detail. continue describing the commit in detail.
- Finally, add yourself to the AUTHORS file (as a separate commit): you - Finally, add yourself to the AUTHORS file (as a separate commit): you
@ -93,4 +95,5 @@ http://contributor-covenant.org/version/1/1/0/.
.. _Contributor Covenant: http://contributor-covenant.org .. _Contributor Covenant: http://contributor-covenant.org
.. _issue tracker: https://github.com/hylang/hy/issues .. _issue tracker: https://github.com/hylang/hy/issues
.. _Fork the Repo: https://help.github.com/articles/fork-a-repo/ .. _Fork the Repo: https://help.github.com/articles/fork-a-repo/
.. _Start Here: http://rogerdudler.github.io/git-guide/) .. _Start Here: http://rogerdudler.github.io/git-guide/
.. _good-first-bug: http://github.com/hylang/hy/issues?q=is%3Aissue+is%3Aopen+label%3Agood-first-bug

View File

@ -23,7 +23,7 @@ all:
@echo "" @echo ""
docs: docs:
make -C docs html $(MAKE) -C docs html
upload: r upload: r
python setup.py sdist upload python setup.py sdist upload
@ -72,10 +72,10 @@ coveralls:
clean: clean:
@find . -name "*.pyc" -exec rm {} \; @find . -name "*.pyc" -exec rm {} \;
@find -name __pycache__ -delete @find . -name __pycache__ -delete
@${RM} -r -f .tox @${RM} -r -f .tox
@${RM} -r -f dist @${RM} -r -f dist
@${RM} -r -f *.egg-info @${RM} -r -f *.egg-info
@${RM} -r -f docs/_build @${RM} -r -f docs/_build
.PHONY: docs .PHONY: all docs upload full venv dev test tox flake clear d diff r python coveralls clean

2
NEWS
View File

@ -280,7 +280,7 @@ Changes from Hy 0.9.10
* Clarified rest / cdr, cleaned up require * Clarified rest / cdr, cleaned up require
* Make with return the last expression from its branch * Make with return the last expression from its branch
* Fix yielding to not suck (#151) * Fix yielding to not suck (#151)
* Make assoc accept multiple values, also added a even/odd check for * Make assoc accept multiple values, also added an even/odd check for
checkargs checkargs
* Added ability to parse doc strings set in defclass declarations, * Added ability to parse doc strings set in defclass declarations,
* Provide bin scripts for both Windows and *nix * Provide bin scripts for both Windows and *nix

View File

@ -6,7 +6,7 @@ Hy
[![Version](https://img.shields.io/pypi/v/hy.svg)](https://pypi.python.org/pypi/hy) [![Version](https://img.shields.io/pypi/v/hy.svg)](https://pypi.python.org/pypi/hy)
[![Coverage Status](https://img.shields.io/coveralls/hylang/hy/master.svg)](https://coveralls.io/r/hylang/hy) [![Coverage Status](https://img.shields.io/coveralls/hylang/hy/master.svg)](https://coveralls.io/r/hylang/hy)
[![XKCD #224](https://raw.github.com/hylang/shyte/18f6925e08684b0e1f52b2cc2c803989cd62cd91/imgs/xkcd.png)](https://xkcd.com/224/) <a href="https://xkcd.com/224/"><img title="We lost the documentation on quantum mechanics. You'll have to decode the regexes yourself." alt="XKCD #224" src="https://raw.github.com/hylang/shyte/18f6925e08684b0e1f52b2cc2c803989cd62cd91/imgs/xkcd.png"></a>
Lisp and Python should love each other. Let's make it happen. [Try it](http://try-hy.appspot.com/). Lisp and Python should love each other. Let's make it happen. [Try it](http://try-hy.appspot.com/).

View File

@ -11,11 +11,11 @@ concise and easy to read.
deliberately captures some form supplied to the macro which may be deliberately captures some form supplied to the macro which may be
referred to by an anaphor (an expression referring to another). referred to by an anaphor (an expression referring to another).
-- Wikipedia (http://en.wikipedia.org/wiki/Anaphoric_macro) -- Wikipedia (https://en.wikipedia.org/wiki/Anaphoric_macro)
To use these macros you need to require the hy.contrib.anaphoric module like so: To use these macros you need to require the hy.contrib.anaphoric module like so:
``(require hy.contrib.anaphoric)`` ``(require [hy.contrib.anaphoric [*]])``
.. _ap-if: .. _ap-if:

View File

@ -25,7 +25,7 @@ Example:
.. code-block:: hy .. code-block:: hy
(require hy.contrib.flow) (require [hy.contrib.flow [case]])
(defn temp-commenter [temp] (defn temp-commenter [temp]
(case temp (case temp
@ -48,7 +48,7 @@ Example:
.. code-block:: hy .. code-block:: hy
(require hy.contrib.flow) (require [hy.contrib.flow [switch]])
(defn temp-commenter [temp] (defn temp-commenter [temp]
(switch temp (switch temp

View File

@ -7,8 +7,9 @@ Contents:
.. toctree:: .. toctree::
:maxdepth: 3 :maxdepth: 3
alias
anaphoric anaphoric
flow
loop loop
multi multi
flow walk
alias

View File

@ -24,7 +24,7 @@ tail-call optimization (TCO) in their Hy code.
position to be implemented as efficiently as goto statements, thus position to be implemented as efficiently as goto statements, thus
allowing efficient structured programming. allowing efficient structured programming.
-- Wikipedia (http://en.wikipedia.org/wiki/Tail_call) -- Wikipedia (https://en.wikipedia.org/wiki/Tail_call)
Macros Macros
====== ======
@ -39,13 +39,13 @@ rebinds the variables set in the recursion point and sends code
execution back to that recursion point. If ``recur`` is used in a execution back to that recursion point. If ``recur`` is used in a
non-tail position, an exception is raised. non-tail position, an exception is raised.
Usage: `(loop bindings &rest body)` Usage: ``(loop bindings &rest body)``
Example: Example:
.. code-block:: hy .. code-block:: hy
(require hy.contrib.loop) (require [hy.contrib.loop [loop]])
(defn factorial [n] (defn factorial [n]
(loop [[i n] [acc 1]] (loop [[i n] [acc 1]]

View File

@ -2,7 +2,7 @@
defmulti defmulti
======== ========
.. versionadded:: 0.10.0 .. versionadded:: 0.11.0
``defmulti``, ``defmethod`` and ``default-method`` lets you define ``defmulti``, ``defmethod`` and ``default-method`` lets you define
multimethods where a dispatching function is used to select between different multimethods where a dispatching function is used to select between different
@ -11,7 +11,7 @@ on the code by `Adam Bard`_.
.. code-block:: clj .. code-block:: clj
=> (require hy.contrib.multi) => (require [hy.contrib.multi [defmulti defmethod]])
=> (defmulti area [shape] => (defmulti area [shape]
... "calculate area of a shape" ... "calculate area of a shape"
... (:type shape)) ... (:type shape))
@ -59,7 +59,7 @@ different implementations can narrow them down.
.. code-block:: clj .. code-block:: clj
=> (require hy.contrib.multi) => (require [hy.contrib.multi [defmulti defmethod]])
=> (defmulti fun [&rest args] => (defmulti fun [&rest args]
... (len args)) ... (len args))

94
docs/contrib/walk.rst Normal file
View File

@ -0,0 +1,94 @@
====
walk
====
.. versionadded:: 0.11.0
Functions
=========
.. _walk:
walk
-----
Usage: `(walk inner outer form)`
``walk`` traverses ``form``, an arbitrary data structure. Applies
``inner`` to each element of form, building up a data structure of the
same type. Applies ``outer`` to the result.
Example:
.. code-block:: hy
=> (import [hy.contrib.walk [walk]])
=> (setv a '(a b c d e f))
=> (walk ord identity a)
(97 98 99 100 101 102)
=> (walk ord first a)
97
postwalk
---------
.. _postwalk:
Usage: `(postwalk f form)`
Performs depth-first, post-order traversal of ``form``. Calls ``f`` on
each sub-form, uses ``f`` 's return value in place of the original.
.. code-block:: hy
=> (import [hy.contrib.walk [postwalk]])
=> (def trail '([1 2 3] [4 [5 6 [7]]]))
=> (defn walking [x]
(print "Walking:" x)
x )
=> (postwalk walking trail)
Walking: 1
Walking: 2
Walking: 3
Walking: (1 2 3)
Walking: 4
Walking: 5
Walking: 6
Walking: 7
Walking: (7)
Walking: (5 6 [7])
Walking: (4 [5 6 [7]])
Walking: ([1 2 3] [4 [5 6 [7]]])
([1 2 3] [4 [5 6 [7]]])
prewalk
--------
.. _prewalk:
Usage: `(prewalk f form)`
Performs depth-first, pre-order traversal of ``form``. Calls ``f`` on
each sub-form, uses ``f`` 's return value in place of the original.
.. code-block:: hy
=> (import [hy.contrib.walk [prewalk]])
=> (def trail '([1 2 3] [4 [5 6 [7]]]))
=> (defn walking [x]
(print "Walking:" x)
x )
=> (prewalk walking trail)
Walking: ([1 2 3] [4 [5 6 [7]]])
Walking: [1 2 3]
Walking: 1
Walking: 2
Walking: 3
Walking: [4 [5 6 [7]]]
Walking: 4
Walking: [5 6 [7]]
Walking: 5
Walking: 6
Walking: [7]
Walking: 7
([1 2 3] [4 [5 6 [7]]])

View File

@ -32,7 +32,7 @@ Do this:
$ . venv/bin/activate $ . venv/bin/activate
or use `virtualenvwrapper <http://virtualenvwrapper.readthedocs.org/en/latest/#introduction>`_ or use `virtualenvwrapper <https://virtualenvwrapper.readthedocs.io/en/latest/#introduction>`_
to create and manage your virtual environment:: to create and manage your virtual environment::
$ mkvirtualenv hy $ mkvirtualenv hy
@ -63,7 +63,7 @@ Test!
===== =====
Tests are located in ``tests/``. We use `nose Tests are located in ``tests/``. We use `nose
<https://nose.readthedocs.org/en/latest/>`_. <https://nose.readthedocs.io/en/latest/>`_.
To run the tests:: To run the tests::

View File

@ -22,7 +22,7 @@ languages.
string. For example, ``foo`` will become ``FOO``. string. For example, ``foo`` will become ``FOO``.
* UTF-8 entities will be encoded using * UTF-8 entities will be encoded using
`punycode <http://en.wikipedia.org/wiki/Punycode>`_ and prefixed with `punycode <https://en.wikipedia.org/wiki/Punycode>`_ and prefixed with
``hy_``. For instance, ```` will become ``hy_w7h``, ```` will become ``hy_``. For instance, ```` will become ``hy_w7h``, ```` will become
``hy_g6h``, and ``i♥u`` will become ``hy_iu_t0x``. ``hy_g6h``, and ``i♥u`` will become ``hy_iu_t0x``.
@ -179,6 +179,79 @@ other case, the first false value will be returned. Example usage:
False False
as->
----
.. versionadded:: 0.12.0
Expands to sequence of assignments to the provided name, starting with head.
The previous result is thus available in the subsequent form. Returns the final
result, and leaves the name bound to it in the local scope. This behaves much
like the other threading macros, but requires you to specify the threading
point per form via the name instead of always the first or last argument.
.. code-block:: clj
;; example how -> and as-> relate
=> (as-> 0 it
... (inc it)
... (inc it))
2
=> (-> 0 inc inc)
2
;; create data for our cuttlefish database
=> (setv data [{:name "hooded cuttlefish"
... :classification {:subgenus "Acanthosepion"
... :species "Sepia prashadi"}
... :discovered {:year 1936
... :name "Ronald Winckworth"}}
... {:name "slender cuttlefish"
... :classification {:subgenus "Doratosepion"
... :species "Sepia braggi"}
... :discovered {:year 1907
... :name "Sir Joseph Cooke Verco"}}])
;; retrieve name of first entry
=> (as-> (first data) it
... (:name it))
'hooded cuttlefish'
;; retrieve species of first entry
=> (as-> (first data) it
... (:classification it)
... (:species it))
'Sepia prashadi'
;; find out who discovered slender cuttlefish
=> (as-> (filter (fn [entry] (= (:name entry)
... "slender cuttlefish")) data) it
... (first it)
... (:discovered it)
... (:name it))
'Sir Joseph Cooke Verco'
;; more convoluted example to load web page and retrieve data from it
=> (import [urllib.request [urlopen]])
=> (as-> (urlopen "http://docs.hylang.org/en/stable/") it
... (.read it)
... (.decode it "utf-8")
... (drop (.index it "Welcome") it)
... (take 30 it)
... (list it)
... (.join "" it))
'Welcome to Hys documentation!
.. note::
In these examples, the REPL will report a tuple (e.g. `('Sepia prashadi',
'Sepia prashadi')`) as the result, but only a single value is actually
returned.
assert assert
------ ------
@ -953,11 +1026,20 @@ that ``import`` can be used.
(import [sys :as systest]) (import [sys :as systest])
;; You can list as many imports as you like of different types. ;; You can list as many imports as you like of different types.
;;
;; Python:
;; from tests.resources import kwtest, function_with_a_dash
;; from os.path import exists, isdir as is_dir, isfile as is_file
;; import sys as systest
(import [tests.resources [kwtest function-with-a-dash]] (import [tests.resources [kwtest function-with-a-dash]]
[os.path [exists isdir isfile]] [os.path [exists
isdir :as dir?
isfile :as file?]]
[sys :as systest]) [sys :as systest])
;; Import all module functions into current namespace ;; Import all module functions into current namespace
;;
;; Python: from sys import *
(import [sys [*]]) (import [sys [*]])
@ -1201,16 +1283,84 @@ alternatively be written using the apostrophe (``'``) symbol.
require require
------- -------
``require`` is used to import macros from a given module. It takes at least one ``require`` is used to import macros from one or more given modules. It allows
parameter specifying the module which macros should be imported. Multiple parameters in all the same formats as ``import``. The ``require`` form itself
modules can be imported with a single ``require``. produces no code in the final program: its effect is purely at compile-time, for
the benefit of macro expansion. Specifically, ``require`` imports each named
module and then makes each requested macro available in the current module.
The following example will import macros from ``module-1`` and ``module-2``: The following are all equivalent ways to call a macro named ``foo`` in the module ``mymodule``:
.. code-block:: clj .. code-block:: clj
(require module-1 module-2) (require mymodule)
(mymodule.foo 1)
(require [mymodule :as M])
(M.foo 1)
(require [mymodule [foo]])
(foo 1)
(require [mymodule [*]])
(foo 1)
(require [mymodule [foo :as bar]])
(bar 1)
Macros that call macros
~~~~~~~~~~~~~~~~~~~~~~~
One aspect of ``require`` that may be surprising is what happens when one
macro's expansion calls another macro. Suppose ``mymodule.hy`` looks like this:
.. code-block:: clj
(defmacro repexpr [n expr]
; Evaluate the expression n times
; and collect the results in a list.
`(list (map (fn [_] ~expr) (range ~n))))
(defmacro foo [n]
`(repexpr ~n (input "Gimme some input: ")))
And then, in your main program, you write:
.. code-block:: clj
(require [mymodule [foo]])
(print (mymodule.foo 3))
Running this raises ``NameError: name 'repexpr' is not defined``, even though
writing ``(print (foo 3))`` in ``mymodule`` works fine. The trouble is that your
main program doesn't have the macro ``repexpr`` available, since it wasn't
imported (and imported under exactly that name, as opposed to a qualified name).
You could do ``(require [mymodule [*]])`` or ``(require [mymodule [foo
repexpr]])``, but a less error-prone approach is to change the definition of
``foo`` to require whatever sub-macros it needs:
.. code-block:: clj
(defmacro foo [n]
`(do
(require mymodule)
(mymodule.repexpr ~n (raw-input "Gimme some input: "))))
It's wise to use ``(require mymodule)`` here rather than ``(require [mymodule
[repexpr]])`` to avoid accidentally shadowing a function named ``repexpr`` in
the main program.
Qualified macro names
~~~~~~~~~~~~~~~~~~~~~
Note that in the current implementation, there's a trick in qualified macro
names, like ``mymodule.foo`` and ``M.foo`` in the above example. These names
aren't actually attributes of module objects; they're just identifiers with
periods in them. In fact, ``mymodule`` and ``M`` aren't defined by these
``require`` forms, even at compile-time. None of this will hurt you unless try
to do introspection of the current module's set of defined macros, which isn't
really supported anyway.
rest / cdr rest / cdr
---------- ----------

View File

@ -35,7 +35,7 @@ Command Line Options
.. cmdoption:: --spy .. cmdoption:: --spy
Print equivalent Python code before executing. For example:: Print equivalent Python code before executing in REPL. For example::
=> (defn salutationsnm [name] (print (+ "Hy " name "!"))) => (defn salutationsnm [name] (print (+ "Hy " name "!")))
def salutationsnm(name): def salutationsnm(name):
@ -45,6 +45,7 @@ Command Line Options
Hy YourName! Hy YourName!
=> =>
`--spy` only works on REPL mode.
.. versionadded:: 0.9.11 .. versionadded:: 0.9.11
.. cmdoption:: --show-tracebacks .. cmdoption:: --show-tracebacks

View File

@ -141,7 +141,7 @@ is ``True``, the function prints Python code instead.
print('Hello World!') print('Hello World!')
.. _emtpy?-fn: .. _empty?-fn:
empty? empty?
------ ------

View File

@ -170,7 +170,7 @@ Cons Cells
cells`_. Cons cells are especially useful to mimic features of "usual" cells`_. Cons cells are especially useful to mimic features of "usual"
LISP variants such as Scheme or Common Lisp. LISP variants such as Scheme or Common Lisp.
.. _cons cells: http://en.wikipedia.org/wiki/Cons .. _cons cells: https://en.wikipedia.org/wiki/Cons
A cons cell is a 2-item object, containing a ``car`` (head) and a A cons cell is a 2-item object, containing a ``car`` (head) and a
``cdr`` (tail). In some Lisp variants, the cons cell is the fundamental ``cdr`` (tail). In some Lisp variants, the cons cell is the fundamental

View File

@ -13,7 +13,7 @@ Quickstart
1. Create a `Virtual Python Environment 1. Create a `Virtual Python Environment
<https://pypi.python.org/pypi/virtualenv>`_. <https://pypi.python.org/pypi/virtualenv>`_.
2. Activate your Virtual Python Environment. 2. Activate your Virtual Python Environment.
3. Install `hy from PyPI <https://pypi.python.org/pypi/hy>`_ with ``pip install hy``. 3. Install `hy from GitHub <https://github.com/hylang/hy>`_ with ``$ pip install git+https://github.com/hylang/hy.git``.
4. Start a REPL with ``hy``. 4. Start a REPL with ``hy``.
5. Type stuff in the REPL:: 5. Type stuff in the REPL::

View File

@ -105,7 +105,7 @@ Layout & Indentation
.. code-block:: clj .. code-block:: clj
(let [foo (bar)] (let [foo (bar)
qux (baz)] qux (baz)]
(foo qux)) (foo qux))

View File

@ -267,7 +267,7 @@ In Hy, you would do:
[true [true
(print "That variable is jussssst right!")]) (print "That variable is jussssst right!")])
What you'll notice is that ``cond`` switches off between a some statement What you'll notice is that ``cond`` switches off between a statement
that is executed and checked conditionally for true or falseness, and that is executed and checked conditionally for true or falseness, and
then a bit of code to execute if it turns out to be true. You'll also then a bit of code to execute if it turns out to be true. You'll also
notice that the ``else`` is implemented at the end simply by checking notice that the ``else`` is implemented at the end simply by checking
@ -292,7 +292,7 @@ You can do the following:
(if (try-some-thing) (if (try-some-thing)
(do (do
(print "this is if true") (print "this is if true")
(print "and why not, let's keep talking about how true it is!)) (print "and why not, let's keep talking about how true it is!"))
(print "this one's still simply just false")) (print "this one's still simply just false"))
You can see that we used ``do`` to wrap multiple statements. If you're You can see that we used ``do`` to wrap multiple statements. If you're
@ -568,15 +568,18 @@ Macros are useful when one wishes to extend Hy or write their own
language on top of that. Many features of Hy are macros, like ``when``, language on top of that. Many features of Hy are macros, like ``when``,
``cond`` and ``->``. ``cond`` and ``->``.
To use macros defined in a different module, it is not enough to What if you want to use a macro that's defined in a different
``import`` the module, because importing happens at run-time, while we module? The special form ``import`` won't help, because it merely
would need macros at compile-time. Instead of importing the module translates to a Python ``import`` statement that's executed at
with macros, ``require`` must be used: run-time, and macros are expanded at compile-time, that is,
during the translate from Hy to Python. Instead, use ``require``,
which imports the module and makes macros available at
compile-time. ``require`` uses the same syntax as ``import``.
.. code-block:: clj .. code-block:: clj
=> (require tutorial.macros) => (require tutorial.macros)
=> (rev (1 2 3 +)) => (tutorial.macros.rev (1 2 3 +))
6 6
Hy <-> Python interop Hy <-> Python interop

View File

@ -19,7 +19,11 @@
# DEALINGS IN THE SOFTWARE. # DEALINGS IN THE SOFTWARE.
from hy.version import __version__, __appname__ # NOQA __appname__ = 'hy'
try:
from hy.version import __version__
except ImportError:
__version__ = 'unknown'
from hy.models.expression import HyExpression # NOQA from hy.models.expression import HyExpression # NOQA

View File

@ -173,8 +173,8 @@ def ideas_macro():
""")]) """)])
require("hy.cmdline", "__console__") require("hy.cmdline", "__console__", all_macros=True)
require("hy.cmdline", "__main__") require("hy.cmdline", "__main__", all_macros=True)
SIMPLE_TRACEBACKS = True SIMPLE_TRACEBACKS = True
@ -274,7 +274,7 @@ def cmdline_handler(scriptname, argv):
parser.add_argument("--spy", action="store_true", parser.add_argument("--spy", action="store_true",
help="print equivalent Python code before executing") help="print equivalent Python code before executing")
parser.add_argument("-v", action="version", version=VERSION) parser.add_argument("-v", "--version", action="version", version=VERSION)
parser.add_argument("--show-tracebacks", action="store_true", parser.add_argument("--show-tracebacks", action="store_true",
help="show complete tracebacks for Hy exceptions") help="show complete tracebacks for Hy exceptions")

View File

@ -503,19 +503,13 @@ class HyASTCompiler(object):
for expr in exprs: for expr in exprs:
if expr in ll_keywords: if expr in ll_keywords:
if expr == "&rest" and lambda_keyword is None: if expr == "&optional":
lambda_keyword = expr
elif expr == "&optional":
if len(defaults) > 0: if len(defaults) > 0:
raise HyTypeError(expr, raise HyTypeError(expr,
"There can only be &optional " "There can only be &optional "
"arguments or one &key argument") "arguments or one &key argument")
lambda_keyword = expr lambda_keyword = expr
elif expr == "&key": elif expr in ("&rest", "&key", "&kwonly", "&kwargs"):
lambda_keyword = expr
elif expr == "&kwonly":
lambda_keyword = expr
elif expr == "&kwargs":
lambda_keyword = expr lambda_keyword = expr
else: else:
raise HyTypeError(expr, raise HyTypeError(expr,
@ -1516,7 +1510,8 @@ class HyASTCompiler(object):
gen.append(ast.comprehension( gen.append(ast.comprehension(
target=target, target=target,
iter=gen_res.force_expr, iter=gen_res.force_expr,
ifs=[])) ifs=[],
is_async=False))
if cond.expr: if cond.expr:
gen[-1].ifs.append(cond.expr) gen[-1].ifs.append(cond.expr)
@ -1742,10 +1737,45 @@ class HyASTCompiler(object):
"unimport" it after we've completed `thing' so that we don't pollute "unimport" it after we've completed `thing' so that we don't pollute
other envs. other envs.
""" """
expression.pop(0) for entry in expression[1:]:
for entry in expression: if isinstance(entry, HySymbol):
__import__(entry) # Import it fo' them macros. # e.g., (require foo)
require(entry, self.module_name) __import__(entry)
require(entry, self.module_name, all_macros=True,
prefix=entry)
elif isinstance(entry, HyList) and len(entry) == 2:
# e.g., (require [foo [bar baz :as MyBaz bing]])
# or (require [foo [*]])
module, names = entry
if not isinstance(names, HyList):
raise HyTypeError(names,
"(require) name lists should be HyLists")
__import__(module)
if '*' in names:
if len(names) != 1:
raise HyTypeError(names, "* in a (require) name list "
"must be on its own")
require(module, self.module_name, all_macros=True)
else:
assignments = {}
while names:
if len(names) > 1 and names[1] == HyKeyword(":as"):
k, _, v = names[:3]
del names[:3]
assignments[k] = v
else:
symbol = names.pop(0)
assignments[symbol] = symbol
require(module, self.module_name, assignments=assignments)
elif (isinstance(entry, HyList) and len(entry) == 3
and entry[1] == HyKeyword(":as")):
# e.g., (require [foo :as bar])
module, _, prefix = entry
__import__(module)
require(module, self.module_name, all_macros=True,
prefix=prefix)
else:
raise HyTypeError(entry, "unrecognized (require) syntax")
return Result() return Result()
@builds("and") @builds("and")
@ -1910,7 +1940,6 @@ class HyASTCompiler(object):
col_offset=child.start_column) col_offset=child.start_column)
return ret return ret
@builds("+")
@builds("*") @builds("*")
@builds("/") @builds("/")
@builds("//") @builds("//")
@ -1918,8 +1947,7 @@ class HyASTCompiler(object):
if len(expression) > 2: if len(expression) > 2:
return self.compile_maths_expression(expression) return self.compile_maths_expression(expression)
else: else:
id_op = {"+": HyInteger(0), "*": HyInteger(1), "/": HyInteger(1), id_op = {"*": HyInteger(1), "/": HyInteger(1), "//": HyInteger(1)}
"//": HyInteger(1)}
op = expression.pop(0) op = expression.pop(0)
arg = expression.pop(0) if expression else id_op[op] arg = expression.pop(0) if expression else id_op[op]
@ -1930,20 +1958,34 @@ class HyASTCompiler(object):
]).replace(expression) ]).replace(expression)
return self.compile_maths_expression(expr) return self.compile_maths_expression(expr)
@builds("-") def compile_maths_expression_additive(self, expression):
@checkargs(min=1)
def compile_maths_expression_sub(self, expression):
if len(expression) > 2: if len(expression) > 2:
return self.compile_maths_expression(expression) return self.compile_maths_expression(expression)
else: else:
arg = expression[1] op = {"+": ast.UAdd, "-": ast.USub}[expression.pop(0)]()
arg = expression.pop(0)
ret = self.compile(arg) ret = self.compile(arg)
ret += ast.UnaryOp(op=ast.USub(), ret += ast.UnaryOp(op=op,
operand=ret.force_expr, operand=ret.force_expr,
lineno=arg.start_line, lineno=arg.start_line,
col_offset=arg.start_column) col_offset=arg.start_column)
return ret return ret
@builds("+")
def compile_maths_expression_add(self, expression):
if len(expression) == 1:
# Nullary +
return ast.Num(n=long_type(0),
lineno=expression.start_line,
col_offset=expression.start_column)
else:
return self.compile_maths_expression_additive(expression)
@builds("-")
@checkargs(min=1)
def compile_maths_expression_sub(self, expression):
return self.compile_maths_expression_additive(expression)
@builds("+=") @builds("+=")
@builds("/=") @builds("/=")
@builds("//=") @builds("//=")
@ -2449,6 +2491,9 @@ class HyASTCompiler(object):
raise HyTypeError(name, ("received a `%s' instead of a symbol " raise HyTypeError(name, ("received a `%s' instead of a symbol "
"for macro name" % type(name).__name__)) "for macro name" % type(name).__name__))
name = HyString(name).replace(name) name = HyString(name).replace(name)
for kw in ("&kwonly", "&kwargs", "&key"):
if kw in expression[0]:
raise HyTypeError(name, "macros cannot use %s" % kw)
new_expression = HyExpression([ new_expression = HyExpression([
HySymbol("with_decorator"), HySymbol("with_decorator"),
HyExpression([HySymbol("hy.macros.macro"), name]), HyExpression([HySymbol("hy.macros.macro"), name]),

View File

@ -17,4 +17,5 @@
(defmacro defnc [name args &rest body] (defmacro defnc [name args &rest body]
`(def ~name (fnc [~@args] ~@body))) `(do (require hy.contrib.curry)
(def ~name (hy.contrib.curry.fnc [~@args] ~@body))))

View File

@ -70,7 +70,8 @@
(defmacro defnr [name lambda-list &rest body] (defmacro defnr [name lambda-list &rest body]
(if (not (= (type name) HySymbol)) (if (not (= (type name) HySymbol))
(macro-error name "defnr takes a name as first argument")) (macro-error name "defnr takes a name as first argument"))
`(setv ~name (fnr ~lambda-list ~@body))) `(do (require hy.contrib.loop)
(setv ~name (hy.contrib.loop.fnr ~lambda-list ~@body))))
(defmacro/g! loop [bindings &rest body] (defmacro/g! loop [bindings &rest body]
@ -87,5 +88,6 @@
;; and erroring if not is a giant TODO. ;; and erroring if not is a giant TODO.
(let [fnargs (map (fn [x] (first x)) bindings) (let [fnargs (map (fn [x] (first x)) bindings)
initargs (map second bindings)] initargs (map second bindings)]
`(do (defnr ~g!recur-fn [~@fnargs] ~@body) `(do (require hy.contrib.loop)
(hy.contrib.loop.defnr ~g!recur-fn [~@fnargs] ~@body)
(~g!recur-fn ~@initargs)))) (~g!recur-fn ~@initargs))))

View File

@ -9,19 +9,23 @@
(defn ~name ~params (defn ~name ~params
(do ~@code))))) (do ~@code)))))
(defn rwm [name path method params code]
`(do (require hy.contrib.meth)
(hy.contrib.meth.route-with-methods ~name ~path ~method ~params ~@code)))
;; Some macro examples ;; Some macro examples
(defmacro route [name path params &rest code] (defmacro route [name path params &rest code]
"Get request" "Get request"
`(route-with-methods ~name ~path ["GET"] ~params ~@code)) (rwm name path ["GET"] params code))
(defmacro post-route [name path params &rest code] (defmacro post-route [name path params &rest code]
"Post request" "Post request"
`(route-with-methods ~name ~path ["POST"] ~params ~@code)) (rwm name path ["POST"] params code))
(defmacro put-route [name path params &rest code] (defmacro put-route [name path params &rest code]
"Put request" "Put request"
`(route-with-methods ~name ~path ["PUT"] ~params ~@code)) (rwm name path ["PUT"] params code))
(defmacro delete-route [name path params &rest code] (defmacro delete-route [name path params &rest code]
"Delete request" "Delete request"
`(route-with-methods ~name ~path ["DELETE"] ~params ~@code)) (rwm name path ["DELETE"] params code))

View File

@ -39,10 +39,6 @@
(import [hy.lex [LexException PrematureEndOfInput tokenize]]) (import [hy.lex [LexException PrematureEndOfInput tokenize]])
(import [hy.compiler [HyASTCompiler]]) (import [hy.compiler [HyASTCompiler]])
(defn _numeric-check [x]
(if (not (numeric? x))
(raise (TypeError (.format "{0!r} is not a number" x)))))
(defn butlast [coll] (defn butlast [coll]
"Returns coll except of last element." "Returns coll except of last element."
(drop-last 1 coll)) (drop-last 1 coll))
@ -66,7 +62,6 @@
(defn dec [n] (defn dec [n]
"Decrement n by 1" "Decrement n by 1"
(_numeric-check n)
(- n 1)) (- n 1))
(defn disassemble [tree &optional [codegen false]] (defn disassemble [tree &optional [codegen false]]
@ -170,7 +165,6 @@
(defn even? [n] (defn even? [n]
"Return true if n is an even number" "Return true if n is an even number"
(_numeric-check n)
(= (% n 2) 0)) (= (% n 2) 0))
(defn every? [pred coll] (defn every? [pred coll]
@ -239,7 +233,6 @@
(defn inc [n] (defn inc [n]
"Increment n by 1" "Increment n by 1"
(_numeric-check n)
(+ n 1)) (+ n 1))
(defn instance? [klass x] (defn instance? [klass x]
@ -325,7 +318,6 @@
(defn neg? [n] (defn neg? [n]
"Return true if n is < 0" "Return true if n is < 0"
(_numeric-check n)
(< n 0)) (< n 0))
(defn none? [x] (defn none? [x]
@ -347,7 +339,6 @@
(defn odd? [n] (defn odd? [n]
"Return true if n is an odd number" "Return true if n is an odd number"
(_numeric-check n)
(= (% n 2) 1)) (= (% n 2) 1))
(def -sentinel (object)) (def -sentinel (object))
@ -364,7 +355,6 @@
(defn pos? [n] (defn pos? [n]
"Return true if n is > 0" "Return true if n is > 0"
(_numeric_check n)
(> n 0)) (> n 0))
(defn rest [coll] (defn rest [coll]
@ -415,7 +405,6 @@
(defn zero? [n] (defn zero? [n]
"Return true if n is 0" "Return true if n is 0"
(_numeric_check n)
(= n 0)) (= n 0))
(defn read [&optional [from-file sys.stdin] (defn read [&optional [from-file sys.stdin]

View File

@ -30,6 +30,16 @@
[hy.models.symbol [HySymbol]] [hy.models.symbol [HySymbol]]
[hy._compat [PY33 PY34]]) [hy._compat [PY33 PY34]])
(defmacro as-> [head name &rest rest]
"Expands to sequence of assignments to the provided name, starting with head.
The previous result is thus available in the subsequent form. Returns the
final result, and leaves the name bound to it in the local scope. This behaves
much like the other threading macros, but requires you to specify the threading
point per form via the name instead of always the first or last argument."
`(do (setv
~name ~head
~@(interleave (repeat name) rest))
~name))
(defmacro with [args &rest body] (defmacro with [args &rest body]
"shorthand for nested with* loops: "shorthand for nested with* loops:

View File

@ -30,7 +30,7 @@
(if (zero? count) (if (zero? count)
(raise (TypeError "Need at least 1 argument to add/concatenate")) (raise (TypeError "Need at least 1 argument to add/concatenate"))
(if (= count 1) (if (= count 1)
(get args 0) (operator.pos (get args 0))
(reduce operator.add args))))) (reduce operator.add args)))))

View File

@ -45,12 +45,12 @@ def ast_compile(ast, filename, mode):
def import_buffer_to_hst(buf): def import_buffer_to_hst(buf):
"""Import content from buf and return an Hy AST.""" """Import content from buf and return a Hy AST."""
return tokenize(buf + "\n") return tokenize(buf + "\n")
def import_file_to_hst(fpath): def import_file_to_hst(fpath):
"""Import content from fpath and return an Hy AST.""" """Import content from fpath and return a Hy AST."""
try: try:
with open(fpath, 'r', encoding='utf-8') as f: with open(fpath, 'r', encoding='utf-8') as f:
return import_buffer_to_hst(f.read()) return import_buffer_to_hst(f.read())
@ -89,6 +89,15 @@ def import_file_to_module(module_name, fpath):
return mod return mod
def import_file_to_globals(env, module_name, fpath):
""" Import content from fpath and puts it into the dict provided
(e.g., for use in a REPL)
"""
mod = import_file_to_module(module_name, fpath)
for k, v in mod.__dict__.items():
env[k] = v
def import_buffer_to_module(module_name, buf): def import_buffer_to_module(module_name, buf):
try: try:
_ast = import_buffer_to_ast(buf, module_name) _ast = import_buffer_to_ast(buf, module_name)

View File

@ -84,22 +84,36 @@ def reader(name):
return _ return _
def require(source_module, target_module): def require(source_module, target_module,
"""Load the macros from `source_module` in the namespace of all_macros=False, assignments={}, prefix=""):
`target_module`. """Load macros from `source_module` in the namespace of
`target_module`. `assignments` maps old names to new names, but is
ignored if `all_macros` is true. If `prefix` is nonempty, it is
prepended to the name of each imported macro. (This means you get
macros named things like "mymacromodule.mymacro", which looks like
an attribute of a module, although it's actually just a symbol
with a period in its name.)
This function is called from the `require` special form in the compiler. This function is called from the `require` special form in the compiler.
""" """
macros = _hy_macros[source_module]
refs = _hy_macros[target_module]
for name, macro in macros.items():
refs[name] = macro
readers = _hy_reader[source_module] seen_names = set()
reader_refs = _hy_reader[target_module] if prefix:
for name, reader in readers.items(): prefix += "."
reader_refs[name] = reader
for d in _hy_macros, _hy_reader:
for name, macro in d[source_module].items():
seen_names.add(name)
if all_macros:
d[target_module][prefix + name] = macro
elif name in assignments:
d[target_module][prefix + assignments[name]] = macro
if not all_macros:
unseen = frozenset(assignments.keys()).difference(seen_names)
if unseen:
raise ImportError("cannot require names: " + repr(list(unseen)))
def load_macros(module_name): def load_macros(module_name):

View File

@ -18,19 +18,16 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE. # DEALINGS IN THE SOFTWARE.
from hy.models import _wrappers, wrap_value
from hy.models.list import HyList from hy.models.list import HyList
from functools import reduce
class HySet(HyList): class HySet(HyList):
""" """
Hy set (actually a list that pretends to be a set) Hy set (just a representation of a set)
""" """
def __init__(self, items):
items = sorted(items)
items = list(reduce(lambda r, v: v in r and r or r+[v], items, []))
super(HySet, self).__init__(items)
def __repr__(self): def __repr__(self):
return "#{%s}" % (" ".join([repr(x) for x in self])) return "#{%s}" % (" ".join([repr(x) for x in self]))
_wrappers[set] = lambda s: HySet(wrap_value(x) for x in s)

View File

@ -1,23 +0,0 @@
# Copyright (c) 2013 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.
__appname__ = "hy"
__version__ = "0.11.0"

View File

@ -22,24 +22,23 @@
import os import os
import re import re
import sys import sys
import subprocess
from setuptools import find_packages, setup from setuptools import find_packages, setup
os.chdir(os.path.split(os.path.abspath(__file__))[0])
PKG = "hy" PKG = "hy"
VERSIONFILE = os.path.join(PKG, "version.py") VERSIONFILE = os.path.join(PKG, "version.py")
verstr = "unknown"
try: try:
verstrline = open(VERSIONFILE, "rt").read() __version__ = (subprocess.check_output
except EnvironmentError: (["git", "describe", "--tags", "--dirty"])
pass # Okay, there is no version file. .decode('ASCII').strip()
else: .replace('-', '+', 1).replace('-', '.'))
VSRE = r"^__version__ = ['\"]([^'\"]*)['\"]" with open(VERSIONFILE, "wt") as o:
mo = re.search(VSRE, verstrline, re.M) o.write("__version__ = {!r}\n".format(__version__))
if mo: except subprocess.CalledProcessError:
__version__ = mo.group(1) __version__ = "unknown"
else:
msg = "if %s.py exists, it is required to be well-formed" % VERSIONFILE
raise RuntimeError(msg)
long_description = """Hy is a Python <--> Lisp layer. It helps long_description = """Hy is a Python <--> Lisp layer. It helps
make things work nicer, and lets Python and the Hy lisp variant play make things work nicer, and lets Python and the Hy lisp variant play

View File

@ -249,6 +249,20 @@ def test_ast_good_import_from():
can_compile("(import [x [y]])") can_compile("(import [x [y]])")
def test_ast_require():
"Make sure AST respects (require) syntax"
can_compile("(require tests.resources.tlib)")
can_compile("(require [tests.resources.tlib [qplah parald]])")
can_compile("(require [tests.resources.tlib [*]])")
can_compile("(require [tests.resources.tlib :as foobar])")
can_compile("(require [tests.resources.tlib [qplah :as quiz]])")
can_compile("(require [tests.resources.tlib [qplah :as quiz parald]])")
cant_compile("(require [tests.resources.tlib])")
cant_compile("(require [tests.resources.tlib [* qplah]])")
cant_compile("(require [tests.resources.tlib [qplah *]])")
cant_compile("(require [tests.resources.tlib [* *]])")
def test_ast_good_get(): def test_ast_good_get():
"Make sure AST can compile valid get" "Make sure AST can compile valid get"
can_compile("(get x y)") can_compile("(get x y)")
@ -397,6 +411,7 @@ def test_lambda_list_keywords_rest():
""" Ensure we can compile functions with lambda list keywords.""" """ Ensure we can compile functions with lambda list keywords."""
can_compile("(fn (x &rest xs) (print xs))") can_compile("(fn (x &rest xs) (print xs))")
cant_compile("(fn (x &rest xs &rest ys) (print xs))") cant_compile("(fn (x &rest xs &rest ys) (print xs))")
can_compile("(fn (&optional a &rest xs) (print xs))")
def test_lambda_list_keywords_key(): def test_lambda_list_keywords_key():
@ -410,6 +425,7 @@ def test_lambda_list_keywords_kwargs():
""" Ensure we can compile functions with &kwargs.""" """ Ensure we can compile functions with &kwargs."""
can_compile("(fn (x &kwargs kw) (list x kw))") can_compile("(fn (x &kwargs kw) (list x kw))")
cant_compile("(fn (x &kwargs xs &kwargs ys) (list x xs ys))") cant_compile("(fn (x &kwargs xs &kwargs ys) (list x xs ys))")
can_compile("(fn (&optional x &kwargs kw) (list x kw))")
def test_lambda_list_keywords_kwonly(): def test_lambda_list_keywords_kwonly():

View File

@ -230,6 +230,16 @@ def test_sets():
HyExpression([HySymbol("baz"), HySymbol("quux")]) HyExpression([HySymbol("baz"), HySymbol("quux")])
])] ])]
# Duplicate items in a literal set should be okay (and should
# be preserved).
objs = tokenize("#{1 2 1 1 2 1}")
assert objs == [HySet([HyInteger(n) for n in [1, 2, 1, 1, 2, 1]])]
assert len(objs[0]) == 6
# https://github.com/hylang/hy/issues/1120
objs = tokenize("#{a 1}")
assert objs == [HySet([HySymbol("a"), HyInteger(1)])]
def test_nospace(): def test_nospace():
""" Ensure we can tokenize without spaces if we have to """ """ Ensure we can tokenize without spaces if we have to """

View File

@ -5,4 +5,4 @@ hyset = HySet([3, 1, 2, 2])
def test_set(): def test_set():
assert hyset == [1, 2, 3] assert hyset == [3, 1, 2, 2]

View File

@ -1,4 +1,4 @@
(require hy.contrib.alias) (require [hy.contrib.alias [defn-alias]])
(defn test-defn-alias [] (defn test-defn-alias []
(defn-alias [tda-main tda-a1 tda-a2] [] :bazinga) (defn-alias [tda-main tda-a1 tda-a2] [] :bazinga)

View File

@ -19,7 +19,7 @@
;; DEALINGS IN THE SOFTWARE. ;; DEALINGS IN THE SOFTWARE.
(import [hy.errors [HyMacroExpansionError]]) (import [hy.errors [HyMacroExpansionError]])
(require hy.contrib.anaphoric) (require [hy.contrib.anaphoric [*]])
;;;; some simple helpers ;;;; some simple helpers

View File

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

View File

@ -1,4 +1,4 @@
(require hy.contrib.curry) (require [hy.contrib.curry [defnc]])
(defnc s [x y z] ((x z) (y z))) ; λxyz.xz(yz) (defnc s [x y z] ((x z) (y z))) ; λxyz.xz(yz)

View File

@ -1,4 +1,4 @@
(require hy.contrib.loop) (require [hy.contrib.loop [loop]])
(import sys) (import sys)
(defn tco-sum [x y] (defn tco-sum [x y]

View File

@ -1,4 +1,4 @@
(require hy.contrib.meth) (require [hy.contrib.meth [route post-route put-route delete-route]])
(defclass FakeMeth [] (defclass FakeMeth []
"Mocking decorator class" "Mocking decorator class"

View File

@ -19,7 +19,7 @@
;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
;; DEALINGS IN THE SOFTWARE. ;; DEALINGS IN THE SOFTWARE.
(require hy.contrib.multi) (require [hy.contrib.multi [defmulti]])
(defn test-different-signatures [] (defn test-different-signatures []

View File

@ -19,6 +19,8 @@
;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
;; DEALINGS IN THE SOFTWARE. ;; DEALINGS IN THE SOFTWARE.
(import [hy._compat [PY3]])
;;;; some simple helpers ;;;; some simple helpers
(defn assert-true [x] (defn assert-true [x]
@ -33,6 +35,12 @@
(defn assert-nil [x] (defn assert-nil [x]
(assert (is x nil))) (assert (is x nil)))
(defn assert-requires-num [f]
(for [x ["foo" [] None]]
(try (f x)
(except [TypeError] True)
(else (assert False)))))
(defn test-coll? [] (defn test-coll? []
"NATIVE: testing coll?" "NATIVE: testing coll?"
(assert-true (coll? [1 2 3])) (assert-true (coll? [1 2 3]))
@ -66,12 +74,7 @@
(assert-equal 0 (dec 1)) (assert-equal 0 (dec 1))
(assert-equal -1 (dec 0)) (assert-equal -1 (dec 0))
(assert-equal 0 (dec (dec 2))) (assert-equal 0 (dec (dec 2)))
(try (do (dec "foo") (assert False)) (assert-requires-num dec))
(except [e [TypeError]] (assert (in "not a number" (str e)))))
(try (do (dec []) (assert False))
(except [e [TypeError]] (assert (in "not a number" (str e)))))
(try (do (dec None) (assert False))
(except [e [TypeError]] (assert (in "not a number" (str e))))))
(defn test-setv [] (defn test-setv []
"NATIVE: testing setv mutation" "NATIVE: testing setv mutation"
@ -173,12 +176,7 @@
(assert-true (even? -2)) (assert-true (even? -2))
(assert-false (even? 1)) (assert-false (even? 1))
(assert-true (even? 0)) (assert-true (even? 0))
(try (even? "foo") (assert-requires-num even?))
(except [e [TypeError]] (assert (in "not a number" (str e)))))
(try (even? [])
(except [e [TypeError]] (assert (in "not a number" (str e)))))
(try (even? None)
(except [e [TypeError]] (assert (in "not a number" (str e))))))
(defn test-every? [] (defn test-every? []
"NATIVE: testing the every? function" "NATIVE: testing the every? function"
@ -263,12 +261,11 @@
"NATIVE: testing the inc function" "NATIVE: testing the inc function"
(assert-equal 3 (inc 2)) (assert-equal 3 (inc 2))
(assert-equal 0 (inc -1)) (assert-equal 0 (inc -1))
(try (do (inc "foo") (assert False)) (assert-requires-num inc)
(except [e [TypeError]] (assert (in "not a number" (str e)))))
(try (do (inc []) (assert False)) (defclass X [object]
(except [e [TypeError]] (assert (in "not a number" (str e))))) [__add__ (fn [self other] (.format "__add__ got {}" other))])
(try (do (inc None) (assert False)) (assert-equal (inc (X)) "__add__ got 1"))
(except [e [TypeError]] (assert (in "not a number" (str e))))))
(defn test-instance [] (defn test-instance []
"NATIVE: testing instance? function" "NATIVE: testing instance? function"
@ -394,24 +391,14 @@
(assert-true (neg? -2)) (assert-true (neg? -2))
(assert-false (neg? 1)) (assert-false (neg? 1))
(assert-false (neg? 0)) (assert-false (neg? 0))
(try (do (neg? "foo") (assert False)) (when PY3
(except [e [TypeError]] (assert (in "not a number" (str e))))) (assert-requires-num neg?)))
(try (do (neg? []) (assert False))
(except [e [TypeError]] (assert (in "not a number" (str e)))))
(try (do (neg? None) (assert False))
(except [e [TypeError]] (assert (in "not a number" (str e))))))
(defn test-zero [] (defn test-zero []
"NATIVE: testing the zero? function" "NATIVE: testing the zero? function"
(assert-false (zero? -2)) (assert-false (zero? -2))
(assert-false (zero? 1)) (assert-false (zero? 1))
(assert-true (zero? 0)) (assert-true (zero? 0)))
(try (do (zero? "foo") (assert False))
(except [e [TypeError]] (assert (in "not a number" (str e)))))
(try (do (zero? []) (assert False))
(except [e [TypeError]] (assert (in "not a number" (str e)))))
(try (do (zero? None) (assert False))
(except [e [TypeError]] (assert (in "not a number" (str e))))))
(defn test-none [] (defn test-none []
"NATIVE: testing for `is None`" "NATIVE: testing for `is None`"
@ -463,12 +450,7 @@
(assert-true (odd? -3)) (assert-true (odd? -3))
(assert-true (odd? 1)) (assert-true (odd? 1))
(assert-false (odd? 0)) (assert-false (odd? 0))
(try (do (odd? "foo") (assert False)) (assert-requires-num odd?))
(except [e [TypeError]] (assert (in "not a number" (str e)))))
(try (do (odd? []) (assert False))
(except [e [TypeError]] (assert (in "not a number" (str e)))))
(try (do (odd? None) (assert False))
(except [e [TypeError]] (assert (in "not a number" (str e))))))
(defn test-partition [] (defn test-partition []
"NATIVE: testing the partition function" "NATIVE: testing the partition function"
@ -500,12 +482,8 @@
(assert-true (pos? 2)) (assert-true (pos? 2))
(assert-false (pos? -1)) (assert-false (pos? -1))
(assert-false (pos? 0)) (assert-false (pos? 0))
(try (do (pos? "foo") (assert False)) (when PY3
(except [e [TypeError]] (assert (in "not a number" (str e))))) (assert-requires-num pos?)))
(try (do (pos? []) (assert False))
(except [e [TypeError]] (assert (in "not a number" (str e)))))
(try (do (pos? None) (assert False))
(except [e [TypeError]] (assert (in "not a number" (str e))))))
(defn test-remove [] (defn test-remove []
"NATIVE: testing the remove function" "NATIVE: testing the remove function"

View File

@ -47,6 +47,7 @@
(defn test-sets [] (defn test-sets []
"NATIVE: test sets work right" "NATIVE: test sets work right"
(assert (= #{1 2 3 4} (| #{1 2} #{3 4}))) (assert (= #{1 2 3 4} (| #{1 2} #{3 4})))
(assert (= (type #{1 2 3 4}) set))
(assert (= #{} (set)))) (assert (= #{} (set))))
@ -587,6 +588,29 @@
["X" "B" "C" "D"]))) ["X" "B" "C" "D"])))
(defn test-as-threading []
"NATIVE: test as threading macro"
(setv data [{:name "hooded cuttlefish"
:classification {:subgenus "Acanthosepion"
:species "Sepia prashadi"}
:discovered {:year 1936
:name "Ronald Winckworth"}}
{:name "slender cuttlefish"
:classification {:subgenus "Doratosepion"
:species "Sepia braggi"}
:discovered {:year 1907
:name "Sir Joseph Cooke Verco"}}])
(assert (= (as-> (first data) x
(:name x))
"hooded cuttlefish"))
(assert (= (as-> (filter (fn [entry] (= (:name entry)
"slender cuttlefish")) data) x
(first x)
(:discovered x)
(:name x))
"Sir Joseph Cooke Verco")))
(defn test-assoc [] (defn test-assoc []
"NATIVE: test assoc" "NATIVE: test assoc"
(setv vals {"one" "two"}) (setv vals {"one" "two"})
@ -1100,11 +1124,34 @@
(defn test-require [] (defn test-require []
"NATIVE: test requiring macros from python code" "NATIVE: test requiring macros from python code"
(try (try (qplah 1 2 3 4)
(assert (= "this won't happen" (qplah 1 2 3 4))) (except [NameError] True)
(except [NameError])) (else (assert False)))
(try (parald 1 2 3 4)
(except [NameError] True)
(else (assert False)))
(require [tests.resources.tlib [qplah]])
(assert (= (qplah 1 2 3) [8 1 2 3]))
(try (parald 1 2 3 4)
(except [NameError] True)
(else (assert False)))
(require tests.resources.tlib) (require tests.resources.tlib)
(assert (= [1 2 3] (qplah 1 2 3)))) (assert (= (tests.resources.tlib.parald 1 2 3) [9 1 2 3]))
(try (parald 1 2 3 4)
(except [NameError] True)
(else (assert False)))
(require [tests.resources.tlib :as T])
(assert (= (T.parald 1 2 3) [9 1 2 3]))
(try (parald 1 2 3 4)
(except [NameError] True)
(else (assert False)))
(require [tests.resources.tlib [parald :as p]])
(assert (= (p 1 2 3) [9 1 2 3]))
(try (parald 1 2 3 4)
(except [NameError] True)
(else (assert False)))
(require [tests.resources.tlib [*]])
(assert (= (parald 1 2 3) [9 1 2 3])))
(defn test-require-native [] (defn test-require-native []
@ -1124,7 +1171,7 @@
(assert (= x [3 2 1])) (assert (= x [3 2 1]))
"success") "success")
(except [NameError] "failure")))) (except [NameError] "failure"))))
(require tests.native_tests.native_macros) (require [tests.native_tests.native_macros [rev]])
(assert (= "success" (assert (= "success"
(try (try
(do (setv x []) (do (setv x [])

View File

@ -28,6 +28,18 @@
(assert (= 0 (+))))) (assert (= 0 (+)))))
(defn test-add-unary []
"NATIVE: test that unary + calls __pos__"
(defclass X [object]
[__pos__ (fn [self] "called __pos__")])
(assert (= (+ (X)) "called __pos__"))
; Make sure the shadowed version works, too.
(setv f +)
(assert (= (f (X)) "called __pos__")))
(setv test_div (fn [] (setv test_div (fn []
"NATIVE: Test division" "NATIVE: Test division"
(assert (= 25 (/ 100 2 2))) (assert (= 25 (/ 100 2 2)))

View File

@ -1,3 +1,5 @@
(import [hy.errors [HyTypeError]])
(defmacro rev [&rest body] (defmacro rev [&rest body]
"Execute the `body` statements in reverse" "Execute the `body` statements in reverse"
(quasiquote (do (unquote-splice (list (reversed body)))))) (quasiquote (do (unquote-splice (list (reversed body))))))
@ -37,6 +39,9 @@
(defmacro a-dict [] {1 2}) (defmacro a-dict [] {1 2})
(assert (= (a-dict) {1 2})) (assert (= (a-dict) {1 2}))
(defmacro a-set [] #{1 2})
(assert (= (a-set) #{1 2}))
(defmacro a-none []) (defmacro a-none [])
(assert (= (a-none) None)) (assert (= (a-none) None))
@ -48,6 +53,26 @@
(defmacro bar [x y] (defmacro bar [x y]
(foo x y)) (foo x y))
(defn test-macro-kw []
"NATIVE: test that an error is raised when &kwonly, &kwargs, or &key is used in a macro"
(try
(eval '(defmacro f [&kwonly a b]))
(except [e HyTypeError]
(assert (= e.message "macros cannot use &kwonly")))
(else (assert false)))
(try
(eval '(defmacro f [&kwargs kw]))
(except [e HyTypeError]
(assert (= e.message "macros cannot use &kwargs")))
(else (assert false)))
(try
(eval '(defmacro f [&key {"kw" "xyz"}]))
(except [e HyTypeError]
(assert (= e.message "macros cannot use &key")))
(else (assert false))))
(defn test-fn-calling-macro [] (defn test-fn-calling-macro []
"NATIVE: test macro calling a plain function" "NATIVE: test macro calling a plain function"
(assert (= 3 (bar 1 2)))) (assert (= 3 (bar 1 2))))

View File

@ -8,13 +8,9 @@
(assert (= (x 1 2 3 4) 10)) (assert (= (x 1 2 3 4) 10))
(assert (= (x 1 2 3 4 5) 15)) (assert (= (x 1 2 3 4 5) 15))
; with strings ; with strings
(assert (= (x "a")
"a"))
(assert (= (x "a" "b" "c") (assert (= (x "a" "b" "c")
"abc")) "abc"))
; with lists ; with lists
(assert (= (x ["a"])
["a"]))
(assert (= (x ["a"] ["b"] ["c"]) (assert (= (x ["a"] ["b"] ["c"])
["a" "b" "c"])))) ["a" "b" "c"]))))

View File

@ -1,7 +1,12 @@
from hy.macros import macro from hy.macros import macro
from hy import HyList from hy import HyList, HyInteger
@macro("qplah") @macro("qplah")
def tmac(*tree): def tmac(*tree):
return HyList(tree) return HyList((HyInteger(8), ) + tree)
@macro("parald")
def tmac2(*tree):
return HyList((HyInteger(9), ) + tree)

View File

@ -1,5 +1,5 @@
[tox] [tox]
envlist = py27,pypy,py33,py34,py35,flake8 envlist = py27,pypy,py33,py34,py35,py36,flake8
skipsdist = True skipsdist = True
[testenv] [testenv]