Merge branch 'master' into multimethod
Conflicts: docs/contrib/multi.rst
This commit is contained in:
commit
77bc767907
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
/hy/version.py
|
||||
*.pyc
|
||||
*swp
|
||||
*hy*egg*
|
||||
|
@ -4,6 +4,8 @@ matrix:
|
||||
include:
|
||||
- python: 3.5
|
||||
env: TOXENV=py35
|
||||
- python: 3.6-dev
|
||||
env: TOXENV=py36
|
||||
env:
|
||||
- TOXENV=py27
|
||||
- TOXENV=py33
|
||||
|
1
AUTHORS
1
AUTHORS
@ -69,3 +69,4 @@
|
||||
* Andrew Savchyn <dev@scorpil.com>
|
||||
* Lev Kravinsky <kravinskylev@gmail.com>
|
||||
* Luna Lunapiena <lunacodes@gmail.com>
|
||||
* Jakub Wilk <jwilk@jwilk.net>
|
||||
|
@ -7,7 +7,9 @@ helps in making Hy more awesome.
|
||||
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
|
||||
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`_)
|
||||
|
||||
- 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
|
||||
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.
|
||||
|
||||
- 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
|
||||
.. _issue tracker: https://github.com/hylang/hy/issues
|
||||
.. _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
|
||||
|
6
Makefile
6
Makefile
@ -23,7 +23,7 @@ all:
|
||||
@echo ""
|
||||
|
||||
docs:
|
||||
make -C docs html
|
||||
$(MAKE) -C docs html
|
||||
|
||||
upload: r
|
||||
python setup.py sdist upload
|
||||
@ -72,10 +72,10 @@ coveralls:
|
||||
|
||||
clean:
|
||||
@find . -name "*.pyc" -exec rm {} \;
|
||||
@find -name __pycache__ -delete
|
||||
@find . -name __pycache__ -delete
|
||||
@${RM} -r -f .tox
|
||||
@${RM} -r -f dist
|
||||
@${RM} -r -f *.egg-info
|
||||
@${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
2
NEWS
@ -280,7 +280,7 @@ Changes from Hy 0.9.10
|
||||
* Clarified rest / cdr, cleaned up require
|
||||
* Make with return the last expression from its branch
|
||||
* 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
|
||||
* Added ability to parse doc strings set in defclass declarations,
|
||||
* Provide bin scripts for both Windows and *nix
|
||||
|
@ -6,7 +6,7 @@ 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)
|
||||
|
||||
[![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/).
|
||||
|
||||
|
@ -11,11 +11,11 @@ concise and easy to read.
|
||||
deliberately captures some form supplied to the macro which may be
|
||||
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:
|
||||
|
||||
``(require hy.contrib.anaphoric)``
|
||||
``(require [hy.contrib.anaphoric [*]])``
|
||||
|
||||
.. _ap-if:
|
||||
|
||||
|
@ -25,7 +25,7 @@ Example:
|
||||
|
||||
.. code-block:: hy
|
||||
|
||||
(require hy.contrib.flow)
|
||||
(require [hy.contrib.flow [case]])
|
||||
|
||||
(defn temp-commenter [temp]
|
||||
(case temp
|
||||
@ -48,7 +48,7 @@ Example:
|
||||
|
||||
.. code-block:: hy
|
||||
|
||||
(require hy.contrib.flow)
|
||||
(require [hy.contrib.flow [switch]])
|
||||
|
||||
(defn temp-commenter [temp]
|
||||
(switch temp
|
||||
|
@ -7,8 +7,9 @@ Contents:
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
|
||||
alias
|
||||
anaphoric
|
||||
flow
|
||||
loop
|
||||
multi
|
||||
flow
|
||||
alias
|
||||
walk
|
||||
|
@ -24,7 +24,7 @@ tail-call optimization (TCO) in their Hy code.
|
||||
position to be implemented as efficiently as goto statements, thus
|
||||
allowing efficient structured programming.
|
||||
|
||||
-- Wikipedia (http://en.wikipedia.org/wiki/Tail_call)
|
||||
-- Wikipedia (https://en.wikipedia.org/wiki/Tail_call)
|
||||
|
||||
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
|
||||
non-tail position, an exception is raised.
|
||||
|
||||
Usage: `(loop bindings &rest body)`
|
||||
Usage: ``(loop bindings &rest body)``
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: hy
|
||||
|
||||
(require hy.contrib.loop)
|
||||
(require [hy.contrib.loop [loop]])
|
||||
|
||||
(defn factorial [n]
|
||||
(loop [[i n] [acc 1]]
|
||||
|
@ -2,7 +2,7 @@
|
||||
defmulti
|
||||
========
|
||||
|
||||
.. versionadded:: 0.10.0
|
||||
.. versionadded:: 0.11.0
|
||||
|
||||
``defmulti``, ``defmethod`` and ``default-method`` lets you define
|
||||
multimethods where a dispatching function is used to select between different
|
||||
@ -11,7 +11,7 @@ on the code by `Adam Bard`_.
|
||||
|
||||
.. code-block:: clj
|
||||
|
||||
=> (require hy.contrib.multi)
|
||||
=> (require [hy.contrib.multi [defmulti defmethod]])
|
||||
=> (defmulti area [shape]
|
||||
... "calculate area of a shape"
|
||||
... (:type shape))
|
||||
@ -59,7 +59,7 @@ different implementations can narrow them down.
|
||||
|
||||
.. code-block:: clj
|
||||
|
||||
=> (require hy.contrib.multi)
|
||||
=> (require [hy.contrib.multi [defmulti defmethod]])
|
||||
=> (defmulti fun [&rest args]
|
||||
... (len args))
|
||||
|
||||
|
94
docs/contrib/walk.rst
Normal file
94
docs/contrib/walk.rst
Normal 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]]])
|
@ -32,7 +32,7 @@ Do this:
|
||||
|
||||
$ . 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::
|
||||
|
||||
$ mkvirtualenv hy
|
||||
@ -63,7 +63,7 @@ Test!
|
||||
=====
|
||||
|
||||
Tests are located in ``tests/``. We use `nose
|
||||
<https://nose.readthedocs.org/en/latest/>`_.
|
||||
<https://nose.readthedocs.io/en/latest/>`_.
|
||||
|
||||
To run the tests::
|
||||
|
||||
|
@ -22,7 +22,7 @@ languages.
|
||||
string. For example, ``foo`` will become ``FOO``.
|
||||
|
||||
* 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_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
|
||||
|
||||
|
||||
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 Hy’s 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
|
||||
------
|
||||
|
||||
@ -953,11 +1026,20 @@ that ``import`` can be used.
|
||||
(import [sys :as systest])
|
||||
|
||||
;; 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]]
|
||||
[os.path [exists isdir isfile]]
|
||||
[os.path [exists
|
||||
isdir :as dir?
|
||||
isfile :as file?]]
|
||||
[sys :as systest])
|
||||
|
||||
;; Import all module functions into current namespace
|
||||
;;
|
||||
;; Python: from sys import *
|
||||
(import [sys [*]])
|
||||
|
||||
|
||||
@ -1201,16 +1283,84 @@ alternatively be written using the apostrophe (``'``) symbol.
|
||||
require
|
||||
-------
|
||||
|
||||
``require`` is used to import macros from a given module. It takes at least one
|
||||
parameter specifying the module which macros should be imported. Multiple
|
||||
modules can be imported with a single ``require``.
|
||||
``require`` is used to import macros from one or more given modules. It allows
|
||||
parameters in all the same formats as ``import``. The ``require`` form itself
|
||||
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
|
||||
|
||||
(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
|
||||
----------
|
||||
|
@ -35,7 +35,7 @@ Command Line Options
|
||||
|
||||
.. 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 "!")))
|
||||
def salutationsnm(name):
|
||||
@ -45,6 +45,7 @@ Command Line Options
|
||||
Hy YourName!
|
||||
=>
|
||||
|
||||
`--spy` only works on REPL mode.
|
||||
.. versionadded:: 0.9.11
|
||||
|
||||
.. cmdoption:: --show-tracebacks
|
||||
|
@ -141,7 +141,7 @@ is ``True``, the function prints Python code instead.
|
||||
print('Hello World!')
|
||||
|
||||
|
||||
.. _emtpy?-fn:
|
||||
.. _empty?-fn:
|
||||
|
||||
empty?
|
||||
------
|
||||
|
@ -170,7 +170,7 @@ Cons Cells
|
||||
cells`_. Cons cells are especially useful to mimic features of "usual"
|
||||
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
|
||||
``cdr`` (tail). In some Lisp variants, the cons cell is the fundamental
|
||||
|
@ -13,7 +13,7 @@ Quickstart
|
||||
1. Create a `Virtual Python Environment
|
||||
<https://pypi.python.org/pypi/virtualenv>`_.
|
||||
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``.
|
||||
5. Type stuff in the REPL::
|
||||
|
||||
|
@ -105,7 +105,7 @@ Layout & Indentation
|
||||
|
||||
.. code-block:: clj
|
||||
|
||||
(let [foo (bar)]
|
||||
(let [foo (bar)
|
||||
qux (baz)]
|
||||
(foo qux))
|
||||
|
||||
|
@ -267,7 +267,7 @@ In Hy, you would do:
|
||||
[true
|
||||
(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
|
||||
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
|
||||
@ -292,7 +292,7 @@ You can do the following:
|
||||
(if (try-some-thing)
|
||||
(do
|
||||
(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"))
|
||||
|
||||
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``,
|
||||
``cond`` and ``->``.
|
||||
|
||||
To use macros defined in a different module, it is not enough to
|
||||
``import`` the module, because importing happens at run-time, while we
|
||||
would need macros at compile-time. Instead of importing the module
|
||||
with macros, ``require`` must be used:
|
||||
What if you want to use a macro that's defined in a different
|
||||
module? The special form ``import`` won't help, because it merely
|
||||
translates to a Python ``import`` statement that's executed at
|
||||
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
|
||||
|
||||
=> (require tutorial.macros)
|
||||
=> (rev (1 2 3 +))
|
||||
=> (tutorial.macros.rev (1 2 3 +))
|
||||
6
|
||||
|
||||
Hy <-> Python interop
|
||||
|
@ -19,7 +19,11 @@
|
||||
# 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
|
||||
|
@ -173,8 +173,8 @@ def ideas_macro():
|
||||
|
||||
""")])
|
||||
|
||||
require("hy.cmdline", "__console__")
|
||||
require("hy.cmdline", "__main__")
|
||||
require("hy.cmdline", "__console__", all_macros=True)
|
||||
require("hy.cmdline", "__main__", all_macros=True)
|
||||
|
||||
SIMPLE_TRACEBACKS = True
|
||||
|
||||
@ -274,7 +274,7 @@ def cmdline_handler(scriptname, argv):
|
||||
parser.add_argument("--spy", action="store_true",
|
||||
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",
|
||||
help="show complete tracebacks for Hy exceptions")
|
||||
|
@ -503,19 +503,13 @@ class HyASTCompiler(object):
|
||||
for expr in exprs:
|
||||
|
||||
if expr in ll_keywords:
|
||||
if expr == "&rest" and lambda_keyword is None:
|
||||
lambda_keyword = expr
|
||||
elif expr == "&optional":
|
||||
if expr == "&optional":
|
||||
if len(defaults) > 0:
|
||||
raise HyTypeError(expr,
|
||||
"There can only be &optional "
|
||||
"arguments or one &key argument")
|
||||
lambda_keyword = expr
|
||||
elif expr == "&key":
|
||||
lambda_keyword = expr
|
||||
elif expr == "&kwonly":
|
||||
lambda_keyword = expr
|
||||
elif expr == "&kwargs":
|
||||
elif expr in ("&rest", "&key", "&kwonly", "&kwargs"):
|
||||
lambda_keyword = expr
|
||||
else:
|
||||
raise HyTypeError(expr,
|
||||
@ -1516,7 +1510,8 @@ class HyASTCompiler(object):
|
||||
gen.append(ast.comprehension(
|
||||
target=target,
|
||||
iter=gen_res.force_expr,
|
||||
ifs=[]))
|
||||
ifs=[],
|
||||
is_async=False))
|
||||
|
||||
if 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
|
||||
other envs.
|
||||
"""
|
||||
expression.pop(0)
|
||||
for entry in expression:
|
||||
__import__(entry) # Import it fo' them macros.
|
||||
require(entry, self.module_name)
|
||||
for entry in expression[1:]:
|
||||
if isinstance(entry, HySymbol):
|
||||
# e.g., (require foo)
|
||||
__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()
|
||||
|
||||
@builds("and")
|
||||
@ -1910,7 +1940,6 @@ class HyASTCompiler(object):
|
||||
col_offset=child.start_column)
|
||||
return ret
|
||||
|
||||
@builds("+")
|
||||
@builds("*")
|
||||
@builds("/")
|
||||
@builds("//")
|
||||
@ -1918,8 +1947,7 @@ class HyASTCompiler(object):
|
||||
if len(expression) > 2:
|
||||
return self.compile_maths_expression(expression)
|
||||
else:
|
||||
id_op = {"+": HyInteger(0), "*": HyInteger(1), "/": HyInteger(1),
|
||||
"//": HyInteger(1)}
|
||||
id_op = {"*": HyInteger(1), "/": HyInteger(1), "//": HyInteger(1)}
|
||||
|
||||
op = expression.pop(0)
|
||||
arg = expression.pop(0) if expression else id_op[op]
|
||||
@ -1930,20 +1958,34 @@ class HyASTCompiler(object):
|
||||
]).replace(expression)
|
||||
return self.compile_maths_expression(expr)
|
||||
|
||||
@builds("-")
|
||||
@checkargs(min=1)
|
||||
def compile_maths_expression_sub(self, expression):
|
||||
def compile_maths_expression_additive(self, expression):
|
||||
if len(expression) > 2:
|
||||
return self.compile_maths_expression(expression)
|
||||
else:
|
||||
arg = expression[1]
|
||||
op = {"+": ast.UAdd, "-": ast.USub}[expression.pop(0)]()
|
||||
arg = expression.pop(0)
|
||||
ret = self.compile(arg)
|
||||
ret += ast.UnaryOp(op=ast.USub(),
|
||||
ret += ast.UnaryOp(op=op,
|
||||
operand=ret.force_expr,
|
||||
lineno=arg.start_line,
|
||||
col_offset=arg.start_column)
|
||||
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("//=")
|
||||
@ -2449,6 +2491,9 @@ class HyASTCompiler(object):
|
||||
raise HyTypeError(name, ("received a `%s' instead of a symbol "
|
||||
"for macro name" % type(name).__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([
|
||||
HySymbol("with_decorator"),
|
||||
HyExpression([HySymbol("hy.macros.macro"), name]),
|
||||
|
@ -17,4 +17,5 @@
|
||||
|
||||
|
||||
(defmacro defnc [name args &rest body]
|
||||
`(def ~name (fnc [~@args] ~@body)))
|
||||
`(do (require hy.contrib.curry)
|
||||
(def ~name (hy.contrib.curry.fnc [~@args] ~@body))))
|
||||
|
@ -70,7 +70,8 @@
|
||||
(defmacro defnr [name lambda-list &rest body]
|
||||
(if (not (= (type name) HySymbol))
|
||||
(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]
|
||||
@ -87,5 +88,6 @@
|
||||
;; and erroring if not is a giant TODO.
|
||||
(let [fnargs (map (fn [x] (first x)) 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))))
|
||||
|
@ -9,19 +9,23 @@
|
||||
(defn ~name ~params
|
||||
(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
|
||||
(defmacro route [name path params &rest code]
|
||||
"Get request"
|
||||
`(route-with-methods ~name ~path ["GET"] ~params ~@code))
|
||||
(rwm name path ["GET"] params code))
|
||||
|
||||
(defmacro post-route [name path params &rest code]
|
||||
"Post request"
|
||||
`(route-with-methods ~name ~path ["POST"] ~params ~@code))
|
||||
(rwm name path ["POST"] params code))
|
||||
|
||||
(defmacro put-route [name path params &rest code]
|
||||
"Put request"
|
||||
`(route-with-methods ~name ~path ["PUT"] ~params ~@code))
|
||||
(rwm name path ["PUT"] params code))
|
||||
|
||||
(defmacro delete-route [name path params &rest code]
|
||||
"Delete request"
|
||||
`(route-with-methods ~name ~path ["DELETE"] ~params ~@code))
|
||||
(rwm name path ["DELETE"] params code))
|
||||
|
@ -39,10 +39,6 @@
|
||||
(import [hy.lex [LexException PrematureEndOfInput tokenize]])
|
||||
(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]
|
||||
"Returns coll except of last element."
|
||||
(drop-last 1 coll))
|
||||
@ -66,7 +62,6 @@
|
||||
|
||||
(defn dec [n]
|
||||
"Decrement n by 1"
|
||||
(_numeric-check n)
|
||||
(- n 1))
|
||||
|
||||
(defn disassemble [tree &optional [codegen false]]
|
||||
@ -170,7 +165,6 @@
|
||||
|
||||
(defn even? [n]
|
||||
"Return true if n is an even number"
|
||||
(_numeric-check n)
|
||||
(= (% n 2) 0))
|
||||
|
||||
(defn every? [pred coll]
|
||||
@ -239,7 +233,6 @@
|
||||
|
||||
(defn inc [n]
|
||||
"Increment n by 1"
|
||||
(_numeric-check n)
|
||||
(+ n 1))
|
||||
|
||||
(defn instance? [klass x]
|
||||
@ -325,7 +318,6 @@
|
||||
|
||||
(defn neg? [n]
|
||||
"Return true if n is < 0"
|
||||
(_numeric-check n)
|
||||
(< n 0))
|
||||
|
||||
(defn none? [x]
|
||||
@ -347,7 +339,6 @@
|
||||
|
||||
(defn odd? [n]
|
||||
"Return true if n is an odd number"
|
||||
(_numeric-check n)
|
||||
(= (% n 2) 1))
|
||||
|
||||
(def -sentinel (object))
|
||||
@ -364,7 +355,6 @@
|
||||
|
||||
(defn pos? [n]
|
||||
"Return true if n is > 0"
|
||||
(_numeric_check n)
|
||||
(> n 0))
|
||||
|
||||
(defn rest [coll]
|
||||
@ -415,7 +405,6 @@
|
||||
|
||||
(defn zero? [n]
|
||||
"Return true if n is 0"
|
||||
(_numeric_check n)
|
||||
(= n 0))
|
||||
|
||||
(defn read [&optional [from-file sys.stdin]
|
||||
|
@ -30,6 +30,16 @@
|
||||
[hy.models.symbol [HySymbol]]
|
||||
[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]
|
||||
"shorthand for nested with* loops:
|
||||
|
@ -30,7 +30,7 @@
|
||||
(if (zero? count)
|
||||
(raise (TypeError "Need at least 1 argument to add/concatenate"))
|
||||
(if (= count 1)
|
||||
(get args 0)
|
||||
(operator.pos (get args 0))
|
||||
(reduce operator.add args)))))
|
||||
|
||||
|
||||
|
@ -45,12 +45,12 @@ def ast_compile(ast, filename, mode):
|
||||
|
||||
|
||||
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")
|
||||
|
||||
|
||||
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:
|
||||
with open(fpath, 'r', encoding='utf-8') as f:
|
||||
return import_buffer_to_hst(f.read())
|
||||
@ -89,6 +89,15 @@ def import_file_to_module(module_name, fpath):
|
||||
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):
|
||||
try:
|
||||
_ast = import_buffer_to_ast(buf, module_name)
|
||||
|
36
hy/macros.py
36
hy/macros.py
@ -84,22 +84,36 @@ def reader(name):
|
||||
return _
|
||||
|
||||
|
||||
def require(source_module, target_module):
|
||||
"""Load the macros from `source_module` in the namespace of
|
||||
`target_module`.
|
||||
def require(source_module, target_module,
|
||||
all_macros=False, assignments={}, prefix=""):
|
||||
"""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.
|
||||
|
||||
"""
|
||||
macros = _hy_macros[source_module]
|
||||
refs = _hy_macros[target_module]
|
||||
for name, macro in macros.items():
|
||||
refs[name] = macro
|
||||
|
||||
readers = _hy_reader[source_module]
|
||||
reader_refs = _hy_reader[target_module]
|
||||
for name, reader in readers.items():
|
||||
reader_refs[name] = reader
|
||||
seen_names = set()
|
||||
if prefix:
|
||||
prefix += "."
|
||||
|
||||
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):
|
||||
|
@ -18,19 +18,16 @@
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
# DEALINGS IN THE SOFTWARE.
|
||||
|
||||
from hy.models import _wrappers, wrap_value
|
||||
from hy.models.list import HyList
|
||||
from functools import reduce
|
||||
|
||||
|
||||
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):
|
||||
return "#{%s}" % (" ".join([repr(x) for x in self]))
|
||||
|
||||
_wrappers[set] = lambda s: HySet(wrap_value(x) for x in s)
|
||||
|
@ -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"
|
23
setup.py
23
setup.py
@ -22,24 +22,23 @@
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
from setuptools import find_packages, setup
|
||||
|
||||
os.chdir(os.path.split(os.path.abspath(__file__))[0])
|
||||
|
||||
PKG = "hy"
|
||||
VERSIONFILE = os.path.join(PKG, "version.py")
|
||||
verstr = "unknown"
|
||||
try:
|
||||
verstrline = open(VERSIONFILE, "rt").read()
|
||||
except EnvironmentError:
|
||||
pass # Okay, there is no version file.
|
||||
else:
|
||||
VSRE = r"^__version__ = ['\"]([^'\"]*)['\"]"
|
||||
mo = re.search(VSRE, verstrline, re.M)
|
||||
if mo:
|
||||
__version__ = mo.group(1)
|
||||
else:
|
||||
msg = "if %s.py exists, it is required to be well-formed" % VERSIONFILE
|
||||
raise RuntimeError(msg)
|
||||
__version__ = (subprocess.check_output
|
||||
(["git", "describe", "--tags", "--dirty"])
|
||||
.decode('ASCII').strip()
|
||||
.replace('-', '+', 1).replace('-', '.'))
|
||||
with open(VERSIONFILE, "wt") as o:
|
||||
o.write("__version__ = {!r}\n".format(__version__))
|
||||
except subprocess.CalledProcessError:
|
||||
__version__ = "unknown"
|
||||
|
||||
long_description = """Hy is a Python <--> Lisp layer. It helps
|
||||
make things work nicer, and lets Python and the Hy lisp variant play
|
||||
|
@ -249,6 +249,20 @@ def test_ast_good_import_from():
|
||||
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():
|
||||
"Make sure AST can compile valid get"
|
||||
can_compile("(get x y)")
|
||||
@ -397,6 +411,7 @@ def test_lambda_list_keywords_rest():
|
||||
""" Ensure we can compile functions with lambda list keywords."""
|
||||
can_compile("(fn (x &rest xs) (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():
|
||||
@ -410,6 +425,7 @@ def test_lambda_list_keywords_kwargs():
|
||||
""" Ensure we can compile functions with &kwargs."""
|
||||
can_compile("(fn (x &kwargs kw) (list x kw))")
|
||||
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():
|
||||
|
@ -230,6 +230,16 @@ def test_sets():
|
||||
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():
|
||||
""" Ensure we can tokenize without spaces if we have to """
|
||||
|
@ -5,4 +5,4 @@ hyset = HySet([3, 1, 2, 2])
|
||||
|
||||
|
||||
def test_set():
|
||||
assert hyset == [1, 2, 3]
|
||||
assert hyset == [3, 1, 2, 2]
|
||||
|
@ -1,4 +1,4 @@
|
||||
(require hy.contrib.alias)
|
||||
(require [hy.contrib.alias [defn-alias]])
|
||||
|
||||
(defn test-defn-alias []
|
||||
(defn-alias [tda-main tda-a1 tda-a2] [] :bazinga)
|
||||
|
@ -19,7 +19,7 @@
|
||||
;; DEALINGS IN THE SOFTWARE.
|
||||
|
||||
(import [hy.errors [HyMacroExpansionError]])
|
||||
(require hy.contrib.anaphoric)
|
||||
(require [hy.contrib.anaphoric [*]])
|
||||
|
||||
;;;; some simple helpers
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
(import [hy.contrib.botsbuildbots [*]])
|
||||
(require hy.contrib.botsbuildbots)
|
||||
(require [hy.contrib.botsbuildbots [Botsbuildbots]])
|
||||
|
||||
(defn test-botsbuildbots []
|
||||
(assert (> (len (first (Botsbuildbots))) 50)))
|
||||
|
@ -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)
|
||||
|
@ -1,4 +1,4 @@
|
||||
(require hy.contrib.loop)
|
||||
(require [hy.contrib.loop [loop]])
|
||||
(import sys)
|
||||
|
||||
(defn tco-sum [x y]
|
||||
|
@ -1,4 +1,4 @@
|
||||
(require hy.contrib.meth)
|
||||
(require [hy.contrib.meth [route post-route put-route delete-route]])
|
||||
|
||||
(defclass FakeMeth []
|
||||
"Mocking decorator class"
|
||||
|
@ -19,7 +19,7 @@
|
||||
;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
;; DEALINGS IN THE SOFTWARE.
|
||||
|
||||
(require hy.contrib.multi)
|
||||
(require [hy.contrib.multi [defmulti]])
|
||||
|
||||
|
||||
(defn test-different-signatures []
|
||||
|
@ -19,6 +19,8 @@
|
||||
;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
;; DEALINGS IN THE SOFTWARE.
|
||||
|
||||
(import [hy._compat [PY3]])
|
||||
|
||||
;;;; some simple helpers
|
||||
|
||||
(defn assert-true [x]
|
||||
@ -33,6 +35,12 @@
|
||||
(defn assert-nil [x]
|
||||
(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? []
|
||||
"NATIVE: testing coll?"
|
||||
(assert-true (coll? [1 2 3]))
|
||||
@ -66,12 +74,7 @@
|
||||
(assert-equal 0 (dec 1))
|
||||
(assert-equal -1 (dec 0))
|
||||
(assert-equal 0 (dec (dec 2)))
|
||||
(try (do (dec "foo") (assert False))
|
||||
(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))))))
|
||||
(assert-requires-num dec))
|
||||
|
||||
(defn test-setv []
|
||||
"NATIVE: testing setv mutation"
|
||||
@ -173,12 +176,7 @@
|
||||
(assert-true (even? -2))
|
||||
(assert-false (even? 1))
|
||||
(assert-true (even? 0))
|
||||
(try (even? "foo")
|
||||
(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))))))
|
||||
(assert-requires-num even?))
|
||||
|
||||
(defn test-every? []
|
||||
"NATIVE: testing the every? function"
|
||||
@ -263,12 +261,11 @@
|
||||
"NATIVE: testing the inc function"
|
||||
(assert-equal 3 (inc 2))
|
||||
(assert-equal 0 (inc -1))
|
||||
(try (do (inc "foo") (assert False))
|
||||
(except [e [TypeError]] (assert (in "not a number" (str e)))))
|
||||
(try (do (inc []) (assert False))
|
||||
(except [e [TypeError]] (assert (in "not a number" (str e)))))
|
||||
(try (do (inc None) (assert False))
|
||||
(except [e [TypeError]] (assert (in "not a number" (str e))))))
|
||||
(assert-requires-num inc)
|
||||
|
||||
(defclass X [object]
|
||||
[__add__ (fn [self other] (.format "__add__ got {}" other))])
|
||||
(assert-equal (inc (X)) "__add__ got 1"))
|
||||
|
||||
(defn test-instance []
|
||||
"NATIVE: testing instance? function"
|
||||
@ -394,24 +391,14 @@
|
||||
(assert-true (neg? -2))
|
||||
(assert-false (neg? 1))
|
||||
(assert-false (neg? 0))
|
||||
(try (do (neg? "foo") (assert False))
|
||||
(except [e [TypeError]] (assert (in "not a number" (str e)))))
|
||||
(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))))))
|
||||
(when PY3
|
||||
(assert-requires-num neg?)))
|
||||
|
||||
(defn test-zero []
|
||||
"NATIVE: testing the zero? function"
|
||||
(assert-false (zero? -2))
|
||||
(assert-false (zero? 1))
|
||||
(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))))))
|
||||
(assert-true (zero? 0)))
|
||||
|
||||
(defn test-none []
|
||||
"NATIVE: testing for `is None`"
|
||||
@ -463,12 +450,7 @@
|
||||
(assert-true (odd? -3))
|
||||
(assert-true (odd? 1))
|
||||
(assert-false (odd? 0))
|
||||
(try (do (odd? "foo") (assert False))
|
||||
(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))))))
|
||||
(assert-requires-num odd?))
|
||||
|
||||
(defn test-partition []
|
||||
"NATIVE: testing the partition function"
|
||||
@ -500,12 +482,8 @@
|
||||
(assert-true (pos? 2))
|
||||
(assert-false (pos? -1))
|
||||
(assert-false (pos? 0))
|
||||
(try (do (pos? "foo") (assert False))
|
||||
(except [e [TypeError]] (assert (in "not a number" (str e)))))
|
||||
(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))))))
|
||||
(when PY3
|
||||
(assert-requires-num pos?)))
|
||||
|
||||
(defn test-remove []
|
||||
"NATIVE: testing the remove function"
|
||||
|
@ -47,6 +47,7 @@
|
||||
(defn test-sets []
|
||||
"NATIVE: test sets work right"
|
||||
(assert (= #{1 2 3 4} (| #{1 2} #{3 4})))
|
||||
(assert (= (type #{1 2 3 4}) set))
|
||||
(assert (= #{} (set))))
|
||||
|
||||
|
||||
@ -587,6 +588,29 @@
|
||||
["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 []
|
||||
"NATIVE: test assoc"
|
||||
(setv vals {"one" "two"})
|
||||
@ -1100,11 +1124,34 @@
|
||||
|
||||
(defn test-require []
|
||||
"NATIVE: test requiring macros from python code"
|
||||
(try
|
||||
(assert (= "this won't happen" (qplah 1 2 3 4)))
|
||||
(except [NameError]))
|
||||
(try (qplah 1 2 3 4)
|
||||
(except [NameError] True)
|
||||
(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)
|
||||
(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 []
|
||||
@ -1124,7 +1171,7 @@
|
||||
(assert (= x [3 2 1]))
|
||||
"success")
|
||||
(except [NameError] "failure"))))
|
||||
(require tests.native_tests.native_macros)
|
||||
(require [tests.native_tests.native_macros [rev]])
|
||||
(assert (= "success"
|
||||
(try
|
||||
(do (setv x [])
|
||||
|
@ -28,6 +28,18 @@
|
||||
(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 []
|
||||
"NATIVE: Test division"
|
||||
(assert (= 25 (/ 100 2 2)))
|
||||
|
@ -1,3 +1,5 @@
|
||||
(import [hy.errors [HyTypeError]])
|
||||
|
||||
(defmacro rev [&rest body]
|
||||
"Execute the `body` statements in reverse"
|
||||
(quasiquote (do (unquote-splice (list (reversed body))))))
|
||||
@ -37,6 +39,9 @@
|
||||
(defmacro a-dict [] {1 2})
|
||||
(assert (= (a-dict) {1 2}))
|
||||
|
||||
(defmacro a-set [] #{1 2})
|
||||
(assert (= (a-set) #{1 2}))
|
||||
|
||||
(defmacro a-none [])
|
||||
(assert (= (a-none) None))
|
||||
|
||||
@ -48,6 +53,26 @@
|
||||
(defmacro bar [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 []
|
||||
"NATIVE: test macro calling a plain function"
|
||||
(assert (= 3 (bar 1 2))))
|
||||
|
@ -8,13 +8,9 @@
|
||||
(assert (= (x 1 2 3 4) 10))
|
||||
(assert (= (x 1 2 3 4 5) 15))
|
||||
; with strings
|
||||
(assert (= (x "a")
|
||||
"a"))
|
||||
(assert (= (x "a" "b" "c")
|
||||
"abc"))
|
||||
; with lists
|
||||
(assert (= (x ["a"])
|
||||
["a"]))
|
||||
(assert (= (x ["a"] ["b"] ["c"])
|
||||
["a" "b" "c"]))))
|
||||
|
||||
|
@ -1,7 +1,12 @@
|
||||
from hy.macros import macro
|
||||
from hy import HyList
|
||||
from hy import HyList, HyInteger
|
||||
|
||||
|
||||
@macro("qplah")
|
||||
def tmac(*tree):
|
||||
return HyList(tree)
|
||||
return HyList((HyInteger(8), ) + tree)
|
||||
|
||||
|
||||
@macro("parald")
|
||||
def tmac2(*tree):
|
||||
return HyList((HyInteger(9), ) + tree)
|
||||
|
Loading…
x
Reference in New Issue
Block a user