Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/TurtleArt/tatype.py
diff options
context:
space:
mode:
authorMarion <marion.zepf@gmail.com>2013-08-25 17:29:52 (GMT)
committer Marion <marion.zepf@gmail.com>2013-08-25 17:29:52 (GMT)
commit46ec8ec0fc8934e94661e6c6b9d45dbcd3a6d7e6 (patch)
treeecba1a26df60409686db8fc0e6f216a0833a03d7 /TurtleArt/tatype.py
parent695bb91e19a959c8d105a4754bb5cd524db4e30a (diff)
add utility to get converter given two types of the hierarchy
Diffstat (limited to 'TurtleArt/tatype.py')
-rw-r--r--TurtleArt/tatype.py167
1 files changed, 100 insertions, 67 deletions
diff --git a/TurtleArt/tatype.py b/TurtleArt/tatype.py
index c7c1fe3..5ddff02 100644
--- a/TurtleArt/tatype.py
+++ b/TurtleArt/tatype.py
@@ -28,6 +28,7 @@ from tautils import debug_output
# types are defined as numbers because they are faster to compare than strings
+# TODO make a dict or something to convert the numbers to strings for output
TYPE_OBJECT = 0
TYPE_CHAR = 1
TYPE_COLOR = 2
@@ -174,18 +175,78 @@ def get_call_ast(func_name, args=None, keywords=None):
kwargs=None)
-class ConversionError(BaseException):
- """ Error that is raised when something goes wrong during type conversion """
+class TATypeError(BaseException):
+ """ TypeError with the types from the hierarchy, not with Python types """
- def __init__(self, message):
- """ message -- the error message """
+ def __init__(self, message, type1=None, type2=None):
+ """ message -- the error message (type1 and type2 are inserted
+ automatically iff message contains '%s' for them) """
self.message = message
+ self.type1 = type1
+ self.type2 = type2
def __str__(self):
- return _("error during type conversion") + ": " + str(self.message)
+ num = self.message.count('%s')
+ if num == 2:
+ msg = self.message % (self.type1, self.type2)
+ elif num == 1:
+ msg = self.message % (self.type1)
+ else:
+ msg = self.message
+ return "TA TypeError: " + msg
-def convert(x, new_type, old_type=None):
+def get_converter(old_type, new_type):
+ """ If there is a converter old_type -> new_type, return it. Else return
+ None. If a chain of converters is necessary, return it as a tuple or
+ list (starting with the innermost, first-to-apply converter). """
+ # every type can be converted to TYPE_OBJECT
+ if new_type == TYPE_OBJECT:
+ return identity
+ # every type can be converted to itself
+ if old_type == new_type:
+ return identity
+
+ # is there a converter for this pair of types?
+ converters_from_old = TYPE_CONVERTERS.get(old_type)
+ if converters_from_old is None:
+ return None
+ converter = converters_from_old.get(new_type)
+ if converter is not None:
+ return converter
+ else:
+ # form the transitive closure of all types that old_type can be
+ # converted to, and look for new_type there
+ backtrace = converters_from_old.copy()
+ new_backtrace = backtrace
+ break_all = False
+ while True:
+ newest_backtrace = {}
+ for t in new_backtrace:
+ for new_t in TYPE_CONVERTERS.get(t, {}):
+ if new_t not in backtrace:
+ newest_backtrace[new_t] = t
+ backtrace[new_t] = t
+ if new_t == new_type:
+ break_all = True
+ break
+ if break_all:
+ break
+ if break_all or not newest_backtrace:
+ break
+ new_backtrace = newest_backtrace
+ # use the backtrace to find the path from old_type to new_type
+ if new_type in backtrace:
+ converter_chain = []
+ t = new_type
+ while t in backtrace and backtrace[t] in ARG_TYPES:
+ converter_chain.insert(0, TYPE_CONVERTERS[backtrace[t]][t])
+ t = backtrace[t]
+ return converter_chain
+ return None
+
+
+def convert(x, new_type, old_type=None, converter=None):
""" Convert x to the new type if possible.
old_type -- the type of x. If not given, it is computed. """
if new_type not in ARG_TYPES:
@@ -202,68 +263,40 @@ def convert(x, new_type, old_type=None):
if old_type == new_type:
return x
- # is there a converter for this pair of types?
- converters_from_old = TYPE_CONVERTERS.get(old_type)
- if converters_from_old is not None:
- converter = converters_from_old.get(new_type)
- if converter is not None:
- # apply the converter
- if is_an_ast:
- if converter == identity:
- return x
- elif is_instancemethod(converter):
- func = ast.Attribute(value=x,
- attr=converter.im_func.__name__,
- ctx=ast.Load)
- return ast.Call(func=func,
- args=[],
- keywords={},
- starargs=None,
- kwargs=None)
- else:
- func_name = converter.__name__
- return get_call_ast(func_name, x)
+ # if the converter is not given, try to find one
+ if converter is None:
+ converter = get_converter(old_type, new_type)
+ if converter is None:
+ # no converter available
+ raise TATypeError("type %s cannot be converted to type %s"
+ % (repr(old_type), repr(new_type)))
+
+ def _apply_converter(converter, y):
+ if is_an_ast:
+ if converter == identity:
+ return y
+ elif is_instancemethod(converter):
+ func = ast.Attribute(value=y,
+ attr=converter.im_func.__name__,
+ ctx=ast.Load)
+ return ast.Call(func=func,
+ args=[],
+ keywords={},
+ starargs=None,
+ kwargs=None)
else:
- return converter(x)
+ func_name = converter.__name__
+ return get_call_ast(func_name, y)
else:
- # form the transitive closure of all types that old_type can be
- # converted to, and look for new_type there
- backtrace = converters_from_old.copy()
- new_backtrace = backtrace
- break_all = False
- while True:
- newest_backtrace = {}
- for t in new_backtrace:
- for new_t in TYPE_CONVERTERS.get(t, {}):
- if new_t not in backtrace:
- newest_backtrace[new_t] = t
- backtrace[new_t] = t
- if new_t == new_type:
- break_all = True
- break
- if break_all:
- break
- if break_all or not newest_backtrace:
- break
- new_backtrace = newest_backtrace
- # use the backtrace to find the path from old_type to new_type
- if new_type in backtrace:
- conversion_path = [new_type]
- while conversion_path[-1] in backtrace:
- conversion_path.append(backtrace[conversion_path[-1]])
- # the last thing must be the conversion function
- conversion_path.pop()
- # the rest are the intermediate types and new_type
- result = x
- intermed_type = old_type
- while conversion_path:
- t = conversion_path.pop()
- result = convert(result, t, old_type=intermed_type)
- intermed_type = t
- return result
-
- # if we have not returned the result, then there is no converter available
- raise ConversionError("type %s cannot be converted to type %s"
- % (repr(old_type), repr(new_type)))
+ return converter(y)
+
+ if isinstance(converter, (list, tuple)):
+ # apply the converter chain recursively
+ result = x
+ for conv in converter:
+ result = _apply_converter(converter, result)
+ return result
+ elif converter is not None:
+ return _apply_converter(converter, x)