Merge branch 'master' into pr/526

This commit is contained in:
Tuukka Turto 2014-05-02 10:34:37 +03:00
commit b1a928037e
22 changed files with 423 additions and 225 deletions

View File

@ -1,19 +1,18 @@
language: python language: python
python: python:
- "pypy" - "pypy"
- "2.6"
- "2.7" - "2.7"
- "3.2" - "3.2"
- "3.3" - "3.3"
- "2.6" - "3.4"
# command to install dependencies # command to install dependencies
install: install:
- pip install -r requirements.txt - pip install -r requirements-dev.txt
- pip install coveralls - pip install coveralls
- if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install argparse importlib unittest2 astor; fi - if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install unittest2; fi # needs for running tests
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install astor; fi - pip install --allow-all-external -e .
- if [[ $TRAVIS_PYTHON_VERSION == 'pypy' ]]; then pip install astor; fi # command to run tests
- python setup.py -q install
# # command to run tests
script: make travis script: make travis
after_success: coveralls after_success: coveralls
notifications: notifications:

View File

@ -45,3 +45,4 @@
* kirbyfan64 <kirbyfan64@users.noreply.github.com> * kirbyfan64 <kirbyfan64@users.noreply.github.com>
* Brendan Curran-Johnson <brendan@bcjbcj.ca> * Brendan Curran-Johnson <brendan@bcjbcj.ca>
* Ivan Kozik <ivan@ludios.org> * Ivan Kozik <ivan@ludios.org>
* Allison Kaptur <allison.kaptur@gmail.com>

80
NEWS
View File

