Teach apply about symbol mangling

apply now mangles strings and keywords according to the Hy mangling
rules (by using the same function, now imported from
hy.lex.parser). With this change, if the dict passed to apply has
keywords, strings or quoted symbols, they'll get mangled, to turn them
into proper keys.

This only works for the cases where the keys are directly in the apply
params. A previously deffed dict, or key through a variable will not be
mangled.

This closes #219.

Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
This commit is contained in:
Gergely Nagy 2015-08-10 13:17:40 +02:00
parent b1f497ae72
commit 26f342d580
4 changed files with 44 additions and 20 deletions

View File

@ -115,8 +115,10 @@ it appends it as the last argument. The following code demonstrates this:
apply
-----
``apply`` is used to apply an optional list of arguments and an optional
dictionary of kwargs to a function.
``apply`` is used to apply an optional list of arguments and an
optional dictionary of kwargs to a function. The symbol mangling
transformations will be applied to all keys in the dictionary of
kwargs, provided the dictionary and its keys are defined in-place.
Usage: ``(apply fn-name [args] [kwargs])``
@ -142,6 +144,8 @@ Examples:
(apply total-purchase [] {"price" 10 "amount" 15 "vat" 1.05})
;=> 165.375
(apply total-purchase [] {:price 10 :amount 15 :vat 1.05})
;=> 165.375
and
---

View File

@ -38,6 +38,8 @@ from hy.models.cons import HyCons
from hy.errors import HyCompileError, HyTypeError
from hy.lex.parser import hy_symbol_mangle
import hy.macros
from hy._compat import (
str_type, long_type, PY27, PY33, PY3, PY34, PY35, raise_empty)
@ -1640,7 +1642,21 @@ class HyASTCompiler(object):
ret = stargs + ret
if expr:
kwargs = self.compile(expr.pop(0))
kwargs = expr.pop(0)
if isinstance(kwargs, HyDict):
new_kwargs = []
for k, v in kwargs.items():
if isinstance(k, HySymbol):
pass
elif isinstance(k, HyString):
k = HyString(hy_symbol_mangle(str_type(k))).replace(k)
elif isinstance(k, HyKeyword):
sym = hy_symbol_mangle(str_type(k)[2:])
k = HyString(sym).replace(k)
new_kwargs += [k, v]
kwargs = HyDict(new_kwargs).replace(kwargs)
kwargs = self.compile(kwargs)
if PY35:
kwargs_expr = kwargs.force_expr
ret.expr.keywords.append(

View File

@ -45,6 +45,22 @@ pg = ParserGenerator(
)
def hy_symbol_mangle(p):
if p.startswith("*") and p.endswith("*") and p not in ("*", "**"):
p = p[1:-1].upper()
if "-" in p and p != "-":
p = p.replace("-", "_")
if p.endswith("?") and p != "?":
p = "is_%s" % (p[:-1])
if p.endswith("!") and p != "!":
p = "%s_bang" % (p[:-1])
return p
def set_boundaries(fun):
@wraps(fun)
def wrapped(p):
@ -297,22 +313,7 @@ def t_identifier(p):
if obj.startswith(":"):
return HyKeyword(obj)
def mangle(p):
if p.startswith("*") and p.endswith("*") and p not in ("*", "**"):
p = p[1:-1].upper()
if "-" in p and p != "-":
p = p.replace("-", "_")
if p.endswith("?") and p != "?":
p = "is_%s" % (p[:-1])
if p.endswith("!") and p != "!":
p = "%s_bang" % (p[:-1])
return p
obj = ".".join([mangle(part) for part in obj.split(".")])
obj = ".".join([hy_symbol_mangle(part) for part in obj.split(".")])
return HySymbol(obj)

View File

@ -309,7 +309,10 @@
(assert (= (apply sumit [] {"a" 1 "b" 1 "c" 2}) 4))
(assert (= (apply sumit ((fn [] [1 1])) {"c" 1}) 3))
(defn noargs [] [1 2 3])
(assert (= (apply noargs) [1 2 3])))
(assert (= (apply noargs) [1 2 3]))
(defn sumit-mangle [an-a a-b a-c a-d] (+ an-a a-b a-c a-d))
(def Z "a_d")
(assert (= (apply sumit-mangle [] {"an-a" 1 :a-b 2 'a-c 3 Z 4}) 10)))
(defn test-apply-with-methods []