Merge branch 'master' into pr/1081

This commit is contained in:
Tuukka Turto 2016-10-20 19:23:57 +03:00
commit d969ed63d6
27 changed files with 242 additions and 102 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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,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
non-tail position, an exception is raised.
Usage: `(loop bindings &rest body)`
Usage: ``(loop bindings &rest body)``
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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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)
@ -1910,7 +1905,6 @@ class HyASTCompiler(object):
col_offset=child.start_column)
return ret
@builds("+")
@builds("*")
@builds("/")
@builds("//")
@ -1918,8 +1912,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 +1923,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 +2456,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]),

View File

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

View File

@ -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 arument."
`(do (setv
~name ~head
~@(interleave (repeat name) rest))
~name))
(defmacro with [args &rest body]
"shorthand for nested with* loops:

View File

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

View File

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

View File

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

View File

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

View File

@ -5,4 +5,4 @@ hyset = HySet([3, 1, 2, 2])
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
;; 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"

View File

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

View File

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

View File

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

View File

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

View File

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