@ -1,3 +1,83 @@
Changes from 0.9.12
0.10.0 - the "oh man I'm late for PyCon" release
Thanks to theanalyst (Abhi) for getting the release notes
together. You're the best!
- Hy Society
[ Breaking Changes ]
We're calling this release 0.10 because we broke
API. Sorry about that. We've removed kwapply in
favor of using `apply`. Please be sure to upgrade
all code to work with `apply`.
(apply function-call args kwargs) ; is the signature
[Thanks]
Major shoutout to Clinton Dreisbach for implementing loop/recur.
As always, massive hugs to olasd for the constant reviews and for
implementing HyCons cells. Thanks to @kenanb for redesigning the
new Hy logo.
Many thanks to algernon for working on adderall, which helped
push Hy further this cycle. Adderall is an implementation of miniKanren
in Hy. If you're interested in using Adderall, check out hydiomatic,
which prettfies Hy source using Adderall rules.
This release saw an increase of about 11 contributors for a point
release, you guys rock!
-Hy Society
[ Language Changes ]
* `for' revamped again (Last time, we hope!), this time using a saner
itertools.product when nesting
* `lisp-if'/`lif' added for the lisp-like everything is true if, giving
seasoned lispers a better if check (0 is a value, etc)
* Reader Macros are macros now!
* yield-from is now a proper yield from on Python 3. It also now breaks on
Python 2.x.
* Added if-not macro
* We finally have a lisp like cons cells
* Generator expressions, set & dict comprehensions are now supported
* (.) is a mini DSL for attribute access
* `macroexpand' & `macroexpand-1' added to core
* `disassemble' added to core, which dumps the AST or equivalent python code
* `coll?' added to core to check for a collection
* `identity' function added to core
[ Misc. Fixes ]
* Lots of doc fixes. Reorganization as well as better docs on Hy internals
* Universal Wheel Support
* Pygments > 1.6 supports Hy now. All codeblocks in docs have been changed
from clojure to hy
* Hy REPL supports invoking with --spy & -i options [reword]
* `first' and `rest' are functions and not macros anymore
* "clean" target added to Makefile
* hy2py supports a bunch of commandline options to show AST, source etc.
* Sub-object mangling: every identifier is split along the dots & mangled
seperately
[ Bug Fixes ]
* Empty MacroExpansions work as expected
* Python 3.4 port. Sorry this wasn't in a 3.4 release time, we forgot to do
a release. Whoops.
* eg/lxml/parse-tumblr.hy works with Python 3
* hy2py works on Windows
* Fixed unicode encoding issue in REPL during unicode exceptions
* Fixed handling of comments at end of input (#382)
[ Contrib changes ]
* Curry module added to contrib
* Loop/recur module added which provides TCO at tail position
* defmulti has been added - check out more in the docs -- thanks to Foxboron for this one!
* Walk module for walking the Hy AST, features a `macroexpand-all` as well
Changes from Hy 0.9.11 Changes from Hy 0.9.11
tl;dr: tl;dr:

View File

@ -1,40 +0,0 @@
#!/usr/bin/env python
from __future__ import print_function
from hy.importer import import_file_to_ast, import_file_to_hst
import argparse
import sys
import astor.codegen
module_name = "<STDIN>"
parser = argparse.ArgumentParser(
prog="hy2py",
usage="%(prog)s [options] FILE",
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument("--with-source", "-s", action="store_true",
help="Show the parsed source structure")
parser.add_argument("--with-ast", "-a", action="store_true",
help="Show the generated AST")
parser.add_argument("--without-python", "-np", action="store_true",
help="Do not show the python code generated from the AST")
parser.add_argument('args', nargs=argparse.REMAINDER, help=argparse.SUPPRESS)
options = parser.parse_args(sys.argv[1:])
if options.with_source:
hst = import_file_to_hst(options.args[0])
print(hst)
print()
print()
_ast = import_file_to_ast(options.args[0], module_name)
if options.with_ast:
print(astor.dump(_ast))
print()
print()
if not options.without_python:
print(astor.codegen.to_source(_ast))

View File

@ -96,7 +96,13 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. See the documentation for # The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes. # a list of builtin themes.
html_theme = 'default' try:
import sphinx_rtd_theme
except ImportError:
html_theme = 'default'
else:
html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
# Theme options are theme-specific and customize the look and feel of a theme # Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the # further. For a list of options available for each theme, see the

View File

@ -92,6 +92,12 @@ Write docs---docs are good! Even this doc!
Core Development Rules Core Development Rules
====================== ======================
Pull requests are good!
Before you submit a PR, please run the tests and check your code against the style guide. You can do both these things at once::
$ make d
All incoming changes need to be acked by 2 different members of Hylang's All incoming changes need to be acked by 2 different members of Hylang's
core team. Additional review is clearly welcome, but we need a minimum of core team. Additional review is clearly welcome, but we need a minimum of
2 signoffs for any change. 2 signoffs for any change.

View File

@ -764,6 +764,36 @@ any numeric type, empty sequence and empty dictionary are considered `False`.
Everything else is considered `True`. Everything else is considered `True`.
lisp-if / lif
-------------
.. versionadded:: 0.10.0
For those that prefer a more lisp-y if clause, we have lisp-if, or lif. This
*only* considers None/nil as false! All other values of python
"falseiness" are considered true.
.. code-block:: clj
=> (lisp-if True "true" "false")
"true"
=> (lisp-if False "true" "false")
"true"
=> (lisp-if 0 "true" "false")
"true"
=> (lisp-if nil "true" "false")
"false"
=> (lisp-if None "true" "false")
"false"
; And, same thing
=> (lif True "true" "false")
"true"
=> (lif nil "true" "false")
"false"
import import
------ ------
@ -824,11 +854,15 @@ Just as in normal function definitions, if the first element of the
body is a string, it serves as docstring. This is useful for giving body is a string, it serves as docstring. This is useful for giving
class methods docstrings. class methods docstrings.
.. code-block:: clj
=> (setv times-three => (setv times-three
... (fn [x] ... (fn [x]
... "Multiplies input by three and returns the result." ... "Multiplies input by three and returns the result."
... (* x 3))) ... (* x 3)))
Then test it via the Python built-in ``help`` function::
=> (help times-three) => (help times-three)
Help on function times_three: Help on function times_three:
@ -1265,23 +1299,15 @@ infinite series without consuming infinite amount of memory.
=> (list-comp x [x (take 15 (random-numbers 1 50))])]) => (list-comp x [x (take 15 (random-numbers 1 50))])])
[7, 41, 6, 22, 32, 17, 5, 38, 18, 38, 17, 14, 23, 23, 19] [7, 41, 6, 22, 32, 17, 5, 38, 18, 38, 17, 14, 23, 23, 19]
.. _zipwith:
zipwith yield-from
------- ----------
.. versionadded:: 0.10.0 .. versionadded:: 0.9.13
`zipwith` zips multiple lists and maps the given function over the result. It is **PYTHON 3.3 AND UP ONLY!**
equilavent to calling ``zip``, followed by calling ``map`` on the result.
In the following example, `zipwith` is used to add the contents of two lists `yield-from` is used to call a subgenerator. This is useful if you
together. The equilavent ``map`` and ``zip`` calls follow. want your coroutine to be able to delegate its processes to another
coroutine, say if using something fancy like
.. code-block:: clj `asyncio <http://docs.python.org/3.4/library/asyncio.html>`_.
=> (import operator.add)
=> (zipwith operator.add [1 2 3] [4 5 6]) ; using zipwith
[5, 7, 9]
=> (map operator.add (zip [1 2 3] [4 5 6])) ; using map+zip
[5, 7, 9]

