Merge branch 'master' of https://github.com/hylang/hy into kwonly_err

Conflicts:
	docs/tutorial.rst
This commit is contained in:
Ryan Gonzalez 2015-11-14 20:40:20 -06:00
commit 7ee7428870
11 changed files with 306 additions and 87 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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