From 46ec8ec0fc8934e94661e6c6b9d45dbcd3a6d7e6 Mon Sep 17 00:00:00 2001 From: Marion Date: Sun, 25 Aug 2013 17:29:52 +0000 Subject: add utility to get converter given two types of the hierarchy --- (limited to 'TurtleArt/tatype.py') 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) -- cgit v0.9.1