View File

@ -2,6 +2,8 @@
Command Line Interface Command Line Interface
====================== ======================
.. _hy:
hy hy
-- --
@ -46,6 +48,8 @@ Command line options
Print the Hy version number and exit. Print the Hy version number and exit.
.. _hyc:
hyc hyc
--- ---
@ -71,3 +75,29 @@ Command line options
$ hyc hyname.hy $ hyc hyname.hy
$ python hyname.pyc $ python hyname.pyc
Hy Afroman! Hy Afroman!
.. _hy2py:
hy2py
-----
.. versionadded:: 0.10.1
Command line options
^^^^^^^^^^^^^^^^^^^^
.. cmdoption:: -s
--with-source
Show the parsed source structure.
.. cmdoption:: -a
--with-ast
Show the generated AST.
.. cmdoption:: -np
--without-python
Do not show the Python code generated from the AST.

View File

@ -495,8 +495,8 @@ nth
Usage: ``(nth coll n)`` Usage: ``(nth coll n)``
Return the `nth` item in a collection, counting from 0. Unlike Return the `nth` item in a collection, counting from 0. Unlike
``get``, ``nth`` works on both iterators and iterables. Returns ``None`` ``get``, ``nth`` works on both iterators and iterables. Raises ``IndexError``
if the `n` is outside the range of `coll`. if the `n` is outside the range of ``coll`` or ``ValueError`` if it's negative.
.. code-block:: hy .. code-block:: hy
@ -506,8 +506,10 @@ if the `n` is outside the range of `coll`.
=> (nth [1 2 4 7] 3) => (nth [1 2 4 7] 3)
7 7
=> (none? (nth [1 2 4 7] 5)) => (nth [1 2 4 7] 5)
True Traceback (most recent call last):
...
IndexError: 5
=> (nth (take 3 (drop 2 [1 2 3 4 5 6])) 2)) => (nth (take 3 (drop 2 [1 2 3 4 5 6])) 2))
5 5
@ -764,6 +766,7 @@ drop
Usage: ``(drop n coll)`` Usage: ``(drop n coll)``
Return an iterator, skipping the first ``n`` members of ``coll`` Return an iterator, skipping the first ``n`` members of ``coll``
Raises ``ValueError`` if ``n`` is negative.
.. code-block:: hy .. code-block:: hy
@ -924,6 +927,7 @@ take
Usage: ``(take n coll)`` Usage: ``(take n coll)``
Return an iterator containing the first ``n`` members of ``coll``. Return an iterator containing the first ``n`` members of ``coll``.
Raises ``ValueError`` if ``n`` is negative.
.. code-block:: hy .. code-block:: hy
@ -979,3 +983,21 @@ Return an iterator from ``coll`` as long as predicate, ``pred`` returns True.
=> (list (take-while neg? [ 1 2 3 -4 5])) => (list (take-while neg? [ 1 2 3 -4 5]))
[] []
.. _zipwith:
zipwith
-------
.. versionadded:: 0.9.13
Usage: ``(zipwith fn coll ...)``
Equivalent to ``zip``, but uses a multi-argument function instead of creating a tuple.
If ``zipwith`` is called with N collections, then ``fn`` must accept N arguments.
.. code-block:: clojure
=> (import operator)
=> (list (zipwith operator.add [1 2 3] [4 5 6]))
[5, 7, 9]

View File

