Allow keyword args in method calls before the obj (#1167)

Unlike Python, Hy allows the programmer to intermingle positional and keyword arguments. This change removes an exception to that rule for method calls, in which the method callee always had to be the first thing after the method. Thus, `(.split :sep "o" "foo")` now compiles to `"foo".split(sep="o")` instead of `HyKeyword("sep").split("o", "foo")`.
This commit is contained in:
Kodi Arfer 2017-01-02 23:24:55 -08:00 committed by gilch
parent 7aaf5f5330
commit ed930edefe
2 changed files with 27 additions and 8 deletions

View File

@ -2068,12 +2068,25 @@ class HyASTCompiler(object):
# Get the object we're calling the method on
# (extracted with the attribute access DSL)
if len(expression) < 2:
raise HyTypeError(expression,
"attribute access requires object")
i = 1
if len(expression) != 2:
# If the expression has only one object,
# always use that as the callee.
# Otherwise, hunt for the first thing that
# isn't a keyword argument or its value.
while i < len(expression):
if isinstance(expression[i], HyKeyword):
# Skip the keyword argument and its value.
i += 1
else:
# Use expression[i].
break
i += 1
else:
raise HyTypeError(expression,
"attribute access requires object")
func = self.compile(HyExpression(
[HySymbol(".").replace(fn), expression.pop(1)] +
[HySymbol(".").replace(fn), expression.pop(i)] +
attrs))
# And get the method

View File

@ -397,26 +397,32 @@
(defclass X [object] [])
(defclass M [object]
[meth (fn [self &rest args]
(.join " " (+ (, "meth") args)))])
[meth (fn [self &rest args &kwargs kwargs]
(.join " " (+ (, "meth") args
(tuple (map (fn [k] (get kwargs k)) (sorted (.keys kwargs)))))))])
(setv x (X))
(setv m (M))
(assert (= (.meth m) "meth"))
(assert (= (.meth m "foo" "bar") "meth foo bar"))
(assert (= (.meth :b "1" :a "2" m "foo" "bar") "meth foo bar 2 1"))
(assert (= (apply .meth [m "foo" "bar"]) "meth foo bar"))
(setv x.p m)
(assert (= (.p.meth x) "meth"))
(assert (= (.p.meth x "foo" "bar") "meth foo bar"))
(assert (= (.p.meth :b "1" :a "2" x "foo" "bar") "meth foo bar 2 1"))
(assert (= (apply .p.meth [x "foo" "bar"]) "meth foo bar"))
(setv x.a (X))
(setv x.a.b m)
(assert (= (.a.b.meth x) "meth"))
(assert (= (.a.b.meth x "foo" "bar") "meth foo bar"))
(assert (= (apply .a.b.meth [x "foo" "bar"]) "meth foo bar")))
(assert (= (.a.b.meth :b "1" :a "2" x "foo" "bar") "meth foo bar 2 1"))
(assert (= (apply .a.b.meth [x "foo" "bar"]) "meth foo bar"))
(assert (is (.isdigit :foo) False)))
(defn test-do []