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>
* Matthew Egan Odendahl <github.gilch@xoxy.net>
* 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)
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

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

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}))
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?

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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