@ -25,16 +25,21 @@
# 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 __future__ import print_function
import argparse import argparse
import code import code
import ast import ast
import sys import sys
import astor.codegen
import hy import hy
from hy.lex import LexException, PrematureEndOfInput, tokenize from hy.lex import LexException, PrematureEndOfInput, tokenize
from hy.compiler import hy_compile, HyTypeError from hy.compiler import hy_compile, HyTypeError
from hy.importer import ast_compile, import_buffer_to_module from hy.importer import (ast_compile, import_buffer_to_module,
import_file_to_ast, import_file_to_hst)
from hy.completer import completion from hy.completer import completion
from hy.macros import macro, require from hy.macros import macro, require
@ -66,7 +71,6 @@ builtins.exit = HyQuitter('exit')
def print_python_code(_ast): def print_python_code(_ast):
import astor.codegen
# astor cannot handle ast.Interactive, so disguise it as a module # astor cannot handle ast.Interactive, so disguise it as a module
_ast_for_print = ast.Module() _ast_for_print = ast.Module()
_ast_for_print.body = _ast.body _ast_for_print.body = _ast.body
@ -313,3 +317,43 @@ def hyc_main():
sys.stderr.write("hyc: Can't open file '%s': [Errno %d] %s\n" % sys.stderr.write("hyc: Can't open file '%s': [Errno %d] %s\n" %
(x.filename, x.errno, x.strerror)) (x.filename, x.errno, x.strerror))
sys.exit(x.errno) sys.exit(x.errno)
# entry point for cmd line script "hy2py"
def hy2py_main():
module_name = "<STDIN>"
options = dict(prog="hy2py", usage="%(prog)s [options] FILE",
formatter_class=argparse.RawDescriptionHelpFormatter)
parser = argparse.ArgumentParser(**options)
parser.add_argument("--with-source", "-s", action="store_true",
help="Show the parsed source structure")
parser.add_argument("--with-ast", "-a", action="store_true",
help="Show the generated AST")
parser.add_argument("--without-python", "-np", action="store_true",
help=("Do not show the Python code generated "
"from the AST"))
parser.add_argument('args', nargs=argparse.REMAINDER,
help=argparse.SUPPRESS)
options = parser.parse_args(sys.argv[1:])
if not options.args:
parser.exit(1, parser.format_help())
if options.with_source:
hst = import_file_to_hst(options.args[0])
print(hst)
print()
print()
_ast = import_file_to_ast(options.args[0], module_name)
if options.with_ast:
print(astor.dump(_ast))
print()
print()
if not options.without_python:
print(astor.codegen.to_source(_ast))
parser.exit(0)

View File

@ -732,25 +732,10 @@ class HyASTCompiler(object):
handler_results += self._compile_catch_expression(e, name) handler_results += self._compile_catch_expression(e, name)
handlers.append(handler_results.stmts.pop()) handlers.append(handler_results.stmts.pop())
elif e[0] == HySymbol("else"): elif e[0] == HySymbol("else"):
if orelse: orelse = self.try_except_helper(e, HySymbol("else"), orelse)
raise HyTypeError(
e,
"`try' cannot have more than one `else'")
else:
orelse = self._compile_branch(e[1:])
# XXX tempvar magic
orelse += orelse.expr_as_stmt()
orelse = orelse.stmts
elif e[0] == HySymbol("finally"): elif e[0] == HySymbol("finally"):
if finalbody: finalbody = self.try_except_helper(e, HySymbol("finally"),
raise HyTypeError( finalbody)
e,
"`try' cannot have more than one `finally'")
else:
finalbody = self._compile_branch(e[1:])
# XXX tempvar magic
finalbody += finalbody.expr_as_stmt()
finalbody = finalbody.stmts
else: else:
raise HyTypeError(e, "Unknown expression in `try'") raise HyTypeError(e, "Unknown expression in `try'")
@ -768,8 +753,8 @@ class HyASTCompiler(object):
col_offset=expr.start_column, col_offset=expr.start_column,
type=None, type=None,
name=None, name=None,
body=[ast.Pass(lineno=expr.start_line, body=[ast.Raise(lineno=expr.start_line,
col_offset=expr.start_column)])] col_offset=expr.start_column)])]
ret = handler_results ret = handler_results
@ -809,6 +794,17 @@ class HyASTCompiler(object):
body=body, body=body,
orelse=orelse) + returnable orelse=orelse) + returnable
def try_except_helper(self, hy_obj, symbol, accumulated):
if accumulated:
raise HyTypeError(
hy_obj,
"`try' cannot have more than one `%s'" % symbol)
else:
accumulated = self._compile_branch(hy_obj[1:])
accumulated += accumulated.expr_as_stmt()
accumulated = accumulated.stmts
return accumulated
@builds("except") @builds("except")
@builds("catch") @builds("catch")
def magic_internal_form(self, expr): def magic_internal_form(self, expr):
@ -1017,6 +1013,28 @@ class HyASTCompiler(object):
return ret return ret
@builds("yield_from")
@checkargs(max=1)
def compile_yield_from_expression(self, expr):
if not PY33:
raise HyCompileError(
"yield-from only supported in python 3.3+!")
expr.pop(0)
ret = Result(contains_yield=True)
value = None
if expr != []:
ret += self.compile(expr.pop(0))
value = ret.force_expr
ret += ast.YieldFrom(
value=value,
lineno=expr.start_line,
col_offset=expr.start_column)
return ret
@builds("import") @builds("import")
def compile_import_expression(self, expr): def compile_import_expression(self, expr):
def _compile_import(expr, module, names=None, importer=ast.Import): def _compile_import(expr, module, names=None, importer=ast.Import):

View File

