Introduce fn/a and defn/a

Closes #1054
This commit is contained in:
Simon Gomizelj 2017-12-29 16:40:19 -05:00
parent 0b2a472d2f
commit e3e01d4405
3 changed files with 47 additions and 7 deletions

View File

@ -250,6 +250,8 @@ class Result(object):
var.arg = new_name
elif isinstance(var, ast.FunctionDef):
var.name = new_name
elif PY35 and isinstance(var, ast.AsyncFunctionDef):
var.name = new_name
else:
raise TypeError("Don't know how to rename a %s!" % (
var.__class__.__name__))
@ -1139,6 +1141,12 @@ class HyASTCompiler(object):
node = asty.Yield if expr[0] == "yield" else asty.YieldFrom
return ret + node(expr, value=ret.force_expr)
@builds("await", iff=PY35)
@checkargs(1)
def compile_await_expression(self, expr):
ret = Result() + self.compile(expr[1])
return ret + asty.Await(expr, value=ret.force_expr)
@builds("import")
def compile_import_expression(self, expr):
expr = copy.deepcopy(expr)
@ -1890,12 +1898,15 @@ class HyASTCompiler(object):
return ret
@builds("fn", "fn*")
@builds("fn/a", iff=PY35)
# The starred version is for internal use (particularly, in the
# definition of `defn`). It ensures that a FunctionDef is
# produced rather than a Lambda.
@checkargs(min=1)
def compile_function_def(self, expression):
force_functiondef = expression.pop(0) == "fn*"
root = expression.pop(0)
force_functiondef = root in ("fn*", "fn/a")
asyncdef = root == "fn/a"
arglist = expression.pop(0)
docstring = None
@ -1904,7 +1915,7 @@ class HyASTCompiler(object):
if not isinstance(arglist, HyList):
raise HyTypeError(expression,
"First argument to `fn' must be a list")
"First argument to `{}' must be a list".format(root))
(ret, args, defaults, stararg,
kwonlyargs, kwonlydefaults, kwargs) = self._parse_lambda_list(arglist)
@ -1980,11 +1991,12 @@ class HyASTCompiler(object):
name = self.get_anon_var()
ret += asty.FunctionDef(expression,
name=name,
args=args,
body=body.stmts,
decorator_list=[])
node = asty.AsyncFunctionDef if asyncdef else asty.FunctionDef
ret += node(expression,
name=name,
args=args,
body=body.stmts,
decorator_list=[])
ast_name = asty.Name(expression, id=name, ctx=ast.Load())

View File

@ -70,6 +70,15 @@
(macro-error name "defn takes a parameter list as second argument"))
`(setv ~name (fn* ~lambda-list ~@body)))
(defmacro defn/a [name lambda-list &rest body]
"Define `name` as a function with `lambda-list` signature and body `body`."
(import hy)
(if (not (= (type name) hy.HySymbol))
(macro-error name "defn/a takes a name as first argument"))
(if (not (isinstance lambda-list hy.HyList))
(macro-error name "defn/a takes a parameter list as second argument"))
`(setv ~name (fn/a ~lambda-list ~@body)))
(defmacro if-python2 [python2-form python3-form]
"If running on python2, execute python2-form, else, execute python3-form"
(import sys)

View File

@ -5,6 +5,8 @@
;; Tests where the emitted code relies on Python ≥3.5.
;; conftest.py skips this file when running on Python <3.5.
(import [asyncio [get-event-loop sleep]])
(defn test-unpacking-pep448-1star []
(setv l [1 2 3])
@ -24,3 +26,20 @@
(assert (= {1 "x" #**d1 #**d2 2 "y"} {"a" 1 "b" 2 "c" 3 "d" 4 1 "x" 2 "y"}))
(defn fun [&optional a b c d e f] [a b c d e f])
(assert (= (fun #**d1 :e "eee" #**d2) [1 2 3 4 "eee" None])))
(defn run-coroutine [coro]
"Run a coroutine until its done in the default event loop."""
(.run_until_complete (get-event-loop) (coro)))
(defn test-fn/a []
(assert (= (run-coroutine (fn/a [] (await (sleep 0)) [1 2 3]))
[1 2 3])))
(defn test-defn/a []
(defn/a coro-test []
(await (sleep 0))
[1 2 3])
(assert (= (run-coroutine coro-test) [1 2 3])))