Merge pull request #1 from hylang/master

update from upstream
This commit is contained in:
Matthew Egan Odendahl 2016-04-15 20:32:19 -06:00
commit d91dbd19f8
27 changed files with 338 additions and 155 deletions

View File

@ -1,13 +1,21 @@
sudo: false
language: python
python:
- "pypy"
- "2.7"
- "3.3"
- "3.4"
cache: pip
# command to run tests
script: make travis
matrix:
include:
- python: 3.5
env: TOXENV=py35
env:
- TOXENV=py27
- TOXENV=py33
- TOXENV=py34
- TOXENV=pypy
- TOXENV=flake8
install: pip install tox
script: tox
cache:
directories:
- .tox
- $HOME/.cache/pip
after_success: make coveralls
notifications:
email:

View File

@ -65,3 +65,7 @@
* Antony Woods <antony@teamwoods.org>
* Matthew Egan Odendahl <github.gilch@xoxy.net>
* Tim Martin <tim@asymptotic.co.uk>
* Johnathon Mlady <john@digitalvectorz.com>
* Andrew Savchyn <dev@scorpil.com>
* Lev Kravinsky <kravinskylev@gmail.com>
* Luna Lunapiena <lunacodes@gmail.com>

View File

@ -6,11 +6,20 @@ 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
- `Fork the repo`_ and create a topic branch for a feature/fix. Avoid
making changes directly on the master branch.
(If you're new to Git: `Start Here`_)
- All incoming features should be accompanied with tests.
- If you are contributing a major change to the Hy language (e.g. changing
the behavior of or removing functions or macros), or you're unsure of
the proposed change, please open an issue in the `issue tracker`_ before
submitting the PR. This will allow others to give feedback on your idea,
and it will avoid constant changes or wasted work. For other PRs (such as
documentation fixes or code cleanup), you can directly open the PR without
first opening a corresponding issue.
- Before you submit a PR, please run the tests and check your code
against the style guide. You can do both of these things at once::
@ -82,3 +91,6 @@ version 1.1.0, available at
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/)

View File

@ -67,14 +67,6 @@ endif
$(pip) install coveralls
$(pip) install --allow-all-external -e .
travis: python
$(nose) -s --with-coverage --cover-package hy
ifeq (PyPy,$(findstring PyPy,$(shell python -V 2>&1 | tail -1)))
@echo "skipping flake8 on pypy"
else
flake8 hy bin tests
endif
coveralls:
$(coveralls)

4
NEWS
View File

@ -60,7 +60,7 @@ Changes from 0.10.0
* nth returns default value when out of bounds
* merge-with added
* doto macro added
* keyword? to findout keywords
* keyword? to find out keywords
* setv no longer allows "." in names
[Internals ]
@ -107,7 +107,7 @@ Changes from 0.9.12
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.
which prettifies Hy source using Adderall rules.
This release saw an increase of about 11 contributors for a point
release, you guys rock!

View File