@ -23,7 +23,9 @@
;;;; to make functional programming slightly easier. ;;;; to make functional programming slightly easier.
;;;; ;;;;
(import itertools)
(import functools)
(import collections)
(import [hy._compat [long-type]]) ; long for python2, int for python3 (import [hy._compat [long-type]]) ; long for python2, int for python3
(import [hy.models.cons [HyCons]]) (import [hy.models.cons [HyCons]])
@ -44,15 +46,11 @@
"Check whether c can be used as a cons object" "Check whether c can be used as a cons object"
(instance? HyCons c)) (instance? HyCons c))
(defn cycle [coll] (defn keyword? [k]
"Yield an infinite repetition of the items in coll" "Check whether k is a keyword"
(setv seen []) (and (instance? (type :foo) k)
(for* [x coll] (.startswith k (get :foo 0))))
(yield x)
(.append seen x))
(while seen
(for* [x seen]
(yield x))))
(defn dec [n] (defn dec [n]
"Decrement n by 1" "Decrement n by 1"
@ -82,22 +80,36 @@
(yield val) (yield val)
(.add seen val)))))) (.add seen val))))))
(if-python2
(do
(setv filterfalse itertools.ifilterfalse)
(setv zip_longest itertools.izip_longest)
(setv filter itertools.ifilter)
(setv map itertools.imap)
(setv zip itertools.izip)
(setv range xrange)
(setv input raw_input))
(do
(setv reduce functools.reduce)
(setv filterfalse itertools.filterfalse)
(setv zip_longest itertools.zip_longest)
; Someone can import these directly from `hy.core.language`;
; we'll make some duplicates.
(setv filter filter)
(setv map map)
(setv zip zip)
(setv range range)
(setv input input)))
(setv cycle itertools.cycle)
(setv repeat itertools.repeat)
(setv drop-while itertools.dropwhile)
(setv take-while itertools.takewhile)
(setv zipwith map)
(defn drop [count coll] (defn drop [count coll]
"Drop `count` elements from `coll` and yield back the rest" "Drop `count` elements from `coll` and yield back the rest"
(let [[citer (iter coll)]] (itertools.islice coll count nil))
(try (for* [i (range count)]
(next citer))
(catch [StopIteration]))
citer))
(defn drop-while [pred coll]
"Drop all elements of `coll` until `pred` is False"
(let [[citer (iter coll)]]
(for* [val citer]
(if (not (pred val))
(do (yield val) (break))))
(for* [val citer]
(yield val))))
(defn empty? [coll] (defn empty? [coll]
"Return True if `coll` is empty" "Return True if `coll` is empty"
@ -121,13 +133,6 @@
(if (not (hasattr tree attr)) (if (not (hasattr tree attr))
(setattr tree attr 1)))) (setattr tree attr 1))))
(defn filter [pred coll]
"Return all elements from `coll` that pass `pred`"
(let [[citer (iter coll)]]
(for* [val citer]
(if (pred val)
(yield val)))))
(defn flatten [coll] (defn flatten [coll]
"Return a single flat list expanding all members of coll" "Return a single flat list expanding all members of coll"
(if (coll? coll) (if (coll? coll)
@ -169,7 +174,7 @@
(defn first [coll] (defn first [coll]
"Return first item from `coll`" "Return first item from `coll`"
(get coll 0)) (nth coll 0))
(defn identity [x] (defn identity [x]
"Returns the argument unchanged" "Returns the argument unchanged"
@ -194,14 +199,13 @@
(defn integer-char? [x] (defn integer-char? [x]
"Return True if char `x` parses as an integer" "Return True if char `x` parses as an integer"
(try (try
(integer? (int x)) (integer? (int x))
(catch [e ValueError] False) (catch [e ValueError] False)
(catch [e TypeError] False))) (catch [e TypeError] False)))
(defn iterable? [x] (defn iterable? [x]
"Return true if x is iterable" "Return true if x is iterable"
(try (do (iter x) true) (isinstance x collections.Iterable))
(catch [Exception] false)))
(defn iterate [f x] (defn iterate [f x]
(setv val x) (setv val x)
@ -211,8 +215,7 @@
(defn iterator? [x] (defn iterator? [x]
"Return true if x is an iterator" "Return true if x is an iterator"
(try (= x (iter x)) (isinstance x collections.Iterator))
(catch [TypeError] false)))
(defn list* [hd &rest tl] (defn list* [hd &rest tl]
"Return a dotted list construed from the elements of the argument" "Return a dotted list construed from the elements of the argument"
@ -253,13 +256,9 @@
(defn nth [coll index] (defn nth [coll index]
"Return nth item in collection or sequence, counting from 0" "Return nth item in collection or sequence, counting from 0"
(if (not (neg? index)) (try
(if (iterable? coll) (next (drop index coll))
(try (get (list (take 1 (drop index coll))) 0) (catch [e StopIteration] (raise (IndexError index)))))
(catch [IndexError] None))
(try (get coll index)
(catch [IndexError] None)))
None))
(defn odd? [n] (defn odd? [n]
"Return true if n is an odd number" "Return true if n is an odd number"
@ -280,14 +279,7 @@
(defn rest [coll] (defn rest [coll]
"Get all the elements of a coll, except the first." "Get all the elements of a coll, except the first."
(slice coll 1)) (drop 1 coll))
(defn repeat [x &optional n]
"Yield x forever or optionally n times"
(if (none? n)
(setv dispatch (fn [] (while true (yield x))))
(setv dispatch (fn [] (for* [_ (range n)] (yield x)))))
(dispatch))
(defn repeatedly [func] (defn repeatedly [func]
"Yield result of running func repeatedly" "Yield result of running func repeatedly"
@ -296,7 +288,7 @@
(defn second [coll] (defn second [coll]
"Return second item from `coll`" "Return second item from `coll`"
(get coll 1)) (nth coll 1))
(defn some [pred coll] (defn some [pred coll]
"Return true if (pred x) is logical true for any x in coll, else false" "Return true if (pred x) is logical true for any x in coll, else false"
@ -317,9 +309,7 @@
(defn take [count coll] (defn take [count coll]
"Take `count` elements from `coll`, or the whole set if the total "Take `count` elements from `coll`, or the whole set if the total
number of entries in `coll` is less than `count`." number of entries in `coll` is less than `count`."
(let [[citer (iter coll)]] (itertools.islice coll nil count))
(for* [_ (range count)]
(yield (next citer)))))
(defn take-nth [n coll] (defn take-nth [n coll]
"Return every nth member of coll "Return every nth member of coll
@ -332,29 +322,15 @@
(next citer)))) (next citer))))
(raise (ValueError "n must be positive")))) (raise (ValueError "n must be positive"))))
(defn take-while [pred coll]
"Take all elements while `pred` is true"
(let [[citer (iter coll)]]
(for* [val citer]
(if (pred val)
(yield val)
(break)))))
(defn zero? [n] (defn zero? [n]
"Return true if n is 0" "Return true if n is 0"
(_numeric_check n) (_numeric_check n)
(= n 0)) (= n 0))
(defn zipwith [func &rest lists]
"Zip the contents of several lists and map a function to the result"
(do
(import functools)
(map (functools.partial (fn [f args] (apply f args)) func) (apply zip lists))))
(def *exports* '[calling-module-name coll? cons cons? cycle dec distinct (def *exports* '[calling-module-name coll? cons cons? cycle dec distinct
disassemble drop drop-while empty? even? every? first filter disassemble drop drop-while empty? even? every? first filter
flatten float? gensym identity inc instance? integer flatten float? gensym identity inc instance? integer
integer? integer-char? iterable? iterate iterator? integer? integer-char? iterable? iterate iterator? keyword?
list* macroexpand macroexpand-1 neg? nil? none? nth list* macroexpand macroexpand-1 map neg? nil? none? nth
numeric? odd? pos? remove repeat repeatedly rest second numeric? odd? pos? range remove repeat repeatedly rest second
some string string? take take-nth take-while zero? zipwith]) some string string? take take-nth take-while zero? zip zipwith])

View File

@ -27,8 +27,8 @@
(import [hy.models.list [HyList]] (import [hy.models.list [HyList]]
[hy.models.symbol [HySymbol]]) [hy.models.symbol [HySymbol]]
[hy._compat [PY33 PY34]])
(defmacro for [args &rest body] (defmacro for [args &rest body]
@ -145,6 +145,11 @@
`(if (not ~test) ~not-branch ~yes-branch))) `(if (not ~test) ~not-branch ~yes-branch)))
(defmacro-alias [lisp-if lif] [test &rest branches]
"Like `if`, but anything that is not None/nil is considered true."
`(if (is-not ~test nil) ~@branches))
(defmacro when [test &rest body] (defmacro when [test &rest body]
"Execute `body` when `test` is true" "Execute `body` when `test` is true"
`(if ~test (do ~@body))) `(if ~test (do ~@body)))
@ -155,12 +160,6 @@
`(if-not ~test (do ~@body))) `(if-not ~test (do ~@body)))
(defmacro yield-from [iterable]
"Yield all the items from iterable"
(let [[x (gensym)]]
`(for* [~x ~iterable]
(yield ~x))))
(defmacro with-gensyms [args &rest body] (defmacro with-gensyms [args &rest body]
`(let ~(HyList (map (fn [x] `[~x (gensym '~x)]) args)) `(let ~(HyList (map (fn [x] `[~x (gensym '~x)]) args))
~@body)) ~@body))

