Merge branch 'master' of https://github.com/hylang/hy into kwonly_err
Conflicts: docs/tutorial.rst
This commit is contained in:
commit
7ee7428870
1
AUTHORS
1
AUTHORS
@ -65,3 +65,4 @@
|
|||||||
* Antony Woods <antony@teamwoods.org>
|
* Antony Woods <antony@teamwoods.org>
|
||||||
* Matthew Egan Odendahl <github.gilch@xoxy.net>
|
* Matthew Egan Odendahl <github.gilch@xoxy.net>
|
||||||
* Tim Martin <tim@asymptotic.co.uk>
|
* Tim Martin <tim@asymptotic.co.uk>
|
||||||
|
* Johnathon Mlady <john@digitalvectorz.com>
|
||||||
|
@ -13,6 +13,9 @@ concise and easy to read.
|
|||||||
|
|
||||||
-- Wikipedia (http://en.wikipedia.org/wiki/Anaphoric_macro)
|
-- 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:
|
.. _ap-if:
|
||||||
|
|
||||||
@ -233,7 +236,7 @@ xi
|
|||||||
|
|
||||||
Usage ``(xi body ...)``
|
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 (``#()``).
|
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))
|
=> (def add-10 (xi + 10 x1))
|
||||||
=> (add-10 6)
|
=> (add-10 6)
|
||||||
16
|
16
|
||||||
|
|
||||||
|
|
||||||
|
@ -823,26 +823,47 @@ keyword, the second function would have raised a ``NameError``.
|
|||||||
(set-a 5)
|
(set-a 5)
|
||||||
(print-a)
|
(print-a)
|
||||||
|
|
||||||
if / if-not
|
if / if* / if-not
|
||||||
-----------
|
-----------------
|
||||||
|
|
||||||
.. versionadded:: 0.10.0
|
.. versionadded:: 0.10.0
|
||||||
if-not
|
if-not
|
||||||
|
|
||||||
``if`` is used to conditionally select code to be executed. It has to contain a
|
``if / if* / if-not`` respect Python *truthiness*, that is, a *test* fails if it
|
||||||
condition block and the block to be executed if the condition block evaluates
|
evaluates to a "zero" (including values of ``len`` zero, ``nil``, and
|
||||||
to ``True``. Optionally, it may contain a final block that is executed in case
|
``false``), and passes otherwise, but values with a ``__bool__`` method
|
||||||
the evaluation of the condition is ``False``.
|
(``__nonzero__`` in Python 2) can overrides this.
|
||||||
|
|
||||||
``if-not`` is similar, but the second block will be executed when the condition
|
The ``if`` macro is for conditionally selecting an expression for evaluation.
|
||||||
fails while the third and final block is executed when the test succeeds -- the
|
The result of the selected expression becomes the result of the entire ``if``
|
||||||
opposite order of ``if``.
|
form. ``if`` can select a group of expressions with the help of a ``do`` block.
|
||||||
|
|
||||||
|
``if`` takes any number of alternating *test* and *then* expressions, plus an
|
||||||
|
optional *else* expression at the end, which defaults to ``nil``. ``if`` checks
|
||||||
|
each *test* in turn, and selects the *then* corresponding to the first passed
|
||||||
|
test. ``if`` does not evaluate any expressions following its selection, similar
|
||||||
|
to the ``if/elif/else`` control structure from Python. If no tests pass, ``if``
|
||||||
|
selects *else*.
|
||||||
|
|
||||||
|
The ``if*`` special form is restricted to 2 or 3 arguments, but otherwise works
|
||||||
|
exactly like ``if`` (which expands to nested ``if*`` forms), so there is
|
||||||
|
generally no reason to use it directly.
|
||||||
|
|
||||||
|
``if-not`` is similar to ``if*`` but the second expression will be executed
|
||||||
|
when the condition fails while the third and final expression is executed when
|
||||||
|
the test succeeds -- the opposite order of ``if*``. The final expression is
|
||||||
|
again optional and defaults to ``nil``.
|
||||||
|
|
||||||
Example usage:
|
Example usage:
|
||||||
|
|
||||||
.. code-block:: clj
|
.. code-block:: clj
|
||||||
|
|
||||||
(if (money-left? account)
|
(print (if (< n 0.0) "negative"
|
||||||
|
(= n 0.0) "zero"
|
||||||
|
(> n 0.0) "positive"
|
||||||
|
"not a number"))
|
||||||
|
|
||||||
|
(if* (money-left? account)
|
||||||
(print "let's go shopping")
|
(print "let's go shopping")
|
||||||
(print "let's go and work"))
|
(print "let's go and work"))
|
||||||
|
|
||||||
@ -850,9 +871,6 @@ Example usage:
|
|||||||
(print "let's go and work")
|
(print "let's go and work")
|
||||||
(print "let's go shopping"))
|
(print "let's go shopping"))
|
||||||
|
|
||||||
Python truthiness is respected. ``None``, ``False``, zero of any numeric type,
|
|
||||||
an empty sequence, and an empty dictionary are considered ``False``; everything
|
|
||||||
else is considered ``True``.
|
|
||||||
|
|
||||||
|
|
||||||
lif and lif-not
|
lif and lif-not
|
||||||
@ -1484,7 +1502,7 @@ infinite series without consuming infinite amount of memory.
|
|||||||
.. code-block:: clj
|
.. code-block:: clj
|
||||||
|
|
||||||
=> (defn multiply [bases coefficients]
|
=> (defn multiply [bases coefficients]
|
||||||
... (for [[(, base coefficient) (zip bases coefficients)]]
|
... (for [(, base coefficient) (zip bases coefficients)]
|
||||||
... (yield (* base coefficient))))
|
... (yield (* base coefficient))))
|
||||||
|
|
||||||
=> (multiply (range 5) (range 5))
|
=> (multiply (range 5) (range 5))
|
||||||
@ -1496,7 +1514,7 @@ infinite series without consuming infinite amount of memory.
|
|||||||
=> (import random)
|
=> (import random)
|
||||||
=> (defn random-numbers [low high]
|
=> (defn random-numbers [low high]
|
||||||
... (while True (yield (.randint random 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]
|
[7, 41, 6, 22, 32, 17, 5, 38, 18, 38, 17, 14, 23, 23, 19]
|
||||||
|
|
||||||
|
|
||||||
|
@ -436,6 +436,26 @@ themselves as an iterator when ``(iter x)`` is called. Contrast with
|
|||||||
=> (iterator? (iter {:a 1 :b 2 :c 3}))
|
=> (iterator? (iter {:a 1 :b 2 :c 3}))
|
||||||
True
|
True
|
||||||
|
|
||||||
|
.. _keyword-fn:
|
||||||
|
|
||||||
|
keyword
|
||||||
|
-------
|
||||||
|
|
||||||
|
.. versionadded:: 0.10.1
|
||||||
|
|
||||||
|
Usage: ``(keyword "foo")``
|
||||||
|
|
||||||
|
Create a keyword from the given value. Strings, numbers, and even
|
||||||
|
objects with the `__name__` magic will work.
|
||||||
|
|
||||||
|
.. code-block:: hy
|
||||||
|
|
||||||
|
=> (keyword "foo")
|
||||||
|
u'\ufdd0:foo'
|
||||||
|
|
||||||
|
=> (keyword 1)
|
||||||
|
u'\ufdd0:1'
|
||||||
|
|
||||||
.. _keyword?-fn:
|
.. _keyword?-fn:
|
||||||
|
|
||||||
keyword?
|
keyword?
|
||||||
@ -536,6 +556,24 @@ calling ``(f val-in-result val-in-latter)``.
|
|||||||
{u'a': 11L, u'c': 30L, u'b': 20L}
|
{u'a': 11L, u'c': 30L, u'b': 20L}
|
||||||
|
|
||||||
|
|
||||||
|
.. _name-fn:
|
||||||
|
|
||||||
|
name
|
||||||
|
----
|
||||||
|
|
||||||
|
.. versionadded:: 0.10.1
|
||||||
|
|
||||||
|
Usage: ``(name :keyword)``
|
||||||
|
|
||||||
|
Convert the given value to a string. Keyword special character will be
|
||||||
|
stripped. Strings will be used as is. Even objects with the `__name__`
|
||||||
|
magic will work.
|
||||||
|
|
||||||
|
.. code-block:: hy
|
||||||
|
|
||||||
|
=> (name :foo)
|
||||||
|
u'foo'
|
||||||
|
|
||||||
.. _neg?-fn:
|
.. _neg?-fn:
|
||||||
|
|
||||||
neg?
|
neg?
|
||||||
|
@ -479,8 +479,7 @@ In Hy:
|
|||||||
"Yet Another Example Class"
|
"Yet Another Example Class"
|
||||||
|
|
||||||
(defn --init-- [self x]
|
(defn --init-- [self x]
|
||||||
(setv self.x x)
|
(setv self.x x))
|
||||||
None)
|
|
||||||
|
|
||||||
(defn get-x [self]
|
(defn get-x [self]
|
||||||
"Return our copy of x"
|
"Return our copy of x"
|
||||||
@ -515,7 +514,7 @@ the program starts executing normally. Very simple example:
|
|||||||
|
|
||||||
=> (defmacro hello [person]
|
=> (defmacro hello [person]
|
||||||
... `(print "Hello there," ~person))
|
... `(print "Hello there," ~person))
|
||||||
=> (Hello "Tuukka")
|
=> (hello "Tuukka")
|
||||||
Hello there, Tuukka
|
Hello there, Tuukka
|
||||||
|
|
||||||
The thing to notice here is that hello macro doesn't output anything on
|
The thing to notice here is that hello macro doesn't output anything on
|
||||||
@ -557,10 +556,21 @@ characters that soon):
|
|||||||
=> #↻(1 2 3 +)
|
=> #↻(1 2 3 +)
|
||||||
6
|
6
|
||||||
|
|
||||||
Macros are useful when one wished to extend the Hy or write their own
|
Macros are useful when one wishes to extend the Hy or write their own
|
||||||
language on top of that. Many features of Hy are macros, like ``when``,
|
language on top of that. Many features of Hy are macros, like ``when``,
|
||||||
``cond`` and ``->``.
|
``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:
|
||||||
|
|
||||||
|
.. code-block:: clj
|
||||||
|
|
||||||
|
=> (require tutorial.macros)
|
||||||
|
=> (rev (1 2 3 +))
|
||||||
|
6
|
||||||
|
|
||||||
Hy <-> Python interop
|
Hy <-> Python interop
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
@ -604,7 +614,7 @@ To use keyword arguments, you can use in ``greetings.py``::
|
|||||||
(import greetings)
|
(import greetings)
|
||||||
(.greet greetings "Foo")
|
(.greet greetings "Foo")
|
||||||
(.greet greetings "Foo" "Darth")
|
(.greet greetings "Foo" "Darth")
|
||||||
(apply (. greetings greet) ["Foo"] {"title" "Lord"})
|
(apply (. greetings greet) ["Foo"] {:title "Lord"})
|
||||||
|
|
||||||
Which would output::
|
Which would output::
|
||||||
|
|
||||||
|
125
hy/compiler.py
125
hy/compiler.py
@ -377,6 +377,7 @@ class HyASTCompiler(object):
|
|||||||
self.anon_var_count = 0
|
self.anon_var_count = 0
|
||||||
self.imports = defaultdict(set)
|
self.imports = defaultdict(set)
|
||||||
self.module_name = module_name
|
self.module_name = module_name
|
||||||
|
self.temp_if = None
|
||||||
if not module_name.startswith("hy.core"):
|
if not module_name.startswith("hy.core"):
|
||||||
# everything in core needs to be explicit.
|
# everything in core needs to be explicit.
|
||||||
load_stdlib()
|
load_stdlib()
|
||||||
@ -589,21 +590,22 @@ class HyASTCompiler(object):
|
|||||||
|
|
||||||
return ret, args, defaults, varargs, kwonlyargs, kwonlydefaults, kwargs
|
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"""
|
"""Return a new `name` object with an ast.Store() context"""
|
||||||
if not func:
|
if not func:
|
||||||
func = ast.Store
|
func = ast.Store
|
||||||
|
|
||||||
if isinstance(name, Result):
|
if isinstance(name, Result):
|
||||||
if not name.is_expr():
|
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
|
name = name.expr
|
||||||
|
|
||||||
if isinstance(name, (ast.Tuple, ast.List)):
|
if isinstance(name, (ast.Tuple, ast.List)):
|
||||||
typ = type(name)
|
typ = type(name)
|
||||||
new_elts = []
|
new_elts = []
|
||||||
for x in name.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)
|
new_name = typ(elts=new_elts)
|
||||||
elif isinstance(name, ast.Name):
|
elif isinstance(name, ast.Name):
|
||||||
new_name = ast.Name(id=name.id, arg=name.arg)
|
new_name = ast.Name(id=name.id, arg=name.arg)
|
||||||
@ -612,7 +614,9 @@ class HyASTCompiler(object):
|
|||||||
elif isinstance(name, ast.Attribute):
|
elif isinstance(name, ast.Attribute):
|
||||||
new_name = ast.Attribute(value=name.value, attr=name.attr)
|
new_name = ast.Attribute(value=name.value, attr=name.attr)
|
||||||
else:
|
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()
|
new_name.ctx = func()
|
||||||
ast.copy_location(new_name, name)
|
ast.copy_location(new_name, name)
|
||||||
@ -952,7 +956,7 @@ class HyASTCompiler(object):
|
|||||||
name = ast_str(name)
|
name = ast_str(name)
|
||||||
else:
|
else:
|
||||||
# Python2 requires an ast.Name, set to ctx Store.
|
# Python2 requires an ast.Name, set to ctx Store.
|
||||||
name = self._storeize(self.compile(name))
|
name = self._storeize(name, self.compile(name))
|
||||||
else:
|
else:
|
||||||
name = None
|
name = None
|
||||||
|
|
||||||
@ -998,16 +1002,46 @@ class HyASTCompiler(object):
|
|||||||
name=name,
|
name=name,
|
||||||
body=body)
|
body=body)
|
||||||
|
|
||||||
@builds("if")
|
@builds("if*")
|
||||||
@checkargs(min=2, max=3)
|
@checkargs(min=2, max=3)
|
||||||
def compile_if(self, expression):
|
def compile_if(self, expression):
|
||||||
expression.pop(0)
|
expression.pop(0)
|
||||||
cond = self.compile(expression.pop(0))
|
cond = self.compile(expression.pop(0))
|
||||||
|
|
||||||
body = self.compile(expression.pop(0))
|
body = self.compile(expression.pop(0))
|
||||||
|
|
||||||
orel = Result()
|
orel = Result()
|
||||||
|
nested = root = False
|
||||||
if expression:
|
if expression:
|
||||||
orel = self.compile(expression.pop(0))
|
orel_expr = expression.pop(0)
|
||||||
|
if isinstance(orel_expr, HyExpression) and isinstance(orel_expr[0],
|
||||||
|
HySymbol) and orel_expr[0] == 'if*':
|
||||||
|
# Nested ifs: don't waste temporaries
|
||||||
|
root = self.temp_if is None
|
||||||
|
nested = True
|
||||||
|
self.temp_if = self.temp_if or self.get_anon_var()
|
||||||
|
orel = self.compile(orel_expr)
|
||||||
|
|
||||||
|
if not cond.stmts and isinstance(cond.force_expr, ast.Name):
|
||||||
|
name = cond.force_expr.id
|
||||||
|
branch = None
|
||||||
|
if name == 'True':
|
||||||
|
branch = body
|
||||||
|
elif name in ('False', 'None'):
|
||||||
|
branch = orel
|
||||||
|
if branch is not None:
|
||||||
|
if self.temp_if and branch.stmts:
|
||||||
|
name = ast.Name(id=ast_str(self.temp_if),
|
||||||
|
arg=ast_str(self.temp_if),
|
||||||
|
ctx=ast.Store(),
|
||||||
|
lineno=expression.start_line,
|
||||||
|
col_offset=expression.start_column)
|
||||||
|
|
||||||
|
branch += ast.Assign(targets=[name],
|
||||||
|
value=body.force_expr,
|
||||||
|
lineno=expression.start_line,
|
||||||
|
col_offset=expression.start_column)
|
||||||
|
|
||||||
|
return branch
|
||||||
|
|
||||||
# We want to hoist the statements from the condition
|
# We want to hoist the statements from the condition
|
||||||
ret = cond
|
ret = cond
|
||||||
@ -1015,7 +1049,7 @@ class HyASTCompiler(object):
|
|||||||
if body.stmts or orel.stmts:
|
if body.stmts or orel.stmts:
|
||||||
# We have statements in our bodies
|
# We have statements in our bodies
|
||||||
# Get a temporary variable for the result storage
|
# Get a temporary variable for the result storage
|
||||||
var = self.get_anon_var()
|
var = self.temp_if or self.get_anon_var()
|
||||||
name = ast.Name(id=ast_str(var), arg=ast_str(var),
|
name = ast.Name(id=ast_str(var), arg=ast_str(var),
|
||||||
ctx=ast.Store(),
|
ctx=ast.Store(),
|
||||||
lineno=expression.start_line,
|
lineno=expression.start_line,
|
||||||
@ -1028,10 +1062,12 @@ class HyASTCompiler(object):
|
|||||||
col_offset=expression.start_column)
|
col_offset=expression.start_column)
|
||||||
|
|
||||||
# and of the else clause
|
# and of the else clause
|
||||||
orel += ast.Assign(targets=[name],
|
if not nested or not orel.stmts or (not root and
|
||||||
value=orel.force_expr,
|
var != self.temp_if):
|
||||||
lineno=expression.start_line,
|
orel += ast.Assign(targets=[name],
|
||||||
col_offset=expression.start_column)
|
value=orel.force_expr,
|
||||||
|
lineno=expression.start_line,
|
||||||
|
col_offset=expression.start_column)
|
||||||
|
|
||||||
# Then build the if
|
# Then build the if
|
||||||
ret += ast.If(test=ret.force_expr,
|
ret += ast.If(test=ret.force_expr,
|
||||||
@ -1054,6 +1090,10 @@ class HyASTCompiler(object):
|
|||||||
orelse=orel.force_expr,
|
orelse=orel.force_expr,
|
||||||
lineno=expression.start_line,
|
lineno=expression.start_line,
|
||||||
col_offset=expression.start_column)
|
col_offset=expression.start_column)
|
||||||
|
|
||||||
|
if root:
|
||||||
|
self.temp_if = None
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
@builds("break")
|
@builds("break")
|
||||||
@ -1306,11 +1346,13 @@ class HyASTCompiler(object):
|
|||||||
col_offset=root.start_column)
|
col_offset=root.start_column)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
ld_targets, ret, _ = self._compile_collect(expr)
|
|
||||||
|
|
||||||
del_targets = []
|
del_targets = []
|
||||||
for target in ld_targets:
|
ret = Result()
|
||||||
del_targets.append(self._storeize(target, ast.Del))
|
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(
|
return ret + ast.Delete(
|
||||||
lineno=expr.start_line,
|
lineno=expr.start_line,
|
||||||
@ -1399,7 +1441,7 @@ class HyASTCompiler(object):
|
|||||||
|
|
||||||
thing = None
|
thing = None
|
||||||
if args != []:
|
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)
|
body = self._compile_branch(expr)
|
||||||
|
|
||||||
@ -1461,7 +1503,7 @@ class HyASTCompiler(object):
|
|||||||
gen = []
|
gen = []
|
||||||
for target, iterable in paired_gens:
|
for target, iterable in paired_gens:
|
||||||
comp_target = self.compile(target)
|
comp_target = self.compile(target)
|
||||||
target = self._storeize(comp_target)
|
target = self._storeize(target, comp_target)
|
||||||
gen_res += self.compile(iterable)
|
gen_res += self.compile(iterable)
|
||||||
gen.append(ast.comprehension(
|
gen.append(ast.comprehension(
|
||||||
target=target,
|
target=target,
|
||||||
@ -1774,18 +1816,7 @@ class HyASTCompiler(object):
|
|||||||
values=[value.force_expr for value in values])
|
values=[value.force_expr for value in values])
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
@builds("=")
|
def _compile_compare_op_expression(self, expression):
|
||||||
@builds("!=")
|
|
||||||
@builds("<")
|
|
||||||
@builds("<=")
|
|
||||||
@builds(">")
|
|
||||||
@builds(">=")
|
|
||||||
@builds("is")
|
|
||||||
@builds("in")
|
|
||||||
@builds("is_not")
|
|
||||||
@builds("not_in")
|
|
||||||
@checkargs(min=2)
|
|
||||||
def compile_compare_op_expression(self, expression):
|
|
||||||
ops = {"=": ast.Eq, "!=": ast.NotEq,
|
ops = {"=": ast.Eq, "!=": ast.NotEq,
|
||||||
"<": ast.Lt, "<=": ast.LtE,
|
"<": ast.Lt, "<=": ast.LtE,
|
||||||
">": ast.Gt, ">=": ast.GtE,
|
">": ast.Gt, ">=": ast.GtE,
|
||||||
@ -1805,6 +1836,32 @@ class HyASTCompiler(object):
|
|||||||
lineno=e.start_line,
|
lineno=e.start_line,
|
||||||
col_offset=e.start_column)
|
col_offset=e.start_column)
|
||||||
|
|
||||||
|
@builds("=")
|
||||||
|
@builds("!=")
|
||||||
|
@builds("<")
|
||||||
|
@builds("<=")
|
||||||
|
@builds(">")
|
||||||
|
@builds(">=")
|
||||||
|
@checkargs(min=1)
|
||||||
|
def compile_compare_op_expression(self, expression):
|
||||||
|
if len(expression) == 2:
|
||||||
|
rval = "True"
|
||||||
|
if expression[0] == "!=":
|
||||||
|
rval = "False"
|
||||||
|
return ast.Name(id=rval,
|
||||||
|
ctx=ast.Load(),
|
||||||
|
lineno=expression.start_line,
|
||||||
|
col_offset=expression.start_column)
|
||||||
|
return self._compile_compare_op_expression(expression)
|
||||||
|
|
||||||
|
@builds("is")
|
||||||
|
@builds("in")
|
||||||
|
@builds("is_not")
|
||||||
|
@builds("not_in")
|
||||||
|
@checkargs(min=2)
|
||||||
|
def compile_compare_op_expression_coll(self, expression):
|
||||||
|
return self._compile_compare_op_expression(expression)
|
||||||
|
|
||||||
@builds("%")
|
@builds("%")
|
||||||
@builds("**")
|
@builds("**")
|
||||||
@builds("<<")
|
@builds("<<")
|
||||||
@ -1911,7 +1968,7 @@ class HyASTCompiler(object):
|
|||||||
|
|
||||||
op = ops[expression[0]]
|
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 = self.compile(expression[2])
|
||||||
|
|
||||||
ret += ast.AugAssign(
|
ret += ast.AugAssign(
|
||||||
@ -2047,7 +2104,7 @@ class HyASTCompiler(object):
|
|||||||
and '.' not in name:
|
and '.' not in name:
|
||||||
result.rename(name)
|
result.rename(name)
|
||||||
else:
|
else:
|
||||||
st_name = self._storeize(ld_name)
|
st_name = self._storeize(name, ld_name)
|
||||||
result += ast.Assign(
|
result += ast.Assign(
|
||||||
lineno=start_line,
|
lineno=start_line,
|
||||||
col_offset=start_column,
|
col_offset=start_column,
|
||||||
@ -2075,7 +2132,7 @@ class HyASTCompiler(object):
|
|||||||
raise HyTypeError(expression,
|
raise HyTypeError(expression,
|
||||||
"for requires two forms in the list")
|
"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()
|
ret = Result()
|
||||||
|
|
||||||
|
@ -25,12 +25,20 @@
|
|||||||
;;; These macros are the essential hy macros.
|
;;; These macros are the essential hy macros.
|
||||||
;;; They are automatically required everywhere, even inside hy.core modules.
|
;;; They are automatically required everywhere, even inside hy.core modules.
|
||||||
|
|
||||||
|
(defmacro if [&rest args]
|
||||||
|
"if with elif"
|
||||||
|
(setv n (len args))
|
||||||
|
(if* n
|
||||||
|
(if* (= n 1)
|
||||||
|
(get args 0)
|
||||||
|
`(if* ~(get args 0)
|
||||||
|
~(get args 1)
|
||||||
|
(if ~@(cut args 2))))))
|
||||||
|
|
||||||
(defmacro macro-error [location reason]
|
(defmacro macro-error [location reason]
|
||||||
"error out properly within a macro"
|
"error out properly within a macro"
|
||||||
`(raise (hy.errors.HyMacroExpansionError ~location ~reason)))
|
`(raise (hy.errors.HyMacroExpansionError ~location ~reason)))
|
||||||
|
|
||||||
|
|
||||||
(defmacro defn [name lambda-list &rest body]
|
(defmacro defn [name lambda-list &rest body]
|
||||||
"define a function `name` with signature `lambda-list` and body `body`"
|
"define a function `name` with signature `lambda-list` and body `body`"
|
||||||
(if (not (= (type name) HySymbol))
|
(if (not (= (type name) HySymbol))
|
||||||
@ -39,7 +47,6 @@
|
|||||||
(macro-error name "defn takes a parameter list as second argument"))
|
(macro-error name "defn takes a parameter list as second argument"))
|
||||||
`(setv ~name (fn ~lambda-list ~@body)))
|
`(setv ~name (fn ~lambda-list ~@body)))
|
||||||
|
|
||||||
|
|
||||||
(defmacro let [variables &rest body]
|
(defmacro let [variables &rest body]
|
||||||
"Execute `body` in the lexical context of `variables`"
|
"Execute `body` in the lexical context of `variables`"
|
||||||
(if (not (isinstance variables HyList))
|
(if (not (isinstance variables HyList))
|
||||||
|
@ -120,7 +120,10 @@
|
|||||||
|
|
||||||
|
|
||||||
(defmacro -> [head &rest rest]
|
(defmacro -> [head &rest rest]
|
||||||
;; TODO: fix the docstring by someone who understands this
|
"Threads the head through the rest of the forms. Inserts
|
||||||
|
head as the second item in the first form of rest. If
|
||||||
|
there are more forms, inserts the first form as the
|
||||||
|
second item in the second form of rest, etc."
|
||||||
(setv ret head)
|
(setv ret head)
|
||||||
(for* [node rest]
|
(for* [node rest]
|
||||||
(if (not (isinstance node HyExpression))
|
(if (not (isinstance node HyExpression))
|
||||||
@ -143,7 +146,10 @@
|
|||||||
~f))
|
~f))
|
||||||
|
|
||||||
(defmacro ->> [head &rest rest]
|
(defmacro ->> [head &rest rest]
|
||||||
;; TODO: fix the docstring by someone who understands this
|
"Threads the head through the rest of the forms. Inserts
|
||||||
|
head as the last item in the first form of rest. If there
|
||||||
|
are more forms, inserts the first form as the last item
|
||||||
|
in the second form of rest, etc."
|
||||||
(setv ret head)
|
(setv ret head)
|
||||||
(for* [node rest]
|
(for* [node rest]
|
||||||
(if (not (isinstance node HyExpression))
|
(if (not (isinstance node HyExpression))
|
||||||
@ -153,20 +159,25 @@
|
|||||||
ret)
|
ret)
|
||||||
|
|
||||||
|
|
||||||
(defmacro if-not [test not-branch &optional [yes-branch nil]]
|
(defmacro if-not [test not-branch &optional yes-branch]
|
||||||
"Like `if`, but execute the first branch when the test fails"
|
"Like `if`, but execute the first branch when the test fails"
|
||||||
(if (nil? yes-branch)
|
`(if* (not ~test) ~not-branch ~yes-branch))
|
||||||
`(if (not ~test) ~not-branch)
|
|
||||||
`(if (not ~test) ~not-branch ~yes-branch)))
|
|
||||||
|
|
||||||
|
|
||||||
(defmacro lif [test &rest branches]
|
(defmacro lif [&rest args]
|
||||||
"Like `if`, but anything that is not None/nil is considered true."
|
"Like `if`, but anything that is not None/nil is considered true."
|
||||||
`(if (is-not ~test nil) ~@branches))
|
(setv n (len args))
|
||||||
|
(if* n
|
||||||
|
(if* (= n 1)
|
||||||
|
(get args 0)
|
||||||
|
`(if* (is-not ~(get args 0) nil)
|
||||||
|
~(get args 1)
|
||||||
|
(lif ~@(cut args 2))))))
|
||||||
|
|
||||||
(defmacro lif-not [test &rest branches]
|
|
||||||
|
(defmacro lif-not [test not-branch &optional yes-branch]
|
||||||
"Like `if-not`, but anything that is not None/nil is considered true."
|
"Like `if-not`, but anything that is not None/nil is considered true."
|
||||||
`(if (is ~test nil) ~@branches))
|
`(if* (is ~test nil) ~not-branch ~yes-branch))
|
||||||
|
|
||||||
|
|
||||||
(defmacro when [test &rest body]
|
(defmacro when [test &rest body]
|
||||||
|
@ -73,15 +73,15 @@ def test_ast_bad_type():
|
|||||||
|
|
||||||
|
|
||||||
def test_ast_bad_if():
|
def test_ast_bad_if():
|
||||||
"Make sure AST can't compile invalid if"
|
"Make sure AST can't compile invalid if*"
|
||||||
cant_compile("(if)")
|
cant_compile("(if*)")
|
||||||
cant_compile("(if foobar)")
|
cant_compile("(if* foobar)")
|
||||||
cant_compile("(if 1 2 3 4 5)")
|
cant_compile("(if* 1 2 3 4 5)")
|
||||||
|
|
||||||
|
|
||||||
def test_ast_valid_if():
|
def test_ast_valid_if():
|
||||||
"Make sure AST can't compile invalid if"
|
"Make sure AST can compile valid if*"
|
||||||
can_compile("(if foo bar)")
|
can_compile("(if* foo bar)")
|
||||||
|
|
||||||
|
|
||||||
def test_ast_valid_unary_op():
|
def test_ast_valid_unary_op():
|
||||||
@ -468,9 +468,9 @@ def test_ast_unicode_strings():
|
|||||||
def test_compile_error():
|
def test_compile_error():
|
||||||
"""Ensure we get compile error in tricky cases"""
|
"""Ensure we get compile error in tricky cases"""
|
||||||
try:
|
try:
|
||||||
can_compile("(fn [] (= 1))")
|
can_compile("(fn [] (in [1 2 3]))")
|
||||||
except HyTypeError as e:
|
except HyTypeError as e:
|
||||||
assert(e.message == "`=' needs at least 2 arguments, got 1.")
|
assert(e.message == "`in' needs at least 2 arguments, got 1.")
|
||||||
else:
|
else:
|
||||||
assert(False)
|
assert(False)
|
||||||
|
|
||||||
@ -539,13 +539,13 @@ def test_invalid_list_comprehension():
|
|||||||
|
|
||||||
def test_bad_setv():
|
def test_bad_setv():
|
||||||
"""Ensure setv handles error cases"""
|
"""Ensure setv handles error cases"""
|
||||||
cant_compile("(setv if 1)")
|
cant_compile("(setv if* 1)")
|
||||||
cant_compile("(setv (a b) [1 2])")
|
cant_compile("(setv (a b) [1 2])")
|
||||||
|
|
||||||
|
|
||||||
def test_defn():
|
def test_defn():
|
||||||
"""Ensure that defn works correctly in various corner cases"""
|
"""Ensure that defn works correctly in various corner cases"""
|
||||||
cant_compile("(defn if [] 1)")
|
cant_compile("(defn if* [] 1)")
|
||||||
cant_compile("(defn \"hy\" [] 1)")
|
cant_compile("(defn \"hy\" [] 1)")
|
||||||
cant_compile("(defn :hy [] 1)")
|
cant_compile("(defn :hy [] 1)")
|
||||||
can_compile("(defn &hy [] 1)")
|
can_compile("(defn &hy [] 1)")
|
||||||
@ -561,5 +561,5 @@ def test_setv_builtins():
|
|||||||
(defn get [self] 42)
|
(defn get [self] 42)
|
||||||
(defclass B []
|
(defclass B []
|
||||||
(defn get [self] 42))
|
(defn get [self] 42))
|
||||||
(defn if [self] 0))
|
(defn if* [self] 0))
|
||||||
""")
|
""")
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
(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 isfile]]
|
||||||
[sys :as systest]
|
[sys :as systest]
|
||||||
[operator [or_]])
|
[operator [or_]]
|
||||||
|
[hy.errors [HyTypeError]])
|
||||||
(import sys)
|
(import sys)
|
||||||
|
|
||||||
(import [hy._compat [PY33 PY34 PY35]])
|
(import [hy._compat [PY33 PY34 PY35]])
|
||||||
@ -60,6 +61,7 @@
|
|||||||
(setv (get foo 0) 12)
|
(setv (get foo 0) 12)
|
||||||
(assert (= (get foo 0) 12)))
|
(assert (= (get foo 0) 12)))
|
||||||
|
|
||||||
|
|
||||||
(defn test-setv-builtin []
|
(defn test-setv-builtin []
|
||||||
"NATIVE: test that setv doesn't work on builtins"
|
"NATIVE: test that setv doesn't work on builtins"
|
||||||
(try (eval '(setv False 1))
|
(try (eval '(setv False 1))
|
||||||
@ -93,6 +95,37 @@
|
|||||||
(except [e [TypeError]] (assert (in "`setv' needs an even number of arguments" (str e))))))
|
(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 []
|
(defn test-fn-corner-cases []
|
||||||
"NATIVE: tests that fn/defn handles corner cases gracefully"
|
"NATIVE: tests that fn/defn handles corner cases gracefully"
|
||||||
(try (eval '(fn "foo"))
|
(try (eval '(fn "foo"))
|
||||||
@ -208,15 +241,26 @@
|
|||||||
|
|
||||||
(defn test-noteq []
|
(defn test-noteq []
|
||||||
"NATIVE: not eq"
|
"NATIVE: not eq"
|
||||||
(assert (!= 2 3)))
|
(assert (!= 2 3))
|
||||||
|
(assert (not (!= 1))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn test-eq []
|
||||||
|
"NATIVE: eq"
|
||||||
|
(assert (= 1 1))
|
||||||
|
(assert (= 1)))
|
||||||
|
|
||||||
|
|
||||||
(defn test-numops []
|
(defn test-numops []
|
||||||
"NATIVE: test numpos"
|
"NATIVE: test numpos"
|
||||||
(assert (> 5 4 3 2 1))
|
(assert (> 5 4 3 2 1))
|
||||||
|
(assert (> 1))
|
||||||
(assert (< 1 2 3 4 5))
|
(assert (< 1 2 3 4 5))
|
||||||
|
(assert (< 1))
|
||||||
(assert (<= 5 5 5 5 ))
|
(assert (<= 5 5 5 5 ))
|
||||||
(assert (>= 5 5 5 5 )))
|
(assert (<= 1))
|
||||||
|
(assert (>= 5 5 5 5 ))
|
||||||
|
(assert (>= 1)))
|
||||||
|
|
||||||
|
|
||||||
(defn test-is []
|
(defn test-is []
|
||||||
@ -263,6 +307,32 @@
|
|||||||
(assert (= (cond) nil)))
|
(assert (= (cond) nil)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn test-if []
|
||||||
|
"NATIVE: test if if works."
|
||||||
|
;; with an odd number of args, the last argument is the default case
|
||||||
|
(assert (= 1 (if 1)))
|
||||||
|
(assert (= 1 (if 0 -1
|
||||||
|
1)))
|
||||||
|
;; with an even number of args, the default is nil
|
||||||
|
(assert (is nil (if)))
|
||||||
|
(assert (is nil (if 0 1)))
|
||||||
|
;; test deeper nesting
|
||||||
|
(assert (= 42
|
||||||
|
(if 0 0
|
||||||
|
nil 1
|
||||||
|
"" 2
|
||||||
|
1 42
|
||||||
|
1 43)))
|
||||||
|
;; test shortcutting
|
||||||
|
(setv x nil)
|
||||||
|
(if 0 (setv x 0)
|
||||||
|
"" (setv x "")
|
||||||
|
42 (setv x 42)
|
||||||
|
43 (setv x 43)
|
||||||
|
(setv x "default"))
|
||||||
|
(assert (= x 42)))
|
||||||
|
|
||||||
|
|
||||||
(defn test-index []
|
(defn test-index []
|
||||||
"NATIVE: Test that dict access works"
|
"NATIVE: Test that dict access works"
|
||||||
(assert (= (get {"one" "two"} "one") "two"))
|
(assert (= (get {"one" "two"} "one") "two"))
|
||||||
@ -928,7 +998,6 @@
|
|||||||
|
|
||||||
(defn test-eval-failure []
|
(defn test-eval-failure []
|
||||||
"NATIVE: test eval failure modes"
|
"NATIVE: test eval failure modes"
|
||||||
(import [hy.errors [HyTypeError]])
|
|
||||||
; yo dawg
|
; yo dawg
|
||||||
(try (eval '(eval)) (except [e HyTypeError]) (else (assert False)))
|
(try (eval '(eval)) (except [e HyTypeError]) (else (assert False)))
|
||||||
(try (eval '(eval "snafu")) (except [e HyTypeError]) (else (assert False)))
|
(try (eval '(eval "snafu")) (except [e HyTypeError]) (else (assert False)))
|
||||||
|
@ -206,11 +206,11 @@
|
|||||||
|
|
||||||
(defn test-lif []
|
(defn test-lif []
|
||||||
"test that lif works as expected"
|
"test that lif works as expected"
|
||||||
; nil is false
|
;; nil is false
|
||||||
(assert (= (lif None "true" "false") "false"))
|
(assert (= (lif None "true" "false") "false"))
|
||||||
(assert (= (lif nil "true" "false") "false"))
|
(assert (= (lif nil "true" "false") "false"))
|
||||||
|
|
||||||
; But everything else is True! Even falsey things.
|
;; But everything else is True! Even falsey things.
|
||||||
(assert (= (lif True "true" "false") "true"))
|
(assert (= (lif True "true" "false") "true"))
|
||||||
(assert (= (lif False "true" "false") "true"))
|
(assert (= (lif False "true" "false") "true"))
|
||||||
(assert (= (lif 0 "true" "false") "true"))
|
(assert (= (lif 0 "true" "false") "true"))
|
||||||
@ -218,7 +218,14 @@
|
|||||||
(assert (= (lif "" "true" "false") "true"))
|
(assert (= (lif "" "true" "false") "true"))
|
||||||
(assert (= (lif (+ 1 2 3) "true" "false") "true"))
|
(assert (= (lif (+ 1 2 3) "true" "false") "true"))
|
||||||
(assert (= (lif nil "true" "false") "false"))
|
(assert (= (lif nil "true" "false") "false"))
|
||||||
(assert (= (lif 0 "true" "false") "true")))
|
(assert (= (lif 0 "true" "false") "true"))
|
||||||
|
|
||||||
|
;; Test ellif [sic]
|
||||||
|
(assert (= (lif nil 0
|
||||||
|
nil 1
|
||||||
|
0 2
|
||||||
|
3)
|
||||||
|
2)))
|
||||||
|
|
||||||
(defn test-lif-not []
|
(defn test-lif-not []
|
||||||
"test that lif-not works as expected"
|
"test that lif-not works as expected"
|
||||||
|
Loading…
Reference in New Issue
Block a user