diff options
author | Marion <marion.zepf@gmail.com> | 2013-08-29 13:30:34 (GMT) |
---|---|---|
committer | Marion <marion.zepf@gmail.com> | 2013-08-29 13:30:34 (GMT) |
commit | a0e0b5a769f95e995e04c58f270cfa953b34f901 (patch) | |
tree | 99824ffbb8bcea0016f55e124456d14c862c2224 | |
parent | 376521086f2263cbd7270b99b36a06f023942824 (diff) |
introduce PrimitiveDisjunctions and TypeDisjunctions
-rw-r--r-- | TurtleArt/taprimitive.py | 177 | ||||
-rw-r--r-- | TurtleArt/tatype.py | 6 |
2 files changed, 112 insertions, 71 deletions
diff --git a/TurtleArt/taprimitive.py b/TurtleArt/taprimitive.py index fbc9c2d..b7bca61 100644 --- a/TurtleArt/taprimitive.py +++ b/TurtleArt/taprimitive.py @@ -20,7 +20,6 @@ import ast from gettext import gettext as _ -import traceback #from ast_pprint import * # only used for debugging, safe to comment out @@ -30,7 +29,7 @@ from talogo import (LogoCode, logoerror) from taturtle import (Turtle, Turtles) from tatype import (convert, get_call_ast, get_converter, get_type, is_bound_instancemethod, is_instancemethod, - is_staticmethod, TATypeError, TYPE_OBJECT) + is_staticmethod, TATypeError, TypeDisjunction, TYPE_OBJECT) from tautils import debug_output from tawindow import (global_objects, TurtleArtWindow) @@ -947,12 +946,23 @@ class Disjunction(tuple): return "".join(s) def get_alternatives(self): - """ Return a tuple of slot alternatives, i.e. self """ + """ Return a tuple of alternatives, i.e. self """ return self +# make TypeDisjunction 'inherit' the methods of the abstract Disjunction class +TypeDisjunction.__repr__ = Disjunction.__repr__ +TypeDisjunction.get_alternatives = Disjunction.get_alternatives + + class PrimitiveDisjunction(Disjunction,Primitive): - """ Disjunction of two or more Primitives """ + """ Disjunction of two or more Primitives. PrimitiveDisjunctions may not + be nested. """ + + @property + def return_type(self): + """ Tuple of the return_types of all disjuncts """ + return TypeDisjunction((prim.return_type for prim in self)) def __call__(self, *runtime_args, **runtime_kwargs): """ TODO doc """ @@ -1017,84 +1027,109 @@ class ArgSlot(object): elif convert_to_ast: argument = value_to_ast(argument) + # 1. can the argument be called? + (func_disjunction, args) = (None, []) + if (isinstance(argument, tuple) and argument + and callable(argument[0])): + func_disjunction = argument[0] + args = argument[1:] + elif callable(argument): + func_disjunction = argument + + # make sure we can loop over func_disjunction + if not isinstance(func_disjunction, PrimitiveDisjunction): + func_disjunction = PrimitiveDisjunction((func_disjunction, )) + error = None - for slot in self.get_alternatives(): - - # 1. Call the argument? - called_argument = argument - (func, args) = (None, []) - if (isinstance(argument, tuple) and argument - and callable(argument[0])): - func = argument[0] - args = argument[1:] - elif callable(argument): - func = argument - - # check if the argument can fill this slot (type-wise) - if slot.wrapper is not None: - arg_type = get_type(slot.wrapper)[0] - elif func is not None: - arg_type = get_type(func)[0] - else: - arg_type = get_type(argument)[0] - converter = get_converter(arg_type, slot.type) - if converter is None: - continue + for func in func_disjunction: + for slot in self.get_alternatives(): - # 1. (cont'd) call the argument - if func is not None: - if convert_to_ast: - call_ast = get_call_ast(func.__name__, - [value_to_ast(arg) for arg in args]) - if slot.call_arg: - was_argument_called = True - if convert_to_ast: - called_argument = call_ast - else: - called_argument = func(*args) + if isinstance(slot.wrapper, PrimitiveDisjunction): + wrapper_disjunction = slot.wrapper else: - if convert_to_ast: - was_argument_called = True - called_argument = ast.Lambda(args=[], vararg=None, - kwarg=None, defaults=[], body=call_ast) - elif args: - was_argument_called = True - if isinstance(func, Primitive): - called_argument = func.fill_slots(*args) - else: - called_argument = Primitive(func, - [ConstantArg(arg) for arg in args]) + wrapper_disjunction = PrimitiveDisjunction((slot.wrapper, )) - # 2. apply any wrappers - wrapped_argument = called_argument - if slot.wrapper is not None: - if convert_to_ast: - if hasattr(slot.wrapper, "get_ast"): - wrapped_argument = slot.wrapper.get_ast( - called_argument) - else: - raise PyExportError(("cannot convert callable %s to " - "an AST") % (repr(slot.wrapper))) - elif callable(slot.wrapper): - wrapped_argument = slot.wrapper(called_argument) + for wrapper in wrapper_disjunction: - # 3. check the type and convert the argument if necessary - try: - converted_argument = convert(wrapped_argument, slot.type, - converter=converter) - except TATypeError as error: - # on failure, try the next slot - continue - else: - # on success, return the result - return converted_argument + # check if the argument can fill this slot (type-wise) + if wrapper is not None: + arg_type = get_type(wrapper)[0] + elif func is not None: + arg_type = get_type(func)[0] + else: + arg_type = get_type(argument)[0] + converter = None + if isinstance(slot.type, TypeDisjunction): + for type_ in slot.type: + converter = get_converter(arg_type, type_) + if converter is not None: + break + else: + type_ = slot.type + converter = get_converter(arg_type, type_) + # unable to convert, try next wrapper/ slot/ func + if converter is None: + continue + + # 1. (cont'd) call the argument or pass it on as a callable + called_argument = argument + if func is not None: + if convert_to_ast: + call_ast = get_call_ast(func.__name__, + [value_to_ast(arg) for arg in args]) + if slot.call_arg: + # call and pass on the return value + was_argument_called = True + if convert_to_ast: + called_argument = call_ast + else: + called_argument = func(*args) + else: + # don't call and pass on the callable + if convert_to_ast: + was_argument_called = True + called_argument = ast.Lambda(body=call_ast, + args=[], vararg=None, kwarg=None, + defaults=[]) + elif args: + was_argument_called = True + if isinstance(func, Primitive): + called_argument = func.fill_slots(*args) + else: + called_argument = Primitive(func, + [ConstantArg(arg) for arg in args]) + + # 2. apply any wrappers + wrapped_argument = called_argument + if wrapper is not None: + if convert_to_ast: + if hasattr(wrapper, "get_ast"): + wrapped_argument = wrapper.get_ast( + called_argument) + else: + raise PyExportError(("cannot convert callable" + " %s to an AST") % (repr(wrapper))) + elif callable(wrapper): + wrapped_argument = wrapper(called_argument) + + # 3. check the type and convert the argument if necessary + try: + converted_argument = convert(wrapped_argument, type_, + converter=converter) + except TATypeError as error: + # on failure, try next wrapper/ slot/ func + continue + else: + # on success, return the result + return converted_argument # if we haven't returned anything yet, then all alternatives failed if error is not None: raise error else: raise TATypeError(bad_value=argument, bad_type=arg_type, - req_type=self.type) + req_type=type_, + message="filling slot " + repr(self)) class ArgSlotDisjunction(Disjunction,ArgSlot): diff --git a/TurtleArt/tatype.py b/TurtleArt/tatype.py index 2c78c92..e6ffbb9 100644 --- a/TurtleArt/tatype.py +++ b/TurtleArt/tatype.py @@ -46,6 +46,12 @@ class Type(object): def __str__(self): return str(self.name) + +class TypeDisjunction(tuple,Type): + """ Disjunction of two or more Types (from the type hierarchy) """ + pass + + TYPE_OBJECT = Type('object', 0) TYPE_CHAR = Type('char', 1) TYPE_COLOR = Type('color', 2) |