Merge branch 'master' into pr/1048

This commit is contained in:
Tuukka Turto 2016-10-20 19:19:21 +03:00
commit 1459eea697
33 changed files with 243 additions and 112 deletions

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,7 +11,7 @@ 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:

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,7 +39,7 @@ 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:

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``.
@ -953,11 +953,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 [*]])

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

View File

@ -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)
@ -1910,7 +1905,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 +1912,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 +1923,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 +2456,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

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

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

@ -397,6 +397,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 +411,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

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

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