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>
|
||||
* Matthew Egan Odendahl <github.gilch@xoxy.net>
|
||||
* 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)
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
@ -823,26 +823,47 @@ keyword, the second function would have raised a ``NameError``.
|
||||
(set-a 5)
|
||||
(print-a)
|
||||
|
||||
if / if-not
|
||||
-----------
|
||||
if / if* / if-not
|
||||
-----------------
|
||||
|
||||
.. versionadded:: 0.10.0
|
||||
if-not
|
||||
|
||||
``if`` is used to conditionally select code to be executed. It has to contain a
|
||||
condition block and the block to be executed if the condition block evaluates
|
||||
to ``True``. Optionally, it may contain a final block that is executed in case
|
||||
the evaluation of the condition is ``False``.
|
||||
``if / if* / if-not`` respect Python *truthiness*, that is, a *test* fails if it
|
||||
evaluates to a "zero" (including values of ``len`` zero, ``nil``, and
|
||||
``false``), and passes otherwise, but values with a ``__bool__`` method
|
||||
(``__nonzero__`` in Python 2) can overrides this.
|
||||
|
||||
``if-not`` is similar, but the second block will be executed when the condition
|
||||
fails while the third and final block is executed when the test succeeds -- the
|
||||
opposite order of ``if``.
|
||||
The ``if`` macro is for conditionally selecting an expression for evaluation.
|
||||
The result of the selected expression becomes the result of the entire ``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:
|
||||
|
||||
.. 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 and work"))
|
||||
|
||||
@ -850,9 +871,6 @@ Example usage:
|
||||
(print "let's go and work")
|
||||
(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
|
||||
@ -1484,7 +1502,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))
|
||||
@ -1496,7 +1514,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]
|
||||
|
||||
|
||||
|
@ -436,6 +436,26 @@ themselves as an iterator when ``(iter x)`` is called. Contrast with
|
||||
=> (iterator? (iter {:a 1 :b 2 :c 3}))
|
||||
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?
|
||||
@ -536,6 +556,24 @@ calling ``(f val-in-result val-in-latter)``.
|
||||
{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?
|
||||
|
@ -479,8 +479,7 @@ In Hy:
|
||||
"Yet Another Example Class"
|
||||
|
||||
(defn --init-- [self x]
|
||||
(setv self.x x)
|
||||
None)
|
||||
(setv self.x x))
|
||||
|
||||
(defn get-x [self]
|
||||
"Return our copy of x"
|
||||
@ -515,7 +514,7 @@ the program starts executing normally. Very simple example:
|
||||
|
||||
=> (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
|
||||
@ -557,10 +556,21 @@ characters that soon):
|
||||
=> #↻(1 2 3 +)
|
||||
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``,
|
||||
``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
|
||||
=====================
|
||||
|
||||
@ -604,7 +614,7 @@ To use keyword arguments, you can use in ``greetings.py``::
|
||||
(import greetings)
|
||||
(.greet greetings "Foo")
|
||||
(.greet greetings "Foo" "Darth")
|
||||
(apply (. greetings greet) ["Foo"] {"title" "Lord"})
|
||||
(apply (. greetings greet) ["Foo"] {:title "Lord"})
|
||||
|
||||
Which would output::
|
||||
|
||||
|
125
hy/compiler.py
125
hy/compiler.py
@ -377,6 +377,7 @@ class HyASTCompiler(object):
|
||||
self.anon_var_count = 0
|
||||
self.imports = defaultdict(set)
|
||||
self.module_name = module_name
|
||||
self.temp_if = None
|
||||
if not module_name.startswith("hy.core"):
|
||||
# everything in core needs to be explicit.
|
||||
load_stdlib()
|
||||
@ -589,21 +590,22 @@ class HyASTCompiler(object):
|
||||
|
||||
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)
|
||||
@ -612,7 +614,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)
|
||||
@ -952,7 +956,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
|
||||
|
||||
@ -998,16 +1002,46 @@ class HyASTCompiler(object):
|
||||
name=name,
|
||||
body=body)
|
||||
|
||||
@builds("if")
|
||||
@builds("if*")
|
||||
@checkargs(min=2, max=3)
|
||||
def compile_if(self, expression):
|
||||
expression.pop(0)
|
||||
cond = self.compile(expression.pop(0))
|
||||
|
||||
body = self.compile(expression.pop(0))
|
||||
|
||||
orel = Result()
|
||||
nested = root = False
|
||||
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
|
||||
ret = cond
|
||||
@ -1015,7 +1049,7 @@ class HyASTCompiler(object):
|
||||
if body.stmts or orel.stmts:
|
||||
# We have statements in our bodies
|
||||
# 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),
|
||||
ctx=ast.Store(),
|
||||
lineno=expression.start_line,
|
||||
@ -1028,10 +1062,12 @@ class HyASTCompiler(object):
|
||||
col_offset=expression.start_column)
|
||||
|
||||
# and of the else clause
|
||||
orel += ast.Assign(targets=[name],
|
||||
value=orel.force_expr,
|
||||
lineno=expression.start_line,
|
||||
col_offset=expression.start_column)
|
||||
if not nested or not orel.stmts or (not root and
|
||||
var != self.temp_if):
|
||||
orel += ast.Assign(targets=[name],
|
||||
value=orel.force_expr,
|
||||
lineno=expression.start_line,
|
||||
col_offset=expression.start_column)
|
||||
|
||||
# Then build the if
|
||||
ret += ast.If(test=ret.force_expr,
|
||||
@ -1054,6 +1090,10 @@ class HyASTCompiler(object):
|
||||
orelse=orel.force_expr,
|
||||
lineno=expression.start_line,
|
||||
col_offset=expression.start_column)
|
||||
|
||||
if root:
|
||||
self.temp_if = None
|
||||
|
||||
return ret
|
||||
|
||||
@builds("break")
|
||||
@ -1306,11 +1346,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,
|
||||
@ -1399,7 +1441,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)
|
||||
|
||||
@ -1461,7 +1503,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,
|
||||
@ -1774,18 +1816,7 @@ class HyASTCompiler(object):
|
||||
values=[value.force_expr for value in values])
|
||||
return ret
|
||||
|
||||
@builds("=")
|
||||
@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):
|
||||
def _compile_compare_op_expression(self, expression):
|
||||
ops = {"=": ast.Eq, "!=": ast.NotEq,
|
||||
"<": ast.Lt, "<=": ast.LtE,
|
||||
">": ast.Gt, ">=": ast.GtE,
|
||||
@ -1805,6 +1836,32 @@ class HyASTCompiler(object):
|
||||
lineno=e.start_line,
|
||||
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("<<")
|
||||
@ -1911,7 +1968,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(
|
||||
@ -2047,7 +2104,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,
|
||||
@ -2075,7 +2132,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()
|
||||
|
||||
|
@ -25,12 +25,20 @@
|
||||
;;; These macros are the essential hy macros.
|
||||
;;; 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]
|
||||
"error out properly within a macro"
|
||||
`(raise (hy.errors.HyMacroExpansionError ~location ~reason)))
|
||||
|
||||
|
||||
(defmacro defn [name lambda-list &rest body]
|
||||
"define a function `name` with signature `lambda-list` and body `body`"
|
||||
(if (not (= (type name) HySymbol))
|
||||
@ -39,7 +47,6 @@
|
||||
(macro-error name "defn takes a parameter list as second argument"))
|
||||
`(setv ~name (fn ~lambda-list ~@body)))
|
||||
|
||||
|
||||
(defmacro let [variables &rest body]
|
||||
"Execute `body` in the lexical context of `variables`"
|
||||
(if (not (isinstance variables HyList))
|
||||
|
@ -120,7 +120,10 @@
|
||||
|
||||
|
||||
(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)
|
||||
(for* [node rest]
|
||||
(if (not (isinstance node HyExpression))
|
||||
@ -143,7 +146,10 @@
|
||||
~f))
|
||||
|
||||
(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)
|
||||
(for* [node rest]
|
||||
(if (not (isinstance node HyExpression))
|
||||
@ -153,20 +159,25 @@
|
||||
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"
|
||||
(if (nil? yes-branch)
|
||||
`(if (not ~test) ~not-branch)
|
||||
`(if (not ~test) ~not-branch ~yes-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."
|
||||
`(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."
|
||||
`(if (is ~test nil) ~@branches))
|
||||
`(if* (is ~test nil) ~not-branch ~yes-branch))
|
||||
|
||||
|
||||
(defmacro when [test &rest body]
|
||||
|
@ -73,15 +73,15 @@ def test_ast_bad_type():
|
||||
|
||||
|
||||
def test_ast_bad_if():
|
||||
"Make sure AST can't compile invalid if"
|
||||
cant_compile("(if)")
|
||||
cant_compile("(if foobar)")
|
||||
cant_compile("(if 1 2 3 4 5)")
|
||||
"Make sure AST can't compile invalid if*"
|
||||
cant_compile("(if*)")
|
||||
cant_compile("(if* foobar)")
|
||||
cant_compile("(if* 1 2 3 4 5)")
|
||||
|
||||
|
||||
def test_ast_valid_if():
|
||||
"Make sure AST can't compile invalid if"
|
||||
can_compile("(if foo bar)")
|
||||
"Make sure AST can compile valid if*"
|
||||
can_compile("(if* foo bar)")
|
||||
|
||||
|
||||
def test_ast_valid_unary_op():
|
||||
@ -468,9 +468,9 @@ def test_ast_unicode_strings():
|
||||
def test_compile_error():
|
||||
"""Ensure we get compile error in tricky cases"""
|
||||
try:
|
||||
can_compile("(fn [] (= 1))")
|
||||
can_compile("(fn [] (in [1 2 3]))")
|
||||
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:
|
||||
assert(False)
|
||||
|
||||
@ -539,13 +539,13 @@ def test_invalid_list_comprehension():
|
||||
|
||||
def test_bad_setv():
|
||||
"""Ensure setv handles error cases"""
|
||||
cant_compile("(setv if 1)")
|
||||
cant_compile("(setv if* 1)")
|
||||
cant_compile("(setv (a b) [1 2])")
|
||||
|
||||
|
||||
def test_defn():
|
||||
"""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)")
|
||||
can_compile("(defn &hy [] 1)")
|
||||
@ -561,5 +561,5 @@ def test_setv_builtins():
|
||||
(defn get [self] 42)
|
||||
(defclass B []
|
||||
(defn get [self] 42))
|
||||
(defn if [self] 0))
|
||||
(defn if* [self] 0))
|
||||
""")
|
||||
|
@ -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"))
|
||||
@ -208,15 +241,26 @@
|
||||
|
||||
(defn test-noteq []
|
||||
"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 []
|
||||
"NATIVE: test numpos"
|
||||
(assert (> 5 4 3 2 1))
|
||||
(assert (> 1))
|
||||
(assert (< 1 2 3 4 5))
|
||||
(assert (< 1))
|
||||
(assert (<= 5 5 5 5 ))
|
||||
(assert (>= 5 5 5 5 )))
|
||||
(assert (<= 1))
|
||||
(assert (>= 5 5 5 5 ))
|
||||
(assert (>= 1)))
|
||||
|
||||
|
||||
(defn test-is []
|
||||
@ -263,6 +307,32 @@
|
||||
(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 []
|
||||
"NATIVE: Test that dict access works"
|
||||
(assert (= (get {"one" "two"} "one") "two"))
|
||||
@ -928,7 +998,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)))
|
||||
|
@ -206,11 +206,11 @@
|
||||
|
||||
(defn test-lif []
|
||||
"test that lif works as expected"
|
||||
; nil is false
|
||||
;; nil is false
|
||||
(assert (= (lif None "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 False "true" "false") "true"))
|
||||
(assert (= (lif 0 "true" "false") "true"))
|
||||
@ -218,7 +218,14 @@
|
||||
(assert (= (lif "" "true" "false") "true"))
|
||||
(assert (= (lif (+ 1 2 3) "true" "false") "true"))
|
||||
(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 []
|
||||
"test that lif-not works as expected"
|
||||
|
Loading…
Reference in New Issue
Block a user