@ -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)
[![XKCD #224](https://raw.github.com/hylang/shyte/18f6925e08684b0e1f52b2cc2c803989cd62cd91/imgs/xkcd.png)](https://xkcd.com/224/)
Lisp and Python should love each other. Let's make it happen. [Try it](http://try-hy.appspot.com/).
@ -32,7 +32,7 @@ It's really awesome.
Oh, and lisps are neat.
![Cuddles the Hacker](http://i.imgur.com/QbPMXTN.png)
![Cuddles the Hacker](https://i.imgur.com/QbPMXTN.png)
(fan art from the one and only [doctormo](http://doctormo.deviantart.com/art/Cuddles-the-Hacker-372184766))
@ -46,3 +46,4 @@ Project
* Bug reports: We have no bugs! Your bugs are your own! (https://github.com/hylang/hy/issues)
* License: MIT (Expat)
* [Contributor Guidelines & Code of Conduct](https://github.com/hylang/hy/blob/master/CONTRIBUTING.rst)
* IRC: Join #hy on [freenode](https://webchat.freenode.net/)

View File

@ -13,6 +13,9 @@ concise and easy to read.
-- Wikipedia (http://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)``
.. _ap-if:
@ -233,7 +236,7 @@ xi
Usage ``(xi body ...)``
Returns a function with parameters implicitly determined by the presence in the body of xi parameters. An xi symbol designates the ith parameter (1-based, e.g. x1, x2, x3, etc.), or all remaining parameters for xi itself. This is not a replacement for lambda. The xi forms cannot be nested.
Returns a function with parameters implicitly determined by the presence in the body of xi parameters. An xi symbol designates the ith parameter (1-based, e.g. x1, x2, x3, etc.), or all remaining parameters for xi itself. This is not a replacement for lambda. The xi forms cannot be nested.
This is similar to Clojure's anonymous function literals (``#()``).
@ -244,5 +247,3 @@ This is similar to Clojure's anonymous function literals (``#()``).
=> (def add-10 (xi + 10 x1))
=> (add-10 6)
16

View File

@ -400,7 +400,7 @@ below:
.. _defn:
defn
------------
----
``defn`` macro is used to define functions. It takes three
parameters: the *name* of the function to define, a vector of *parameters*,
@ -430,7 +430,26 @@ Parameters may have the following keywords in front of them:
101.0
&key
Parameter is a dict of keyword arguments. The keys of the dict
specify the parameter names and the values give the default values
of the parameters.
.. code-block:: clj
=> (defn key-parameters [&key {"a" 1 "b" 2}]
... (print "a is" a "and b is" b))
=> (key-parameters :a 1 :b 2)
a is 1 and b is 2
=> (key-parameters :b 1 :a 2)
a is 2 and b is 1
The following declarations are equivalent:
.. code-block:: clj
(defn key-parameters [&key {"a" 1 "b" 2}])
(defn key-parameters [&optional [a 1] [b 2]])
&kwargs
Parameter will contain 0 or more keyword arguments.
@ -1489,6 +1508,27 @@ expands to:
Section :ref:`using-gensym`
xor
---
.. versionadded:: 0.12.0
``xor`` is used in logical expressions to perform exclusive or. It takes two
parameters. It returns ``True`` if only of the parameters is ``True``. In all
other cases ``False`` is returned. Example usage:
.. code-block:: clj
=> (xor True False)
True
=> (xor True True)
False
=> (xor [] [0])
True
yield
-----
@ -1502,7 +1542,7 @@ infinite series without consuming infinite amount of memory.
.. code-block:: clj
=> (defn multiply [bases coefficients]
... (for [[(, base coefficient) (zip bases coefficients)]]
... (for [(, base coefficient) (zip bases coefficients)]
... (yield (* base coefficient))))
=> (multiply (range 5) (range 5))
@ -1514,7 +1554,7 @@ infinite series without consuming infinite amount of memory.
=> (import random)
=> (defn random-numbers [low high]
... (while True (yield (.randint random low high))))
=> (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]

View File

@ -177,7 +177,7 @@ A cons cell is a 2-item object, containing a ``car`` (head) and a
building block, and S-expressions are actually represented as linked
lists of cons cells. This is not the case in Hy, as the usual
expressions are made of Python lists wrapped in a
``HyExpression``. However, the ``HyCons`` mimicks the behavior of
``HyExpression``. However, the ``HyCons`` mimics the behavior of
"usual" Lisp variants thusly:
- ``(cons something nil)`` is ``(HyExpression [something])``
@ -386,7 +386,7 @@ A first pass might be something like:
[(zero? obscure-name) ~zero-form]
[(neg? obscure-name) ~neg-form])))
where ``obsure-name`` is an attempt to pick some variable name as not to
where ``obscure-name`` is an attempt to pick some variable name as not to
conflict with other code. But of course, while well-intentioned,
this is no guarantee.

View File

@ -31,13 +31,18 @@ Quickstart
7. Open up an elite programming editor and type::
#! /usr/bin/env hy
(print "I was going to code in Python syntax, but then I got Hy.")
8. Save as ``awesome.hy``.
9. And run your first Hy program::
9. Make it executable::
hy awesome.hy
chmod +x awesome.hy
10. Take a deep breath so as to not hyperventilate.
11. Smile villainously and sneak off to your hydeaway and do
10. And run your first Hy program::
./awesome.hy
11. Take a deep breath so as to not hyperventilate.
12. Smile villainously and sneak off to your hydeaway and do
unspeakable things.

View File

@ -9,7 +9,7 @@ Hy Style Guide
The Hy style guide intends to be a set of ground rules for the Hyve
(yes, the Hy community prides itself in appending Hy to everything)
to write idiomatic Hy code. Hy derives a lot from Clojure & Common
Lisp, while always maintaining Python interopability.
Lisp, while always maintaining Python interoperability.
Prelude

View File

@ -119,7 +119,7 @@ processing"; this means that the structure of the program is
actually lists of lists. (If you're familiar with Python lists,
imagine the entire same structure as above but with square brackets
instead, any you'll be able to see the structure above as both a
program and a datastructure.) This is easier to understand with more
program and a data structure.) This is easier to understand with more
examples, so let's write a simple Python program, test it, and then
show the equivalent Hy program::
@ -168,7 +168,7 @@ There are some advantages to having a code structure that's actually a
very simple data structure as the core of Lisp is based on. For one
thing, it means that your programs are easy to parse and that the
entire actual structure of the program is very clearly exposed to you.
(There's an extra step in hy where the structure you see is converted
(There's an extra step in Hy where the structure you see is converted
to Python's own representations ... in "purer" Lisps such as Common
Lisp or Emacs Lisp, the data structure you see in the code and the
data structure that is executed is much more literally close.)
@ -258,6 +258,7 @@ In Hy, you would do:
.. code-block:: clj
(setv somevar 33)
(cond
[(> somevar 50)
(print "That variable is too big!")]
@ -306,6 +307,13 @@ Comments start with semicolons:
; (print "but this will not")
(+ 1 2 3) ; we'll execute the addition, but not this comment!
Hashbang (``#!``) syntax is supported:
.. code-block:: clj
#! /usr/bin/env hy
(print "Make me executable, and run me!")
Looping is not hard but has a kind of special structure. In Python,
we might do::
@ -505,22 +513,22 @@ In Hy:
Macros
======
One really powerful feature of Hy are macros. They are small functios that are
One really powerful feature of Hy are macros. They are small functions that are
used to generate code (or data). When program written in Hy is started, the
macros are executed and their output is placed in program source. After this,
macros are executed and their output is placed in the program source. After this,
the program starts executing normally. Very simple example:
.. code-block:: clj
=> (defmacro hello [person]
... `(print "Hello there," ~person))
=> (Hello "Tuukka")
=> (hello "Tuukka")
Hello there, Tuukka
The thing to notice here is that hello macro doesn't output anything on
screen. Instead it creates piece of code that is then executed and prints on
screen. Macro writes a piece of program that looks like this (provided that
we used "Tuukka" as parameter:
screen. This macro writes a piece of program that looks like this (provided that
we used "Tuukka" as parameter):
.. code-block:: clj
@ -536,14 +544,14 @@ We can also manipulate code with macros:
=> (rev (1 2 3 +))
6
The code that was generated with this macro just switched around some the
elements, so by the time program started executing, it actually red:
The code that was generated with this macro just switched around some of the
elements, so by the time program started executing, it actually reads:
.. code-block:: clj
(+ 1 2 3)
Sometimes it's nice to have a very short name for macro that doesn't take much
Sometimes it's nice to have a very short name for a macro that doesn't take much
space or use extra parentheses. Reader macros can be pretty useful in these
situations (and since Hy operates well with unicode, we aren't running out of
characters that soon):
@ -556,14 +564,14 @@ characters that soon):
=> #↻(1 2 3 +)
6
Macros are useful when one wishes to extend the Hy or write their own
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, it must be ``require``d:
with macros, ``require`` must be used:
.. code-block:: clj

View File

@ -97,7 +97,7 @@ class HyREPL(code.InteractiveConsole):
if e.source is None:
e.source = source
e.filename = filename
sys.stderr.write(str(e))
print(e, file=sys.stderr)
return False
try:
@ -110,7 +110,7 @@ class HyREPL(code.InteractiveConsole):
e.source = source
e.filename = filename
if SIMPLE_TRACEBACKS:
sys.stderr.write(str(e))
print(e, file=sys.stderr)
else:
self.showtraceback()
return False
@ -179,16 +179,18 @@ require("hy.cmdline", "__main__")
SIMPLE_TRACEBACKS = True
def run_command(source):
def pretty_error(func, *args, **kw):
try:
import_buffer_to_module("__main__", source)
return func(*args, **kw)
except (HyTypeError, LexException) as e:
if SIMPLE_TRACEBACKS:
sys.stderr.write(str(e))
return 1
raise
except Exception:
print(e, file=sys.stderr)
sys.exit(1)
raise
def run_command(source):
pretty_error(import_buffer_to_module, "__main__", source)
return 0
@ -199,22 +201,14 @@ def run_module(mod_name):
sys.argv = [pth] + sys.argv
return run_file(pth)
sys.stderr.write("{0}: module '{1}' not found.\n".format(hy.__appname__,
mod_name))
print("{0}: module '{1}' not found.\n".format(hy.__appname__, mod_name),
file=sys.stderr)
return 1
def run_file(filename):
from hy.importer import import_file_to_module
try:
import_file_to_module("__main__", filename)
except (HyTypeError, LexException) as e:
if SIMPLE_TRACEBACKS:
sys.stderr.write(str(e))
return 1
raise
except Exception:
raise
pretty_error(import_file_to_module, "__main__", filename)
return 0
@ -334,8 +328,8 @@ def cmdline_handler(scriptname, argv):
try:
return run_file(options.args[0])
except HyIOError as e:
sys.stderr.write("hy: Can't open file '%s': [Errno %d] %s\n" %
(e.filename, e.errno, e.strerror))
print("hy: Can't open file '{0}': [Errno {1}] {2}\n".format(
e.filename, e.errno, e.strerror), file=sys.stderr)
sys.exit(e.errno)
# User did NOTHING!
@ -359,11 +353,11 @@ def hyc_main():
for file in options.files:
try:
write_hy_as_pyc(file)
print("Compiling %s" % file)
pretty_error(write_hy_as_pyc, file)
except IOError as x:
sys.stderr.write("hyc: Can't open file '%s': [Errno %d] %s\n" %
(x.filename, x.errno, x.strerror))
print("hyc: Can't open file '{0}': [Errno {1}] {2}\n".format(
x.filename, x.errno, x.strerror), file=sys.stderr)
sys.exit(x.errno)
@ -391,7 +385,7 @@ def hy2py_main():
parser.exit(1, parser.format_help())
if options.with_source:
hst = import_file_to_hst(options.args[0])
hst = pretty_error(import_file_to_hst, options.args[0])
# need special printing on Windows in case the
# codepage doesn't support utf-8 characters
if PY3 and platform.system() == "Windows":
@ -405,7 +399,7 @@ def hy2py_main():
print()
print()
_ast = import_file_to_ast(options.args[0], module_name)
_ast = pretty_error(import_file_to_ast, options.args[0], module_name)
if options.with_ast:
if PY3 and platform.system() == "Windows":
_print_for_windows(astor.dump(_ast))

View File

@ -532,7 +532,7 @@ class HyASTCompiler(object):
raise HyTypeError(expr,
"There can only be one "
"&rest argument")
varargs = str(expr)
varargs = expr
elif lambda_keyword == "&key":
if type(expr) != HyDict:
raise HyTypeError(expr,
@ -547,6 +547,10 @@ class HyASTCompiler(object):
# defining keyword arguments.
it = iter(expr)
for k, v in zip(it, it):
if not isinstance(k, HyString):
raise HyTypeError(expr,
"Only strings can be used "
"as parameter names")
args.append(k)
ret += self.compile(v)
defaults.append(ret.force_expr)
@ -560,6 +564,10 @@ class HyASTCompiler(object):
else:
k = expr
v = HySymbol("None").replace(k)
if not isinstance(k, HyString):
raise HyTypeError(expr,
"Only strings can be used as "
"parameter names")
args.append(k)
ret += self.compile(v)
defaults.append(ret.force_expr)
@ -586,25 +594,26 @@ class HyASTCompiler(object):
raise HyTypeError(expr,
"There can only be one "
"&kwargs argument")
kwargs = str(expr)
kwargs = expr
return ret, args, defaults, varargs, kwonlyargs, kwonlydefaults, kwargs
def _storeize(self, name, func=None):
def _storeize(self, expr, name, func=None):
"""Return a new `name` object with an ast.Store() context"""
if not func:
func = ast.Store
if isinstance(name, Result):
if not name.is_expr():
raise TypeError("Can't assign / delete a non-expression")
raise HyTypeError(expr,
"Can't assign or delete a non-expression")
name = name.expr
if isinstance(name, (ast.Tuple, ast.List)):
typ = type(name)
new_elts = []
for x in name.elts:
new_elts.append(self._storeize(x, func))
new_elts.append(self._storeize(expr, x, func))
new_name = typ(elts=new_elts)
elif isinstance(name, ast.Name):
new_name = ast.Name(id=name.id, arg=name.arg)
@ -613,7 +622,9 @@ class HyASTCompiler(object):
elif isinstance(name, ast.Attribute):
new_name = ast.Attribute(value=name.value, attr=name.attr)
else:
raise TypeError("Can't assign / delete a %s object" % type(name))
raise HyTypeError(expr,
"Can't assign or delete a %s" %
type(expr).__name__)
new_name.ctx = func()
ast.copy_location(new_name, name)
@ -953,7 +964,7 @@ class HyASTCompiler(object):
name = ast_str(name)
else:
# Python2 requires an ast.Name, set to ctx Store.
name = self._storeize(self.compile(name))
name = self._storeize(name, self.compile(name))
else:
name = None
@ -971,7 +982,7 @@ class HyASTCompiler(object):
col_offset=expr.start_column,
ctx=ast.Load())
else:
# [] → all exceptions catched
# [] → all exceptions caught
_type = Result()
elif isinstance(exceptions_list, HySymbol):
_type = self.compile(exceptions_list)
@ -1343,11 +1354,13 @@ class HyASTCompiler(object):
col_offset=root.start_column)
return result
ld_targets, ret, _ = self._compile_collect(expr)
del_targets = []
for target in ld_targets:
del_targets.append(self._storeize(target, ast.Del))
ret = Result()
for target in expr:
compiled_target = self.compile(target)
ret += compiled_target
del_targets.append(self._storeize(target, compiled_target,
ast.Del))
return ret + ast.Delete(
lineno=expr.start_line,
@ -1436,7 +1449,7 @@ class HyASTCompiler(object):
thing = None
if args != []:
thing = self._storeize(self.compile(args.pop(0)))
thing = self._storeize(args[0], self.compile(args.pop(0)))
body = self._compile_branch(expr)
@ -1498,7 +1511,7 @@ class HyASTCompiler(object):
gen = []
for target, iterable in paired_gens:
comp_target = self.compile(target)
target = self._storeize(comp_target)
target = self._storeize(target, comp_target)
gen_res += self.compile(iterable)
gen.append(ast.comprehension(
target=target,
@ -1963,7 +1976,7 @@ class HyASTCompiler(object):
op = ops[expression[0]]
target = self._storeize(self.compile(expression[1]))
target = self._storeize(expression[1], self.compile(expression[1]))
ret = self.compile(expression[2])
ret += ast.AugAssign(
@ -1984,7 +1997,7 @@ class HyASTCompiler(object):
@builds(HyExpression)
def compile_expression(self, expression):
# Perform macro expansions
expression = macroexpand(expression, self.module_name)
expression = macroexpand(expression, self)
if not isinstance(expression, HyExpression):
# Go through compile again if the type changed.
return self.compile(expression)
@ -2099,7 +2112,7 @@ class HyASTCompiler(object):
and '.' not in name:
result.rename(name)
else:
st_name = self._storeize(ld_name)
st_name = self._storeize(name, ld_name)
result += ast.Assign(
lineno=start_line,
col_offset=start_column,
@ -2127,7 +2140,7 @@ class HyASTCompiler(object):
raise HyTypeError(expression,
"for requires two forms in the list")
target = self._storeize(self.compile(target_name))
target = self._storeize(target_name, self.compile(target_name))
ret = Result()
@ -2257,10 +2270,14 @@ class HyASTCompiler(object):
# list because it's really just an internal parsing thing.
if kwargs:
kwargs = ast.arg(arg=kwargs, annotation=None)
kwargs = ast.arg(arg=ast_str(kwargs), annotation=None,
lineno=kwargs.start_line,
col_offset=kwargs.start_column)
if stararg:
stararg = ast.arg(arg=stararg, annotation=None)
stararg = ast.arg(arg=ast_str(stararg), annotation=None,
lineno=stararg.start_line,
col_offset=stararg.start_column)
# Let's find a better home for these guys.
else:
@ -2275,6 +2292,12 @@ class HyASTCompiler(object):
col_offset=x.start_column)
for x in kwonlyargs]
if kwargs:
kwargs = ast_str(kwargs)
if stararg:
stararg = ast_str(stararg)
args = ast.arguments(
args=args,
vararg=stararg,
@ -2294,7 +2317,10 @@ class HyASTCompiler(object):
return ret
if body.expr:
if body.contains_yield:
if body.contains_yield and not PY33:
# Prior to PEP 380 (introduced in Python 3.3)
# generators may not have a value in a return
# statement.
body += body.expr_as_stmt()
else:
body += ast.Return(value=body.expr,
@ -2381,7 +2407,7 @@ class HyASTCompiler(object):
body += self.compile(rewire_init(expr))
for expression in expressions:
expr = rewire_init(macroexpand(expression, self.module_name))
expr = rewire_init(macroexpand(expression, self))
body += self.compile(expr)
self.allow_builtins = allow_builtins
@ -2441,7 +2467,7 @@ class HyASTCompiler(object):
NOT_READERS = [":", "&"]
if name in NOT_READERS or len(name) > 1:
raise NameError("%s can't be used as a macro reader symbol" % name)
if not isinstance(name, HySymbol):
if not isinstance(name, HySymbol) and not isinstance(name, HyString):
raise HyTypeError(name,
("received a `%s' instead of a symbol "
"for reader macro name" % type(name).__name__))
@ -2467,10 +2493,7 @@ class HyASTCompiler(object):
"Trying to expand a reader macro using `{0}' instead "
"of string".format(type(str_char).__name__),
)
module = self.module_name
expr = reader_macroexpand(str_char, expression.pop(0), module)
expr = reader_macroexpand(str_char, expression.pop(0), self)
return self.compile(expr)
@builds("eval_and_compile")
@ -2568,11 +2591,6 @@ def hy_compile(tree, module_name, root=ast.Module, get_expr=False):
`last_expression` is the.
"""
if hasattr(sys, "subversion"):
implementation = sys.subversion[0].lower()
elif hasattr(sys, "implementation"):
implementation = sys.implementation.name.lower()
body = []
expr = None
@ -2592,12 +2610,6 @@ def hy_compile(tree, module_name, root=ast.Module, get_expr=False):
ret = root(body=body)
# PyPy _really_ doesn't like the ast going backwards...
if implementation != "cpython":
for node in ast.walk(ret):
node.lineno = 1
node.col_offset = 1
if get_expr:
expr = ast.Expression(body=expr)
ret = (ret, expr)

View File

@ -25,8 +25,9 @@
;;; These macros make writing functional programs more concise
(defmacro ap-if (test-form &rest args)
`(let [it ~test-form] (if it ~@args)))
(defmacro ap-if [test-form then-form &optional else-form]
`(let [it ~test-form]
(if it ~then-form ~else-form)))
(defmacro ap-each [lst &rest body]
@ -35,7 +36,7 @@
(defmacro ap-each-while [lst form &rest body]
"Evalutate the body form for each element in the list while the
"Evaluate the body form for each element in the list while the
predicate form evaluates to True."
`(let [p (lambda [it] ~form)]
(for [it ~lst]
@ -140,7 +141,7 @@
(.startswith a 'x)
(.isdigit (cdr a))))
[0]))))])
;; generate the &rest paremeter only if 'xi is present in body
;; generate the &rest parameter only if 'xi is present in body
~@(if (in 'xi flatbody)
'(&rest xi)
'())]

View File

@ -37,6 +37,7 @@
[hy.models.symbol [HySymbol]]
[hy.models.keyword [HyKeyword *keyword-prefix*]])
(import [hy.lex [LexException PrematureEndOfInput tokenize]])
(import [hy.compiler [HyASTCompiler]])
(defn _numeric-check [x]
(if (not (numeric? x))
@ -296,14 +297,14 @@
(import hy.macros)
(setv name (calling-module-name))
(hy.macros.macroexpand form name))
(hy.macros.macroexpand form (HyASTCompiler name)))
(defn macroexpand-1 [form]
"Return the single step macro expansion of form"
(import hy.macros)
(setv name (calling-module-name))
(hy.macros.macroexpand-1 form name))
(hy.macros.macroexpand-1 form (HyASTCompiler name)))
(defn merge-with [f &rest maps]
"Returns a map that consists of the rest of the maps joined onto
@ -463,6 +464,11 @@
(hyify (. value __name__))
(except [] (string value))))))
(defn xor [a b]
"Perform exclusive or between two parameters"
(or (and a (not b))
(and b (not a))))
(def *exports*
'[*map accumulate butlast calling-module-name chain coll? combinations
compress cons cons? count cycle dec distinct disassemble drop drop-last
@ -472,4 +478,4 @@
last list* macroexpand macroexpand-1 map merge-with multicombinations name
neg? nil? none? nth numeric? odd? partition permutations pos? product range
read read-str remove repeat repeatedly rest reduce second some string
string? symbol? take take-nth take-while tee zero? zip zip-longest])
string? symbol? take take-nth take-while xor tee zero? zip zip-longest])

View File

@ -25,8 +25,6 @@ import traceback
from clint.textui import colored
from hy._compat import PY3
class HyError(Exception):
"""
@ -101,12 +99,9 @@ class HyTypeError(TypeError):
result += colored.yellow("%s: %s\n\n" %
(self.__class__.__name__,
self.message))
self.message.encode('utf-8')))
if not PY3:
return result.encode('utf-8')
else:
return result
return result
class HyMacroExpansionError(HyTypeError):

View File

@ -52,6 +52,8 @@ def macro(name):
"""
def _(fn):
argspec = getargspec(fn)
fn._hy_macro_pass_compiler = argspec.keywords is not None
module_name = fn.__module__
if module_name.startswith("hy.core"):
module_name = None
@ -123,7 +125,7 @@ def load_macros(module_name):
_import(module)
def make_emtpy_fn_copy(fn):
def make_empty_fn_copy(fn):
argspec = getargspec(fn)
formatted_args = formatargspec(*argspec)
fn_str = 'lambda {}: None'.format(
@ -133,22 +135,22 @@ def make_emtpy_fn_copy(fn):
return empty_fn
def macroexpand(tree, module_name):
def macroexpand(tree, compiler):
"""Expand the toplevel macros for the `tree`.
Load the macros from the given `module_name`, then expand the (top-level)
macros in `tree` until it stops changing.
"""
load_macros(module_name)
load_macros(compiler.module_name)
old = None
while old != tree:
old = tree
tree = macroexpand_1(tree, module_name)
tree = macroexpand_1(tree, compiler)
return tree
def macroexpand_1(tree, module_name):
def macroexpand_1(tree, compiler):
"""Expand the toplevel macro from `tree` once, in the context of
`module_name`."""
if isinstance(tree, HyExpression):
@ -161,22 +163,25 @@ def macroexpand_1(tree, module_name):
ntree = HyExpression(tree[:])
ntree.replace(tree)
opts = {}
if isinstance(fn, HyString):
m = _hy_macros[module_name].get(fn)
m = _hy_macros[compiler.module_name].get(fn)
if m is None:
m = _hy_macros[None].get(fn)
if m is not None:
if m._hy_macro_pass_compiler:
opts['compiler'] = compiler
try:
m_copy = make_emtpy_fn_copy(m)
m_copy(*ntree[1:])
m_copy = make_empty_fn_copy(m)
m_copy(*ntree[1:], **opts)
except TypeError as e:
msg = "expanding `" + str(tree[0]) + "': "
msg += str(e).replace("<lambda>()", "", 1).strip()
raise HyMacroExpansionError(tree, msg)
try:
obj = wrap_value(m(*ntree[1:]))
obj = wrap_value(m(*ntree[1:], **opts))
except HyTypeError as e:
if e.expression is None:
e.expression = tree
@ -186,16 +191,15 @@ def macroexpand_1(tree, module_name):
raise HyMacroExpansionError(tree, msg)
replace_hy_obj(obj, tree)
return obj
return ntree
return tree
def reader_macroexpand(char, tree, module_name):
def reader_macroexpand(char, tree, compiler):
"""Expand the reader macro "char" with argument `tree`."""
load_macros(module_name)
load_macros(compiler.module_name)
reader_macro = _hy_reader[module_name].get(char)
reader_macro = _hy_reader[compiler.module_name].get(char)
if reader_macro is None:
try:
reader_macro = _hy_reader[None][char]

View File

@ -50,7 +50,7 @@ if sys.version_info[:2] < (2, 7):
install_requires.append('argparse>=1.2.1')
install_requires.append('importlib>=1.0.2')
if os.name == 'nt':
install_requires.append('pyreadline==2.0')
install_requires.append('pyreadline>=2.1')
setup(
name=PKG,
@ -89,6 +89,7 @@ setup(
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Topic :: Software Development :: Code Generators",
"Topic :: Software Development :: Compilers",
"Topic :: Software Development :: Libraries",

View File

@ -372,6 +372,7 @@ def test_ast_lambda_lists():
cant_compile('(fn [&key {"a" b} &key {"foo" bar}] [a foo])')
cant_compile('(fn [&optional a &key {"foo" bar}] [a foo])')
cant_compile('(fn [&optional [a b c]] a)')
cant_compile('(fn [&optional [1 2]] (list 1 2))')
def test_ast_print():
@ -402,6 +403,7 @@ def test_lambda_list_keywords_key():
""" Ensure we can compile functions with &key."""
can_compile("(fn (x &key {foo True}) (list x foo))")
cant_compile("(fn (x &key {bar \"baz\"} &key {foo 42}) (list x bar foo))")
cant_compile("(fn (x &key {1 2 3 4}) (list x))")
def test_lambda_list_keywords_kwargs():

View File

@ -26,6 +26,8 @@ from hy import compiler
from hy.models.expression import HyExpression
from hy.models.list import HyList
from hy.models.symbol import HySymbol
from hy.models.integer import HyInteger
from hy._compat import PY33
if sys.version_info[0] <= 2 and sys.version_info[1] <= 6:
import unittest2 as unittest
@ -98,3 +100,37 @@ class HyASTCompilerTest(unittest.TestCase):
expr = ret.expr
self.assertIsInstance(expr, ast.Name)
self.assertEqual(expr.id, "c")
def test_compiler_yield_return(self):
"""
Check that the compiler correctly generates return statements for
a generator function. In Python versions prior to 3.3, the return
statement in a generator can't take a value, so the final expression
should not generate a return statement. From 3.3 onwards a return
value should be generated.
"""
ret = self.c.compile_function_def(
self._make_expression(HySymbol("fn"),
HyList(),
HyExpression([HySymbol("yield"),
HyInteger(2)]),
HyExpression([HySymbol("+"),
HyInteger(1),
HyInteger(1)])))
self.assertEqual(len(ret.stmts), 1)
stmt = ret.stmts[0]
self.assertIsInstance(stmt, ast.FunctionDef)
body = stmt.body
self.assertEquals(len(body), 2)
self.assertIsInstance(body[0], ast.Expr)
self.assertIsInstance(body[0].value, ast.Yield)
if PY33:
# From 3.3+, the final statement becomes a return value
self.assertIsInstance(body[1], ast.Return)
self.assertIsInstance(body[1].value, ast.BinOp)
else:
# In earlier versions, the expression is not returned
self.assertIsInstance(body[1], ast.Expr)
self.assertIsInstance(body[1].value, ast.BinOp)

View File

@ -8,6 +8,8 @@ from hy.models.symbol import HySymbol
from hy.models.expression import HyExpression
from hy.errors import HyMacroExpansionError
from hy.compiler import HyASTCompiler
@macro("test")
def tmac(*tree):
@ -17,14 +19,16 @@ def tmac(*tree):
def test_preprocessor_simple():
""" Test basic macro expansion """
obj = macroexpand(tokenize('(test "one" "two")')[0], __name__)
obj = macroexpand(tokenize('(test "one" "two")')[0],
HyASTCompiler(__name__))
assert obj == HyList(["one", "two"])
assert type(obj) == HyList
def test_preprocessor_expression():
""" Test that macro expansion doesn't recurse"""
obj = macroexpand(tokenize('(test (test "one" "two"))')[0], __name__)
obj = macroexpand(tokenize('(test (test "one" "two"))')[0],
HyASTCompiler(__name__))
assert type(obj) == HyList
assert type(obj[0]) == HyExpression
@ -35,13 +39,13 @@ def test_preprocessor_expression():
obj = HyList([HyString("one"), HyString("two")])
obj = tokenize('(shill ["one" "two"])')[0][1]
assert obj == macroexpand(obj, '')
assert obj == macroexpand(obj, HyASTCompiler(""))
def test_preprocessor_exceptions():
""" Test that macro expansion raises appropriate exceptions"""
try:
macroexpand(tokenize('(defn)')[0], __name__)
macroexpand(tokenize('(defn)')[0], HyASTCompiler(__name__))
assert False
except HyMacroExpansionError as e:
assert "_hy_anon_fn_" not in str(e)

View File

@ -1,11 +1,12 @@
from hy.macros import macroexpand
from hy.compiler import HyTypeError
from hy.compiler import HyTypeError, HyASTCompiler
from hy.lex import tokenize
def test_reader_macro_error():
"""Check if we get correct error with wrong disptach character"""
"""Check if we get correct error with wrong dispatch character"""
try:
macroexpand(tokenize("(dispatch_reader_macro '- '())")[0], __name__)
macroexpand(tokenize("(dispatch_reader_macro '- '())")[0],
HyASTCompiler(__name__))
except HyTypeError as e:
assert "with the character `-`" in str(e)

View File

@ -18,10 +18,11 @@
;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
;; DEALINGS IN THE SOFTWARE.
;;;; some simple helpers
(import [hy.errors [HyMacroExpansionError]])
(require hy.contrib.anaphoric)
;;;; some simple helpers
(defn assert-true [x]
(assert (= True x)))
@ -35,7 +36,10 @@
(defn test-ap-if []
"NATIVE: testing anaphoric if"
(ap-if true (assert-true it))
(ap-if false true (assert-false it)))
(ap-if false true (assert-false it))
(try (macroexpand '(ap-if true))
(except [HyMacroExpansionError] true)
(else (assert false))))
(defn test-ap-each []
"NATIVE: testing anaphoric each"

View File

@ -1,7 +1,8 @@
(import [tests.resources [kwtest function-with-a-dash]]
[os.path [exists isdir isfile]]
[sys :as systest]
[operator [or_]])
[operator [or_]]
[hy.errors [HyTypeError]])
(import sys)
(import [hy._compat [PY33 PY34 PY35]])
@ -60,6 +61,7 @@
(setv (get foo 0) 12)
(assert (= (get foo 0) 12)))
(defn test-setv-builtin []
"NATIVE: test that setv doesn't work on builtins"
(try (eval '(setv False 1))
@ -93,6 +95,37 @@
(except [e [TypeError]] (assert (in "`setv' needs an even number of arguments" (str e))))))
(defn test-store-errors []
"NATIVE: test that setv raises the correct errors when given wrong argument types"
(try
(do
(eval '(setv (do 1 2) 1))
(assert false))
(except [e HyTypeError]
(assert (= e.message "Can't assign or delete a non-expression"))))
(try
(do
(eval '(setv 1 1))
(assert false))
(except [e HyTypeError]
(assert (= e.message "Can't assign or delete a HyInteger"))))
(try
(do
(eval '(setv {1 2} 1))
(assert false))
(except [e HyTypeError]
(assert (= e.message "Can't assign or delete a HyDict"))))
(try
(do
(eval '(del 1 1))
(assert false))
(except [e HyTypeError]
(assert (= e.message "Can't assign or delete a HyInteger")))))
(defn test-fn-corner-cases []
"NATIVE: tests that fn/defn handles corner cases gracefully"
(try (eval '(fn "foo"))
@ -894,6 +927,15 @@
(assert (= a 1)))
(defn test-xor []
"NATIVE: test the xor macro"
(let [xor-both-true (xor true true)
xor-both-false (xor false false)
xor-true-false (xor true false)]
(assert (= xor-both-true false))
(assert (= xor-both-false false))
(assert (= xor-true-false true))))
(defn test-if-return-branching []
"NATIVE: test the if return branching"
; thanks, algernon
@ -965,7 +1007,6 @@
(defn test-eval-failure []
"NATIVE: test eval failure modes"
(import [hy.errors [HyTypeError]])
; yo dawg
(try (eval '(eval)) (except [e HyTypeError]) (else (assert False)))
(try (eval '(eval "snafu")) (except [e HyTypeError]) (else (assert False)))

View File

@ -2,7 +2,7 @@
(defn test-reader-macro []
"Test a basic redaer macro"
"Test a basic reader macro"
(defreader ^ [expr]
expr)
@ -37,6 +37,15 @@
(assert (= (, 1 2 3) a)))
(defn test-reader-macro-string-name []
"Test if defreader accepts a string as a macro name."
(defreader "." [expr]
expr)
(assert (= #."works" "works")))
(defn test-builtin-decorator-reader []
(defn increment-arguments [func]
"Increments each argument passed to the decorated function."
@ -55,7 +64,7 @@
(assert (= (, (, 2 3 4) {"quux" 5 "baz" 6})
(foo 1 2 3 :quux 4 :baz 5)))
;; @wraps preserved the doctstring and __name__
;; @wraps preserved the docstring and __name__
(assert (= "foo" (. foo --name--)))
(assert (= "Bar." (. foo --doc--)))

View File

@ -1,11 +1,13 @@
[tox]
envlist = py27,pypy,py33,flake8
envlist = py27,pypy,py33,py34,py35,flake8
skipsdist = True
[testenv]
commands =
pip install --allow-all-external -e .
nosetests
passenv =
TERM
deps =
-rrequirements-dev.txt