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.
This commit is contained in:
parent
8d6f9c3d84
commit
2375392962
@ -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...
|
||||
|
39
hy/macros.py
39
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)
|
||||
|
@ -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")
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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(), ()))
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
Loading…
x
Reference in New Issue
Block a user