Fix apply with method calls
Apply didn't work on method calls (i.e. `(apply .foo [bar]) broke). This slipped through because there were no tests of this behavior. I noticed it while trying to merge the `meth` fixes.
This commit is contained in:
parent
744cd71171
commit
cfbc792957
@ -1278,26 +1278,77 @@ class HyASTCompiler(object):
|
|||||||
@checkargs(min=1, max=3)
|
@checkargs(min=1, max=3)
|
||||||
def compile_apply_expression(self, expr):
|
def compile_apply_expression(self, expr):
|
||||||
expr.pop(0) # apply
|
expr.pop(0) # apply
|
||||||
call = self.compile(expr.pop(0))
|
|
||||||
call = ast.Call(func=call.expr,
|
ret = Result()
|
||||||
args=[],
|
|
||||||
keywords=[],
|
fun = expr.pop(0)
|
||||||
starargs=None,
|
|
||||||
kwargs=None,
|
# We actually defer the compilation of the function call to
|
||||||
lineno=expr.start_line,
|
# @builds(HyExpression), allowing us to work on method calls
|
||||||
col_offset=expr.start_column)
|
call = HyExpression([fun]).replace(fun)
|
||||||
ret = call
|
|
||||||
|
if isinstance(fun, HySymbol) and fun.startswith("."):
|
||||||
|
# (apply .foo lst) needs to work as lst[0].foo(*lst[1:])
|
||||||
|
if not expr:
|
||||||
|
raise HyTypeError(
|
||||||
|
expr, "apply of a method needs to have an argument"
|
||||||
|
)
|
||||||
|
|
||||||
|
# We need to grab the arguments, and split them.
|
||||||
|
|
||||||
|
# Assign them to a variable if they're not one already
|
||||||
|
if type(expr[0]) == HyList:
|
||||||
|
if len(expr[0]) == 0:
|
||||||
|
raise HyTypeError(
|
||||||
|
expr, "apply of a method needs to have an argument"
|
||||||
|
)
|
||||||
|
call.append(expr[0].pop(0))
|
||||||
|
else:
|
||||||
|
if isinstance(expr[0], HySymbol):
|
||||||
|
tempvar = expr[0]
|
||||||
|
else:
|
||||||
|
tempvar = HySymbol(self.get_anon_var()).replace(expr[0])
|
||||||
|
assignment = HyExpression(
|
||||||
|
[HySymbol("setv"), tempvar, expr[0]]
|
||||||
|
).replace(expr[0])
|
||||||
|
|
||||||
|
# and add the assignment to our result
|
||||||
|
ret += self.compile(assignment)
|
||||||
|
|
||||||
|
# The first argument is the object on which to call the method
|
||||||
|
# So we translate (apply .foo args) to (.foo (get args 0))
|
||||||
|
call.append(HyExpression(
|
||||||
|
[HySymbol("get"), tempvar, HyInteger(0)]
|
||||||
|
).replace(tempvar))
|
||||||
|
|
||||||
|
# We then pass the other arguments to the function
|
||||||
|
expr[0] = HyExpression(
|
||||||
|
[HySymbol("slice"), tempvar, HyInteger(1)]
|
||||||
|
).replace(expr[0])
|
||||||
|
|
||||||
|
ret += self.compile(call)
|
||||||
|
|
||||||
|
if not isinstance(ret.expr, ast.Call):
|
||||||
|
raise HyTypeError(
|
||||||
|
fun, "compiling the application of `{}' didn't return a "
|
||||||
|
"function call, but `{}'".format(fun, type(ret.expr).__name__)
|
||||||
|
)
|
||||||
|
if ret.expr.starargs or ret.expr.kwargs:
|
||||||
|
raise HyTypeError(
|
||||||
|
expr, "compiling the function application returned a function "
|
||||||
|
"call with arguments"
|
||||||
|
)
|
||||||
|
|
||||||
if expr:
|
if expr:
|
||||||
stargs = expr.pop(0)
|
stargs = expr.pop(0)
|
||||||
if stargs is not None:
|
if stargs is not None:
|
||||||
stargs = self.compile(stargs)
|
stargs = self.compile(stargs)
|
||||||
call.starargs = stargs.force_expr
|
ret.expr.starargs = stargs.force_expr
|
||||||
ret = stargs + ret
|
ret = stargs + ret
|
||||||
|
|
||||||
if expr:
|
if expr:
|
||||||
kwargs = self.compile(expr.pop(0))
|
kwargs = self.compile(expr.pop(0))
|
||||||
call.kwargs = kwargs.force_expr
|
ret.expr.kwargs = kwargs.force_expr
|
||||||
ret = kwargs + ret
|
ret = kwargs + ret
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
@ -164,6 +164,7 @@
|
|||||||
{"y" 5 "z" 3})
|
{"y" 5 "z" 3})
|
||||||
{"x" 1 "y" 5 "z" 3 "one" "three"})))
|
{"x" 1 "y" 5 "z" 3 "one" "three"})))
|
||||||
|
|
||||||
|
|
||||||
(defn test-apply []
|
(defn test-apply []
|
||||||
"NATIVE: test working with args and functions"
|
"NATIVE: test working with args and functions"
|
||||||
(defn sumit [a b c] (+ a b c))
|
(defn sumit [a b c] (+ a b c))
|
||||||
@ -174,6 +175,18 @@
|
|||||||
(defn noargs [] [1 2 3])
|
(defn noargs [] [1 2 3])
|
||||||
(assert (= (apply noargs) [1 2 3])))
|
(assert (= (apply noargs) [1 2 3])))
|
||||||
|
|
||||||
|
|
||||||
|
(defn test-apply-with-methods []
|
||||||
|
"NATIVE: test apply to call a method"
|
||||||
|
(setv str "foo {bar}")
|
||||||
|
(assert (= (apply .format [str] {"bar" "baz"})
|
||||||
|
(apply .format ["foo {0}" "baz"])
|
||||||
|
"foo baz"))
|
||||||
|
(setv lst ["a {0} {1} {foo} {bar}" "b" "c"])
|
||||||
|
(assert (= (apply .format lst {"foo" "d" "bar" "e"})
|
||||||
|
"a b c d e")))
|
||||||
|
|
||||||
|
|
||||||
(defn test-dotted []
|
(defn test-dotted []
|
||||||
"NATIVE: test dotted invocation"
|
"NATIVE: test dotted invocation"
|
||||||
(assert (= (.join " " ["one" "two"]) "one two")))
|
(assert (= (.join " " ["one" "two"]) "one two")))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user