Introduce for/a* and for/a expressions
This commit is contained in:
parent
783d53ecb7
commit
1e4ad3167b
@ -1,7 +1,7 @@
|
|||||||
import _pytest
|
import _pytest
|
||||||
import hy
|
import hy
|
||||||
import os
|
import os
|
||||||
from hy._compat import PY3, PY35
|
from hy._compat import PY3, PY35, PY36
|
||||||
|
|
||||||
NATIVE_TESTS = os.path.join("", "tests", "native_tests", "")
|
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 NATIVE_TESTS in path.dirname + os.sep
|
||||||
and path.basename != "__init__.hy"
|
and path.basename != "__init__.hy"
|
||||||
and not ("py3_only" in path.basename and not PY3)
|
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)
|
m = _pytest.python.pytest_pycollect_makemodule(path, parent)
|
||||||
# Spoof the module name to avoid hitting an assertion in pytest.
|
# Spoof the module name to avoid hitting an assertion in pytest.
|
||||||
m.name = m.name[:-len(".hy")] + ".py"
|
m.name = m.name[:-len(".hy")] + ".py"
|
||||||
|
@ -22,6 +22,7 @@ import sys
|
|||||||
|
|
||||||
PY3 = sys.version_info[0] >= 3
|
PY3 = sys.version_info[0] >= 3
|
||||||
PY35 = sys.version_info >= (3, 5)
|
PY35 = sys.version_info >= (3, 5)
|
||||||
|
PY36 = sys.version_info >= (3, 6)
|
||||||
|
|
||||||
str_type = str if PY3 else unicode # NOQA
|
str_type = str if PY3 else unicode # NOQA
|
||||||
bytes_type = bytes if PY3 else str # NOQA
|
bytes_type = bytes if PY3 else str # NOQA
|
||||||
|
@ -1830,16 +1830,16 @@ class HyASTCompiler(object):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
@builds("for*")
|
@builds("for*")
|
||||||
|
@builds("for/a*", iff=PY35)
|
||||||
@checkargs(min=1)
|
@checkargs(min=1)
|
||||||
def compile_for_expression(self, expression):
|
def compile_for_expression(self, expression):
|
||||||
expression.pop(0) # for
|
root = expression.pop(0)
|
||||||
|
|
||||||
args = expression.pop(0)
|
args = expression.pop(0)
|
||||||
|
|
||||||
if not isinstance(args, HyList):
|
if not isinstance(args, HyList):
|
||||||
raise HyTypeError(expression,
|
raise HyTypeError(expression,
|
||||||
"`for` expects a list, received `{0}`".format(
|
"`{0}` expects a list, received `{1}`".format(
|
||||||
type(args).__name__))
|
root, type(args).__name__))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
target_name, iterable = args
|
target_name, iterable = args
|
||||||
@ -1864,11 +1864,12 @@ class HyASTCompiler(object):
|
|||||||
body = self._compile_branch(expression)
|
body = self._compile_branch(expression)
|
||||||
body += body.expr_as_stmt()
|
body += body.expr_as_stmt()
|
||||||
|
|
||||||
ret += asty.For(expression,
|
node = asty.For if root == 'for*' else asty.AsyncFor
|
||||||
target=target,
|
ret += node(expression,
|
||||||
iter=ret.force_expr,
|
target=target,
|
||||||
body=body.stmts,
|
iter=ret.force_expr,
|
||||||
orelse=orel.stmts)
|
body=body.stmts,
|
||||||
|
orelse=orel.stmts)
|
||||||
|
|
||||||
ret.contains_yield = body.contains_yield
|
ret.contains_yield = body.contains_yield
|
||||||
|
|
||||||
|
@ -108,11 +108,7 @@ used as the result."
|
|||||||
root)))
|
root)))
|
||||||
|
|
||||||
|
|
||||||
(defmacro for [args &rest body]
|
(defmacro _for [node 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."
|
|
||||||
(setv body (list body))
|
(setv body (list body))
|
||||||
(if (empty? body)
|
(if (empty? body)
|
||||||
(macro-error None "`for' requires a body to evaluate"))
|
(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.")
|
(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? body) (macro-error None "`for' requires a body to evaluate")
|
||||||
(empty? args) `(do ~@body ~@belse)
|
(empty? args) `(do ~@body ~@belse)
|
||||||
(= (len args) 2) `(for* [~@args] (do ~@body) ~@belse)
|
(= (len args) 2) `(~node [~@args] (do ~@body) ~@belse)
|
||||||
(do
|
(do
|
||||||
(setv alist (cut args 0 None 2))
|
(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]
|
(defmacro -> [head &rest rest]
|
||||||
|
39
tests/native_tests/py36_only_tests.hy
Normal file
39
tests/native_tests/py36_only_tests.hy
Normal 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)))))
|
Loading…
x
Reference in New Issue
Block a user