View File

@ -20,4 +20,4 @@
__appname__ = "hy" __appname__ = "hy"
__version__ = "0.9.12" __version__ = "0.10.0"

View File

@ -1,6 +1,12 @@
-r requirements.txt # test tools
tox
nose nose
tox
# code quality
flake8
coverage
# documentation
Pygments>=1.6 Pygments>=1.6
Sphinx Sphinx
coverage sphinx_rtd_theme

View File

@ -1,3 +0,0 @@
# Check site / dev for more deps!
flake8
rply>=0.7.0

View File

@ -59,7 +59,8 @@ setup(
entry_points={ entry_points={
'console_scripts': [ 'console_scripts': [
'hy = hy.cmdline:hy_main', 'hy = hy.cmdline:hy_main',
'hyc = hy.cmdline:hyc_main' 'hyc = hy.cmdline:hyc_main',
'hy2py = hy.cmdline:hy2py_main',
] ]
}, },
packages=find_packages(exclude=['tests*']), packages=find_packages(exclude=['tests*']),

View File

@ -82,8 +82,8 @@
(assert-equal res [None 4 5]) (assert-equal res [None 4 5])
(setv res (list (drop 0 [1 2 3 4 5]))) (setv res (list (drop 0 [1 2 3 4 5])))
(assert-equal res [1 2 3 4 5]) (assert-equal res [1 2 3 4 5])
(setv res (list (drop -1 [1 2 3 4 5]))) (try (do (list (drop -1 [1 2 3 4 5])) (assert False))
(assert-equal res [1 2 3 4 5]) (catch [e [ValueError]] nil))
(setv res (list (drop 6 (iter [1 2 3 4 5])))) (setv res (list (drop 6 (iter [1 2 3 4 5]))))
(assert-equal res []) (assert-equal res [])
(setv res (list (take 5 (drop 2 (iterate inc 0))))) (setv res (list (take 5 (drop 2 (iterate inc 0)))))
@ -335,12 +335,15 @@
"NATIVE: testing the nth function" "NATIVE: testing the nth function"
(assert-equal 2 (nth [1 2 4 7] 1)) (assert-equal 2 (nth [1 2 4 7] 1))
(assert-equal 7 (nth [1 2 4 7] 3)) (assert-equal 7 (nth [1 2 4 7] 3))
(assert-true (none? (nth [1 2 4 7] 5))) (try (do (nth [1 2 4 7] 5) (assert False))
(assert-true (none? (nth [1 2 4 7] -1))) (catch [e [IndexError]] nil))
(try (do (nth [1 2 4 7] -1) (assert False))
(catch [e [ValueError]] nil))
;; now for iterators ;; now for iterators
(assert-equal 2 (nth (iter [1 2 4 7]) 1)) (assert-equal 2 (nth (iter [1 2 4 7]) 1))
(assert-equal 7 (nth (iter [1 2 4 7]) 3)) (assert-equal 7 (nth (iter [1 2 4 7]) 3))
(assert-true (none? (nth (iter [1 2 4 7]) -1))) (try (do (nth (iter [1 2 4 7]) -1) (assert False))
(catch [e [ValueError]] nil))
(assert-equal 5 (nth (take 3 (drop 2 [1 2 3 4 5 6])) 2))) (assert-equal 5 (nth (take 3 (drop 2 [1 2 3 4 5 6])) 2)))
(defn test-numeric? [] (defn test-numeric? []
@ -429,8 +432,8 @@
(assert-equal res ["s" "s" "s" "s"]) (assert-equal res ["s" "s" "s" "s"])
(setv res (list (take 0 (repeat "s")))) (setv res (list (take 0 (repeat "s"))))
(assert-equal res []) (assert-equal res [])
(setv res (list (take -1 (repeat "s")))) (try (do (list (take -1 (repeat "s"))) (assert False))
(assert-equal res []) (catch [e [ValueError]] nil))
(setv res (list (take 6 [1 2 None 4]))) (setv res (list (take 6 [1 2 None 4])))
(assert-equal res [1 2 None 4])) (assert-equal res [1 2 None 4]))
@ -478,3 +481,14 @@
(assert-equal (list res) [4 4 4]) (assert-equal (list res) [4 4 4])
(setv res (zipwith operator.sub [3 7 9] [1 2 4])) (setv res (zipwith operator.sub [3 7 9] [1 2 4]))
(assert-equal (list res) [2 5 5])) (assert-equal (list res) [2 5 5]))
(defn test-is-keyword []
"NATIVE: testing the keyword? function"
(assert (keyword? ':bar))
(assert (keyword? ':baz))
(assert (keyword? :bar))
(assert (keyword? :baz))
(assert (not (keyword? "foo")))
(assert (not (keyword? ":foo")))
(assert (not (keyword? 1)))
(assert (not (keyword? nil))))

View File

@ -236,6 +236,11 @@
"NATIVE: test do" "NATIVE: test do"
(do)) (do))
(defn test-bare-try [] (try
(try (raise ValueError))
(except [ValueError])
(else (assert false))))
(defn test-exceptions [] (defn test-exceptions []
"NATIVE: test Exceptions" "NATIVE: test Exceptions"
@ -469,7 +474,7 @@
(defn test-rest [] (defn test-rest []
"NATIVE: test rest" "NATIVE: test rest"
(assert (= (rest [1 2 3 4 5]) [2 3 4 5]))) (assert (= (list (rest [1 2 3 4 5])) [2 3 4 5])))
(defn test-importas [] (defn test-importas []

View File

@ -1,3 +1,6 @@
(import [hy._compat [PY33]])
(import [hy.errors [HyCompileError]])
(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))))))
@ -99,11 +102,18 @@
(defn test-yield-from [] (defn test-yield-from []
"NATIVE: testing yield from" "NATIVE: testing yield from"
(defn yield-from-test []
(for* [i (range 3)] (try
(yield i)) (eval
(yield-from [1 2 3])) '(do (defn yield-from-test []
(assert (= (list (yield-from-test)) [0 1 2 1 2 3]))) (for* [i (range 3)]
(yield i))
(yield-from [1 2 3]))
(assert (= (list (yield-from-test)) [0 1 2 1 2 3]))))
(catch [e HyCompileError]
;; Yup, this should happen on non-Python3.3 thingies
(assert (not PY33)))
(else (assert PY33))))
(defn test-if-python2 [] (defn test-if-python2 []
(import sys) (import sys)
@ -191,6 +201,25 @@
:yes))) :yes)))
(defn test-lisp-if []
"test that lisp-if works as expected"
; nil is false
(assert (= (lisp-if None "true" "false") "false"))
(assert (= (lisp-if nil "true" "false") "false"))
; But everything else is True! Even falsey things.
(assert (= (lisp-if True "true" "false") "true"))
(assert (= (lisp-if False "true" "false") "true"))
(assert (= (lisp-if 0 "true" "false") "true"))
(assert (= (lisp-if "some-string" "true" "false") "true"))
(assert (= (lisp-if "" "true" "false") "true"))
(assert (= (lisp-if (+ 1 2 3) "true" "false") "true"))
; Just to be sure, test the alias lif
(assert (= (lif nil "true" "false") "false"))
(assert (= (lif 0 "true" "false") "true")))
(defn test-defn-alias [] (defn test-defn-alias []
(defn-alias [tda-main tda-a1 tda-a2] [] :bazinga) (defn-alias [tda-main tda-a1 tda-a2] [] :bazinga)
(defun-alias [tda-main tda-a1 tda-a2] [] :bazinga) (defun-alias [tda-main tda-a1 tda-a2] [] :bazinga)

View File

@ -129,7 +129,7 @@ def test_hy2py():
for f in filenames: for f in filenames:
if f.endswith(".hy"): if f.endswith(".hy"):
i += 1 i += 1
ret = run_cmd("python bin/hy2py -s -a " ret = run_cmd("hy2py -s -a "
+ os.path.join(dirpath, f)) + os.path.join(dirpath, f))
assert ret[0] == 0, f assert ret[0] == 0, f
assert len(ret[1]) > 1, f assert len(ret[1]) > 1, f

37
tox.ini
View File

@ -1,39 +1,18 @@
[tox] [tox]
envlist = py27,pypy,py32,py33,py26,flake8 envlist = py26,py27,pypy,py32,py33,flake8
skipsdist = True
[testenv] [testenv]
commands = nosetests commands =
pip install --allow-all-external -e .
nosetests
deps = deps =
nose -rrequirements-dev.txt
setuptools
rply
[testenv:pypy]
commands = nosetests
deps =
astor
nose
setuptools
rply
[testenv:py27]
commands = nosetests
deps =
astor
nose
setuptools
rply
[testenv:py26] [testenv:py26]
deps = deps =
astor {[testenv]deps}
nose
setuptools
unittest2 unittest2
importlib
rply
[testenv:flake8] [testenv:flake8]
deps =
flake8
rply
commands = flake8 hy bin tests commands = flake8 hy bin tests