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.
|
# DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
from hy.compiler import hy_compile, HyTypeError
|
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 hy.lex import tokenize, LexException
|
||||||
|
|
||||||
from io import open
|
from io import open
|
||||||
@ -103,7 +103,7 @@ def hy_eval(hytree, namespace, module_name):
|
|||||||
foo.end_line = 0
|
foo.end_line = 0
|
||||||
foo.start_column = 0
|
foo.start_column = 0
|
||||||
foo.end_column = 0
|
foo.end_column = 0
|
||||||
hytree.replace(foo)
|
replace_hy_obj(hytree, foo)
|
||||||
_ast, expr = hy_compile(hytree, module_name, get_expr=True)
|
_ast, expr = hy_compile(hytree, module_name, get_expr=True)
|
||||||
|
|
||||||
# Spoof the positions in the generated ast...
|
# 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
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
# DEALINGS IN THE SOFTWARE.
|
# DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
from hy.models import replace_hy_obj, wrap_value
|
||||||
from hy.models.expression import HyExpression
|
from hy.models.expression import HyExpression
|
||||||
from hy.models.string import HyString
|
from hy.models.string import HyString
|
||||||
from hy.models.symbol import HySymbol
|
from hy.models.symbol import HySymbol
|
||||||
@ -106,37 +107,6 @@ def require(source_module, target_module):
|
|||||||
reader_refs[name] = reader
|
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):
|
def load_macros(module_name):
|
||||||
"""Load the hy builtin macros for module `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)
|
m = _hy_macros[None].get(fn)
|
||||||
if m is not None:
|
if m is not None:
|
||||||
try:
|
try:
|
||||||
obj = _wrap_value(m(*ntree[1:]))
|
obj = wrap_value(m(*ntree[1:]))
|
||||||
|
|
||||||
except HyTypeError as e:
|
except HyTypeError as e:
|
||||||
if e.expression is None:
|
if e.expression is None:
|
||||||
e.expression = tree
|
e.expression = tree
|
||||||
@ -202,7 +173,7 @@ def macroexpand_1(tree, module_name):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
msg = "expanding `" + str(tree[0]) + "': " + repr(e)
|
msg = "expanding `" + str(tree[0]) + "': " + repr(e)
|
||||||
raise HyMacroExpansionError(tree, msg)
|
raise HyMacroExpansionError(tree, msg)
|
||||||
obj.replace(tree)
|
replace_hy_obj(obj, tree)
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
return ntree
|
return ntree
|
||||||
@ -223,4 +194,4 @@ def reader_macroexpand(char, tree, module_name):
|
|||||||
)
|
)
|
||||||
|
|
||||||
expr = _hy_reader[module_name][char](tree)
|
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")
|
raise TypeError("Can't replace a non Hy object with a Hy object")
|
||||||
|
|
||||||
return self
|
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
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
# DEALINGS IN THE SOFTWARE.
|
# DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
from hy.models import HyObject
|
from hy.models import HyObject, _wrappers
|
||||||
|
|
||||||
|
|
||||||
class HyComplex(HyObject, complex):
|
class HyComplex(HyObject, complex):
|
||||||
@ -30,3 +30,5 @@ class HyComplex(HyObject, complex):
|
|||||||
def __new__(cls, number, *args, **kwargs):
|
def __new__(cls, number, *args, **kwargs):
|
||||||
number = complex(number)
|
number = complex(number)
|
||||||
return super(HyComplex, cls).__new__(cls, 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
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
# DEALINGS IN THE SOFTWARE.
|
# DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
from hy.macros import _wrap_value
|
from hy.models import HyObject, replace_hy_obj, wrap_value
|
||||||
from hy.models import HyObject
|
|
||||||
from hy.models.expression import HyExpression
|
from hy.models.expression import HyExpression
|
||||||
from hy.models.symbol import HySymbol
|
from hy.models.symbol import HySymbol
|
||||||
|
|
||||||
@ -42,17 +41,17 @@ class HyCons(HyObject):
|
|||||||
if cdr[0] in ("unquote", "unquote_splice"):
|
if cdr[0] in ("unquote", "unquote_splice"):
|
||||||
return super(HyCons, cls).__new__(cls)
|
return super(HyCons, cls).__new__(cls)
|
||||||
|
|
||||||
return cdr.__class__([_wrap_value(car)] + cdr)
|
return cdr.__class__([wrap_value(car)] + cdr)
|
||||||
|
|
||||||
elif cdr is None:
|
elif cdr is None:
|
||||||
return HyExpression([_wrap_value(car)])
|
return HyExpression([wrap_value(car)])
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return super(HyCons, cls).__new__(cls)
|
return super(HyCons, cls).__new__(cls)
|
||||||
|
|
||||||
def __init__(self, car, cdr):
|
def __init__(self, car, cdr):
|
||||||
self.car = _wrap_value(car)
|
self.car = wrap_value(car)
|
||||||
self.cdr = _wrap_value(cdr)
|
self.cdr = wrap_value(cdr)
|
||||||
|
|
||||||
def __getitem__(self, n):
|
def __getitem__(self, n):
|
||||||
if n == 0:
|
if n == 0:
|
||||||
@ -88,9 +87,9 @@ class HyCons(HyObject):
|
|||||||
|
|
||||||
def replace(self, other):
|
def replace(self, other):
|
||||||
if self.car is not None:
|
if self.car is not None:
|
||||||
self.car.replace(other)
|
replace_hy_obj(self.car, other)
|
||||||
if self.cdr is not None:
|
if self.cdr is not None:
|
||||||
self.cdr.replace(other)
|
replace_hy_obj(self.cdr, other)
|
||||||
|
|
||||||
HyObject.replace(self, other)
|
HyObject.replace(self, other)
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
# DEALINGS IN THE SOFTWARE.
|
# DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
from hy.models import _wrappers, wrap_value
|
||||||
from hy.models.list import HyList
|
from hy.models.list import HyList
|
||||||
|
|
||||||
|
|
||||||
@ -37,3 +38,5 @@ class HyDict(HyList):
|
|||||||
|
|
||||||
def items(self):
|
def items(self):
|
||||||
return list(zip(self.keys(), self.values()))
|
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
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
# DEALINGS IN THE SOFTWARE.
|
# DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
from hy.models import _wrappers, wrap_value
|
||||||
from hy.models.list import HyList
|
from hy.models.list import HyList
|
||||||
|
|
||||||
|
|
||||||
@ -28,3 +29,5 @@ class HyExpression(HyList):
|
|||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "(%s)" % (" ".join([repr(x) for x in 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
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
# DEALINGS IN THE SOFTWARE.
|
# DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
from hy.models import HyObject
|
from hy.models import HyObject, _wrappers
|
||||||
|
|
||||||
|
|
||||||
class HyFloat(HyObject, float):
|
class HyFloat(HyObject, float):
|
||||||
@ -30,3 +30,5 @@ class HyFloat(HyObject, float):
|
|||||||
def __new__(cls, number, *args, **kwargs):
|
def __new__(cls, number, *args, **kwargs):
|
||||||
number = float(number)
|
number = float(number)
|
||||||
return super(HyFloat, cls).__new__(cls, 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
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
# DEALINGS IN THE SOFTWARE.
|
# DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
from hy.models import HyObject
|
from hy.models import HyObject, _wrappers
|
||||||
from hy._compat import long_type
|
from hy._compat import long_type
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
class HyInteger(HyObject, long_type):
|
class HyInteger(HyObject, long_type):
|
||||||
"""
|
"""
|
||||||
@ -32,3 +33,9 @@ class HyInteger(HyObject, long_type):
|
|||||||
def __new__(cls, number, *args, **kwargs):
|
def __new__(cls, number, *args, **kwargs):
|
||||||
number = long_type(number)
|
number = long_type(number)
|
||||||
return super(HyInteger, cls).__new__(cls, 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
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
# DEALINGS IN THE SOFTWARE.
|
# DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
from hy.models import HyObject
|
from hy.models import HyObject, replace_hy_obj, _wrappers, wrap_value
|
||||||
|
|
||||||
|
|
||||||
class HyList(HyObject, list):
|
class HyList(HyObject, list):
|
||||||
@ -28,7 +28,7 @@ class HyList(HyObject, list):
|
|||||||
|
|
||||||
def replace(self, other):
|
def replace(self, other):
|
||||||
for x in self:
|
for x in self:
|
||||||
x.replace(other)
|
replace_hy_obj(x, other)
|
||||||
|
|
||||||
HyObject.replace(self, other)
|
HyObject.replace(self, other)
|
||||||
return self
|
return self
|
||||||
@ -49,3 +49,6 @@ class HyList(HyObject, list):
|
|||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "[%s]" % (" ".join([repr(x) for x in 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
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
# DEALINGS IN THE SOFTWARE.
|
# DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
from hy.models import HyObject
|
from hy.models import HyObject, _wrappers
|
||||||
from hy._compat import str_type
|
from hy._compat import str_type
|
||||||
|
|
||||||
|
|
||||||
@ -29,3 +29,5 @@ class HyString(HyObject, str_type):
|
|||||||
Python version.
|
Python version.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
_wrappers[str_type] = HyString
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
# DEALINGS IN THE SOFTWARE.
|
# DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
from hy.models import _wrappers
|
||||||
from hy.models.string import HyString
|
from hy.models.string import HyString
|
||||||
|
|
||||||
|
|
||||||
@ -28,3 +29,6 @@ class HySymbol(HyString):
|
|||||||
|
|
||||||
def __init__(self, string):
|
def __init__(self, string):
|
||||||
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