Introduce for/a* and for/a expressions

This commit is contained in:
Simon Gomizelj 2017-12-30 17:31:06 -05:00
parent 783d53ecb7
commit 1e4ad3167b
5 changed files with 72 additions and 18 deletions

View File

@ -1,7 +1,7 @@
import _pytest
import hy
import os
from hy._compat import PY3, PY35
from hy._compat import PY3, PY35, PY36
NATIVE_TESTS = os.path.join("", "tests", "native_tests", "")
@ -10,7 +10,8 @@ def pytest_collect_file(parent, path):
and NATIVE_TESTS in path.dirname + os.sep
and path.basename != "__init__.hy"
and not ("py3_only" in path.basename and not PY3)
and not ("py35_only" in path.basename and not PY35)):
and not ("py35_only" in path.basename and not PY35)
and not ("py36_only" in path.basename and not PY36)):
m = _pytest.python.pytest_pycollect_makemodule(path, parent)
# Spoof the module name to avoid hitting an assertion in pytest.
m.name = m.name[:-len(".hy")] + ".py"

View File

@ -22,6 +22,7 @@ import sys
PY3 = sys.version_info[0] >= 3
PY35 = sys.version_info >= (3, 5)
PY36 = sys.version_info >= (3, 6)
str_type = str if PY3 else unicode # NOQA
bytes_type = bytes if PY3 else str # NOQA

View File

@ -1830,16 +1830,16 @@ class HyASTCompiler(object):
return result
@builds("for*")
@builds("for/a*", iff=PY35)
@checkargs(min=1)
def compile_for_expression(self, expression):
expression.pop(0) # for
root = expression.pop(0)
args = expression.pop(0)
if not isinstance(args, HyList):
raise HyTypeError(expression,
"`for` expects a list, received `{0}`".format(
type(args).__name__))
"`{0}` expects a list, received `{1}`".format(
root, type(args).__name__))
try:
target_name, iterable = args
@ -1864,11 +1864,12 @@ class HyASTCompiler(object):
body = self._compile_branch(expression)
body += body.expr_as_stmt()
ret += asty.For(expression,
target=target,
iter=ret.force_expr,
body=body.stmts,
orelse=orel.stmts)
node = asty.For if root == 'for*' else asty.AsyncFor
ret += node(expression,
target=target,
iter=ret.force_expr,
body=body.stmts,
orelse=orel.stmts)
ret.contains_yield = body.contains_yield

View File

@ -108,11 +108,7 @@ used as the result."
root)))
(defmacro for [args &rest body]
"Build a for-loop with `args` as a [element coll] bracket pair and run `body`.
Args may contain multiple pairs, in which case it executes a nested for-loop
in order of the given pairs."
(defmacro _for [node args &rest body]
(setv body (list body))
(if (empty? body)
(macro-error None "`for' requires a body to evaluate"))
@ -124,10 +120,26 @@ in order of the given pairs."
(odd? (len args)) (macro-error args "`for' requires an even number of args.")
(empty? body) (macro-error None "`for' requires a body to evaluate")
(empty? args) `(do ~@body ~@belse)
(= (len args) 2) `(for* [~@args] (do ~@body) ~@belse)
(= (len args) 2) `(~node [~@args] (do ~@body) ~@belse)
(do
(setv alist (cut args 0 None 2))
`(for* [(, ~@alist) (genexpr (, ~@alist) [~@args])] (do ~@body) ~@belse))))
`(~node [(, ~@alist) (genexpr (, ~@alist) [~@args])] (do ~@body) ~@belse))))
(defmacro for [args &rest body]
"Build a for-loop with `args` as a [element coll] bracket pair and run `body`.
Args may contain multiple pairs, in which case it executes a nested for-loop
in order of the given pairs."
`(_for for* ~args ~@body))
(defmacro for/a [args &rest body]
"Build a for/a-loop with `args` as a [element coll] bracket pair and run `body`.
Args may contain multiple pairs, in which case it executes a nested for/a-loop
in order of the given pairs."
`(_for for/a* ~args ~@body))
(defmacro -> [head &rest rest]

View File

@ -0,0 +1,39 @@
;; Copyright 2017 the authors.
;; This file is part of Hy, which is free software licensed under the Expat
;; license. See the LICENSE.
;; Tests where the emitted code relies on Python ≥3.6.
;; conftest.py skips this file when running on Python <3.6.
(import [asyncio [get-event-loop sleep]])
(defn run-coroutine [coro]
"Run a coroutine until its done in the default event loop."""
(.run_until_complete (get-event-loop) (coro)))
(defn test-for/a []
(defn/a numbers []
(for [i [1 2]]
(yield i)))
(run-coroutine
(fn/a []
(setv x 0)
(for/a [a (numbers)]
(setv x (+ x a)))
(assert (= x 3)))))
(defn test-for/a-else []
(defn/a numbers []
(for [i [1 2]]
(yield i)))
(run-coroutine
(fn/a []
(setv x 0)
(for/a [a (numbers)]
(setv x (+ x a))
(else (setv x (+ x 50))))
(assert (= x 53)))))