Implement keyword argument passing... (foo-func 1 2 :kw1 "bar") works!
This code is heavily, *heavily* based off of Guillermo Vaya (willyfrog)'s work... instead of defining its own keyword arg though, it uses the "standard" :kwarg type, which is the main difference from willyfrog's original branch. Included tests and some documentation in the tutorial. Also documented "apply" separately as an example of reproducing *args and **kwargs.
This commit is contained in:
parent
96c591ff9d
commit
d98e4fd733
@ -396,15 +396,26 @@ The same thing in Hy::
|
||||
[1 2 None 42]
|
||||
=> (optional_arg 1 2 3 4)
|
||||
[1 2 3 4]
|
||||
=> (apply optional_arg []
|
||||
... {"keyword1" 1
|
||||
... "pos2" 2
|
||||
... "pos1" 3
|
||||
... "keyword2" 4})
|
||||
...
|
||||
=> (optional_arg :keyword1 1
|
||||
... :pos2 2
|
||||
... :pos1 3
|
||||
... :keyword2 4)
|
||||
[3, 2, 1, 4]
|
||||
|
||||
See how we use apply to handle the fancy passing? :)
|
||||
Are you familiar with passing in *args and **kwargs in Python?::
|
||||
|
||||
>>> args = [1 2]
|
||||
>>> kwargs = {"keyword2": 3
|
||||
... "keyword1": 4}
|
||||
>>> optional_arg(*args, **kwargs)
|
||||
|
||||
We can reproduce this with "apply"::
|
||||
|
||||
=> (setv args [1 2])
|
||||
=> (setv kwargs {"keyword2" 3
|
||||
... "keyword1" 4})
|
||||
=> (apply optional_arg args kwargs)
|
||||
[1, 2, 4, 3]
|
||||
|
||||
There's also a dictionary-style keyword arguments construction that
|
||||
looks like:
|
||||
@ -479,7 +490,7 @@ In Hy:
|
||||
.. code-block:: clj
|
||||
|
||||
(defclass Customer [models.Model]
|
||||
[[name (apply models.CharField [] {"max_length" 255})]
|
||||
[[name (models.CharField :max_length 255})]
|
||||
[address (models.TextField)]
|
||||
[notes (models.TextField)]])
|
||||
|
||||
|
@ -433,7 +433,7 @@ class HyASTCompiler(object):
|
||||
|
||||
raise HyCompileError(Exception("Unknown type: `%s'" % _type))
|
||||
|
||||
def _compile_collect(self, exprs):
|
||||
def _compile_collect(self, exprs, with_kwargs=False):
|
||||
"""Collect the expression contexts from a list of compiled expression.
|
||||
|
||||
This returns a list of the expression contexts, and the sum of the
|
||||
@ -442,10 +442,33 @@ class HyASTCompiler(object):
|
||||
"""
|
||||
compiled_exprs = []
|
||||
ret = Result()
|
||||
for expr in exprs:
|
||||
ret += self.compile(expr)
|
||||
compiled_exprs.append(ret.force_expr)
|
||||
return compiled_exprs, ret
|
||||
keywords = []
|
||||
|
||||
exprs_iter = exprs.__iter__()
|
||||
for expr in exprs_iter:
|
||||
if with_kwargs and isinstance(expr, HyKeyword):
|
||||
try:
|
||||
value = next(exprs_iter)
|
||||
except StopIteration:
|
||||
msg = "Keyword argument {kw} needs a value"
|
||||
raise HyCompileError(msg.format(kw=str(expr)))
|
||||
|
||||
compiled_value = self.compile(value)
|
||||
|
||||
# no unicode for py2 in ast names
|
||||
keyword = str(expr[2:])
|
||||
if "-" in keyword and keyword != "-":
|
||||
keyword = keyword.replace("-", "_")
|
||||
|
||||
keywords.append(ast.keyword(arg=keyword,
|
||||
value=compiled_value.force_expr,
|
||||
lineno=expr.start_line,
|
||||
col_offset=expr.start_column))
|
||||
else:
|
||||
ret += self.compile(expr)
|
||||
compiled_exprs.append(ret.force_expr)
|
||||
|
||||
return compiled_exprs, ret, keywords
|
||||
|
||||
def _compile_branch(self, exprs):
|
||||
return _branch(self.compile(expr) for expr in exprs)
|
||||
@ -897,7 +920,7 @@ class HyASTCompiler(object):
|
||||
if isinstance(exceptions_list, list):
|
||||
if len(exceptions_list):
|
||||
# [FooBar BarFoo] → catch Foobar and BarFoo exceptions
|
||||
elts, _type = self._compile_collect(exceptions_list)
|
||||
elts, _type, _ = self._compile_collect(exceptions_list)
|
||||
_type += ast.Tuple(elts=elts,
|
||||
lineno=expr.start_line,
|
||||
col_offset=expr.start_column,
|
||||
@ -1140,7 +1163,7 @@ class HyASTCompiler(object):
|
||||
expr.pop(0) # index
|
||||
|
||||
val = self.compile(expr.pop(0))
|
||||
slices, ret = self._compile_collect(expr)
|
||||
slices, ret, _ = self._compile_collect(expr)
|
||||
|
||||
if val.stmts:
|
||||
ret += val
|
||||
@ -1200,7 +1223,7 @@ class HyASTCompiler(object):
|
||||
@checkargs(min=1)
|
||||
def compile_del_expression(self, expr):
|
||||
expr.pop(0)
|
||||
ld_targets, ret = self._compile_collect(expr)
|
||||
ld_targets, ret, _ = self._compile_collect(expr)
|
||||
|
||||
del_targets = []
|
||||
for target in ld_targets:
|
||||
@ -1271,7 +1294,7 @@ class HyASTCompiler(object):
|
||||
if not fn.stmts or not (isinstance(fn.stmts[-1], ast.FunctionDef) or
|
||||
isinstance(fn.stmts[-1], ast.ClassDef)):
|
||||
raise HyTypeError(expr, "Decorated a non-function")
|
||||
decorators, ret = self._compile_collect(expr)
|
||||
decorators, ret, _ = self._compile_collect(expr)
|
||||
fn.stmts[-1].decorator_list = decorators
|
||||
return ret + fn
|
||||
|
||||
@ -1333,7 +1356,7 @@ class HyASTCompiler(object):
|
||||
@builds(",")
|
||||
def compile_tuple(self, expr):
|
||||
expr.pop(0)
|
||||
elts, ret = self._compile_collect(expr)
|
||||
elts, ret, _ = self._compile_collect(expr)
|
||||
ret += ast.Tuple(elts=elts,
|
||||
lineno=expr.start_line,
|
||||
col_offset=expr.start_column,
|
||||
@ -1562,7 +1585,7 @@ class HyASTCompiler(object):
|
||||
ops = {"and": ast.And,
|
||||
"or": ast.Or}
|
||||
operator = expression.pop(0)
|
||||
values, ret = self._compile_collect(expression)
|
||||
values, ret, _ = self._compile_collect(expression)
|
||||
|
||||
ret += ast.BoolOp(op=ops[operator](),
|
||||
lineno=operator.start_line,
|
||||
@ -1593,7 +1616,7 @@ class HyASTCompiler(object):
|
||||
ops = [op() for x in range(1, len(expression))]
|
||||
|
||||
e = expression[0]
|
||||
exprs, ret = self._compile_collect(expression)
|
||||
exprs, ret, _ = self._compile_collect(expression)
|
||||
|
||||
return ret + ast.Compare(left=exprs[0],
|
||||
ops=ops,
|
||||
@ -1762,11 +1785,20 @@ class HyASTCompiler(object):
|
||||
|
||||
if not func:
|
||||
func = self.compile(fn)
|
||||
args, ret = self._compile_collect(expression[1:])
|
||||
|
||||
# An exception for pulling together keyword args is if we're doing
|
||||
# a typecheck, eg (type :foo)
|
||||
if fn in ("type", "HyKeyword", "keyword", "name", "is_keyword"):
|
||||
with_kwargs = False
|
||||
else:
|
||||
with_kwargs = True
|
||||
|
||||
args, ret, kwargs = self._compile_collect(expression[1:],
|
||||
with_kwargs)
|
||||
|
||||
ret += ast.Call(func=func.expr,
|
||||
args=args,
|
||||
keywords=[],
|
||||
keywords=kwargs,
|
||||
starargs=None,
|
||||
kwargs=None,
|
||||
lineno=expression.start_line,
|
||||
@ -1879,7 +1911,7 @@ class HyASTCompiler(object):
|
||||
|
||||
@builds(HyList)
|
||||
def compile_list(self, expression):
|
||||
elts, ret = self._compile_collect(expression)
|
||||
elts, ret, _ = self._compile_collect(expression)
|
||||
ret += ast.List(elts=elts,
|
||||
ctx=ast.Load(),
|
||||
lineno=expression.start_line,
|
||||
@ -1983,7 +2015,7 @@ class HyASTCompiler(object):
|
||||
if not isinstance(base_list, HyList):
|
||||
raise HyTypeError(expression,
|
||||
"Bases class must be a list")
|
||||
bases_expr, bases = self._compile_collect(base_list)
|
||||
bases_expr, bases, _ = self._compile_collect(base_list)
|
||||
else:
|
||||
bases_expr = []
|
||||
bases = Result()
|
||||
@ -2184,7 +2216,7 @@ class HyASTCompiler(object):
|
||||
|
||||
@builds(HyDict)
|
||||
def compile_dict(self, m):
|
||||
keyvalues, ret = self._compile_collect(m)
|
||||
keyvalues, ret, _ = self._compile_collect(m)
|
||||
|
||||
ret += ast.Dict(lineno=m.start_line,
|
||||
col_offset=m.start_column,
|
||||
|
@ -1133,3 +1133,23 @@
|
||||
(assert (= (name :foo) "foo"))
|
||||
(assert (= (name :foo_bar) "foo-bar"))
|
||||
(assert (= (name test-name-conversion) "test-name-conversion")))
|
||||
|
||||
(defn test-keywords []
|
||||
"Check keyword use in function calls"
|
||||
(assert (= (kwtest) {}))
|
||||
(assert (= (kwtest :key "value") {"key" "value"}))
|
||||
(assert (= (kwtest :key-with-dashes "value") {"key_with_dashes" "value"}))
|
||||
(assert (= (kwtest :result (+ 1 1)) {"result" 2}))
|
||||
(assert (= (kwtest :key (kwtest :key2 "value")) {"key" {"key2" "value"}})))
|
||||
|
||||
(defmacro identify-keywords [&rest elts]
|
||||
`(list
|
||||
(map
|
||||
(lambda (x) (if (is-keyword x) "keyword" "other"))
|
||||
~elts)))
|
||||
|
||||
(defn test-keywords-and-macros []
|
||||
"Macros should still be able to handle keywords as they best see fit."
|
||||
(assert
|
||||
(= (identify-keywords 1 "bloo" :foo)
|
||||
["other" "other" "keyword"])))
|
||||
|
Loading…
Reference in New Issue
Block a user