From 2375392962760c621342106c68208256ec221136 Mon Sep 17 00:00:00 2001 From: Zhao Shenyang Date: Sat, 24 Jan 2015 08:16:38 +0800 Subject: [PATCH] Add 'replace_hy_obj' to safely replace Hy objects Currently '.replace' method is used to replace hy objects. This is not safe when we are not sure if the 'obj' in 'obj.replace(other)' is an instance of HyObject. In these cases, we can use function 'replace_hy_obj(obj, other)' instead. This function will try to wrap 'obj' if it's not an instance of HyObject. This also means that we need a wrapping function in hy.models'. Hence I moved the '_wrap_value' function from hy.macros into hy.models. To avoid circular importing, the wrapper functions are provided individually by each model type's own file. --- hy/importer.py | 4 ++-- hy/macros.py | 39 +++++---------------------------------- hy/models/__init__.py | 32 ++++++++++++++++++++++++++++++++ hy/models/complex.py | 4 +++- hy/models/cons.py | 15 +++++++-------- hy/models/dict.py | 3 +++ hy/models/expression.py | 3 +++ hy/models/float.py | 4 +++- hy/models/integer.py | 9 ++++++++- hy/models/list.py | 7 +++++-- hy/models/string.py | 4 +++- hy/models/symbol.py | 4 ++++ 12 files changed, 78 insertions(+), 50 deletions(-) diff --git a/hy/importer.py b/hy/importer.py index a81d27b..9a7b90c 100644 --- a/hy/importer.py +++ b/hy/importer.py @@ -20,7 +20,7 @@ # DEALINGS IN THE SOFTWARE. from hy.compiler import hy_compile, HyTypeError -from hy.models import HyObject +from hy.models import HyObject, replace_hy_obj from hy.lex import tokenize, LexException from io import open @@ -103,7 +103,7 @@ def hy_eval(hytree, namespace, module_name): foo.end_line = 0 foo.start_column = 0 foo.end_column = 0 - hytree.replace(foo) + replace_hy_obj(hytree, foo) _ast, expr = hy_compile(hytree, module_name, get_expr=True) # Spoof the positions in the generated ast... diff --git a/hy/macros.py b/hy/macros.py index 3d238c0..bcc60f1 100644 --- a/hy/macros.py +++ b/hy/macros.py @@ -18,6 +18,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. +from hy.models import replace_hy_obj, wrap_value from hy.models.expression import HyExpression from hy.models.string import HyString from hy.models.symbol import HySymbol @@ -106,37 +107,6 @@ def require(source_module, target_module): reader_refs[name] = reader -# type -> wrapping function mapping for _wrap_value -_wrappers = { - int: HyInteger, - bool: lambda x: HySymbol("True") if x else HySymbol("False"), - float: HyFloat, - complex: HyComplex, - str_type: HyString, - dict: lambda d: HyDict(_wrap_value(x) for x in sum(d.items(), ())), - list: lambda l: HyList(_wrap_value(x) for x in l), - tuple: lambda t: HyList(_wrap_value(x) for x in t), - type(None): lambda foo: HySymbol("None"), - HyExpression: lambda e: HyExpression(_wrap_value(x) for x in e), -} - -if sys.version_info[0] < 3: # do not add long on python3 - _wrappers[long_type] = HyInteger - - -def _wrap_value(x): - """Wrap `x` into the corresponding Hy type. - - This allows a macro to return an unquoted expression transparently. - - """ - wrapper = _wrappers.get(type(x)) - if wrapper is None: - return x - else: - return wrapper(x) - - def load_macros(module_name): """Load the hy builtin macros for module `module_name`. @@ -194,7 +164,8 @@ def macroexpand_1(tree, module_name): m = _hy_macros[None].get(fn) if m is not None: try: - obj = _wrap_value(m(*ntree[1:])) + obj = wrap_value(m(*ntree[1:])) + except HyTypeError as e: if e.expression is None: e.expression = tree @@ -202,7 +173,7 @@ def macroexpand_1(tree, module_name): except Exception as e: msg = "expanding `" + str(tree[0]) + "': " + repr(e) raise HyMacroExpansionError(tree, msg) - obj.replace(tree) + replace_hy_obj(obj, tree) return obj return ntree @@ -223,4 +194,4 @@ def reader_macroexpand(char, tree, module_name): ) expr = _hy_reader[module_name][char](tree) - return _wrap_value(expr).replace(tree) + return replace_hy_obj(wrap_value(expr), tree) diff --git a/hy/models/__init__.py b/hy/models/__init__.py index 31820f1..a99fe80 100644 --- a/hy/models/__init__.py +++ b/hy/models/__init__.py @@ -35,3 +35,35 @@ class HyObject(object): raise TypeError("Can't replace a non Hy object with a Hy object") return self + +_wrappers = {} + +def wrap_value(x): + """Wrap `x` into the corresponding Hy type. + + This allows replace_hy_obj to convert a non Hy object to a Hy object. + + This also allows a macro to return an unquoted expression transparently. + + """ + + wrapper = _wrappers.get(type(x)) + if wrapper is None: + return x + else: + return wrapper(x) + + +def replace_hy_obj(obj, other): + + if isinstance(obj, HyObject): + return obj.replace(other) + + wrapped_obj = wrap_value(obj) + + if isinstance(wrapped_obj, HyObject): + return wrapped_obj.replace(other) + else: + raise TypeError("Can't replace a Hy object with a non wrappable non Hy object") + + diff --git a/hy/models/complex.py b/hy/models/complex.py index 26ac863..aead797 100644 --- a/hy/models/complex.py +++ b/hy/models/complex.py @@ -18,7 +18,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -from hy.models import HyObject +from hy.models import HyObject, _wrappers class HyComplex(HyObject, complex): @@ -30,3 +30,5 @@ class HyComplex(HyObject, complex): def __new__(cls, number, *args, **kwargs): number = complex(number) return super(HyComplex, cls).__new__(cls, number) + +_wrappers[complex] = HyComplex diff --git a/hy/models/cons.py b/hy/models/cons.py index 180c5cb..6c2fdc0 100644 --- a/hy/models/cons.py +++ b/hy/models/cons.py @@ -18,8 +18,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -from hy.macros import _wrap_value -from hy.models import HyObject +from hy.models import HyObject, replace_hy_obj, wrap_value from hy.models.expression import HyExpression from hy.models.symbol import HySymbol @@ -42,17 +41,17 @@ class HyCons(HyObject): if cdr[0] in ("unquote", "unquote_splice"): return super(HyCons, cls).__new__(cls) - return cdr.__class__([_wrap_value(car)] + cdr) + return cdr.__class__([wrap_value(car)] + cdr) elif cdr is None: - return HyExpression([_wrap_value(car)]) + return HyExpression([wrap_value(car)]) else: return super(HyCons, cls).__new__(cls) def __init__(self, car, cdr): - self.car = _wrap_value(car) - self.cdr = _wrap_value(cdr) + self.car = wrap_value(car) + self.cdr = wrap_value(cdr) def __getitem__(self, n): if n == 0: @@ -88,9 +87,9 @@ class HyCons(HyObject): def replace(self, other): if self.car is not None: - self.car.replace(other) + replace_hy_obj(self.car, other) if self.cdr is not None: - self.cdr.replace(other) + replace_hy_obj(self.cdr, other) HyObject.replace(self, other) diff --git a/hy/models/dict.py b/hy/models/dict.py index e0e99bd..bb02afc 100644 --- a/hy/models/dict.py +++ b/hy/models/dict.py @@ -18,6 +18,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. +from hy.models import _wrappers, wrap_value from hy.models.list import HyList @@ -37,3 +38,5 @@ class HyDict(HyList): def items(self): return list(zip(self.keys(), self.values())) + +_wrappers[dict] = lambda d: HyDict(wrap_value(x) for x in sum(d.items(), ())) diff --git a/hy/models/expression.py b/hy/models/expression.py index 4172670..6e8b041 100644 --- a/hy/models/expression.py +++ b/hy/models/expression.py @@ -18,6 +18,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. +from hy.models import _wrappers, wrap_value from hy.models.list import HyList @@ -28,3 +29,5 @@ class HyExpression(HyList): def __repr__(self): return "(%s)" % (" ".join([repr(x) for x in self])) + +_wrappers[HyExpression] = lambda e: HyExpression(wrap_value(x) for x in e) diff --git a/hy/models/float.py b/hy/models/float.py index ffcf455..1632963 100644 --- a/hy/models/float.py +++ b/hy/models/float.py @@ -18,7 +18,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -from hy.models import HyObject +from hy.models import HyObject, _wrappers class HyFloat(HyObject, float): @@ -30,3 +30,5 @@ class HyFloat(HyObject, float): def __new__(cls, number, *args, **kwargs): number = float(number) return super(HyFloat, cls).__new__(cls, number) + +_wrappers[float] = HyFloat diff --git a/hy/models/integer.py b/hy/models/integer.py index 614520f..4b3bb5f 100644 --- a/hy/models/integer.py +++ b/hy/models/integer.py @@ -18,9 +18,10 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -from hy.models import HyObject +from hy.models import HyObject, _wrappers from hy._compat import long_type +import sys class HyInteger(HyObject, long_type): """ @@ -32,3 +33,9 @@ class HyInteger(HyObject, long_type): def __new__(cls, number, *args, **kwargs): number = long_type(number) return super(HyInteger, cls).__new__(cls, number) + + +_wrappers[int] = HyInteger + +if sys.version_info[0] < 3: # do not add long on python3 + _wrappers[long_type] = HyInteger diff --git a/hy/models/list.py b/hy/models/list.py index e45e40b..54c8a85 100644 --- a/hy/models/list.py +++ b/hy/models/list.py @@ -18,7 +18,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -from hy.models import HyObject +from hy.models import HyObject, replace_hy_obj, _wrappers, wrap_value class HyList(HyObject, list): @@ -28,7 +28,7 @@ class HyList(HyObject, list): def replace(self, other): for x in self: - x.replace(other) + replace_hy_obj(x, other) HyObject.replace(self, other) return self @@ -49,3 +49,6 @@ class HyList(HyObject, list): def __repr__(self): return "[%s]" % (" ".join([repr(x) for x in self])) + +_wrappers[list] = lambda l: HyList(wrap_value(x) for x in l) +_wrappers[tuple] = lambda t: HyList(wrap_value(x) for x in t) diff --git a/hy/models/string.py b/hy/models/string.py index ab7d792..2e406cb 100644 --- a/hy/models/string.py +++ b/hy/models/string.py @@ -18,7 +18,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -from hy.models import HyObject +from hy.models import HyObject, _wrappers from hy._compat import str_type @@ -29,3 +29,5 @@ class HyString(HyObject, str_type): Python version. """ pass + +_wrappers[str_type] = HyString diff --git a/hy/models/symbol.py b/hy/models/symbol.py index 7a1685c..8d767c7 100644 --- a/hy/models/symbol.py +++ b/hy/models/symbol.py @@ -18,6 +18,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. +from hy.models import _wrappers from hy.models.string import HyString @@ -28,3 +29,6 @@ class HySymbol(HyString): def __init__(self, string): self += string + +_wrappers[bool] = lambda x: HySymbol("True") if x else HySymbol("False") +_wrappers[type(None)] = lambda foo: HySymbol("None")