예제 #1
0
def value_to_ast(value, *args_for_prim, **kwargs_for_prim):
    """ Turn a value into an AST. Supported types: Primitive, int, float,
    bool, basestring, list
    If the value is already an AST, return it unchanged.
    If the value is a non-exportable Primitive, return None. """
    # already an AST
    if isinstance(value, ast.AST):
        return value
    # Primitive
    elif isinstance(value, Primitive):
        if value.export_me:
            return value.get_ast(*args_for_prim, **kwargs_for_prim)
        else:
            return None
    # boolean
    elif isinstance(value, bool):
        return ast.Name(id=str(value), ctx=ast.Load)
    # number
    elif isinstance(value, (int, float)):
        return ast.Num(n=value)
    # string
    elif isinstance(value, basestring):
        return ast.Str(value)
    # list (recursively transform to an AST)
    elif isinstance(value, list):
        ast_list = []
        for item in value:
            item_ast = value_to_ast(item)
            if item_ast is not None:
                ast_list.append(item_ast)
        return ast.List(elts=ast_list, ctx=ast.Load)
    # color
    elif isinstance(value, Color):
        # call to the Color constructor with this object's values,
        # e.g., Color('red', 0, 50, 100)
        return get_call_ast('Color',
                            [value.name, value.color, value.shade, value.gray],
                            return_type=TYPE_COLOR)
    # vector
    elif isinstance(value, Vector):
        # call to the Vector constructor with this object's values,
        # e.g., Vector('banana', [105, 1, 27, 3, 0])
        return get_call_ast('Vector', [value.name, value.vector],
                            return_type=TYPE_VECTOR)
    # media
    elif isinstance(value, Media):
        args = [value_to_ast(value.type), value_to_ast(value.value)]
        return get_call_ast('Media', args, return_type=TYPE_MEDIA)
    # unknown
    else:
        raise PyExportError("unknown type of raw value: " + repr(type(value)))
def value_to_ast(value, *args_for_prim, **kwargs_for_prim):
    """ Turn a value into an AST. Supported types: Primitive, int, float,
    bool, basestring, list
    If the value is already an AST, return it unchanged.
    If the value is a non-exportable Primitive, return None. """
    # already an AST
    if isinstance(value, ast.AST):
        return value
    # Primitive
    elif isinstance(value, Primitive):
        if value.export_me:
            return value.get_ast(*args_for_prim, **kwargs_for_prim)
        else:
            return None
    # boolean
    elif isinstance(value, bool):
        return ast.Name(id=str(value), ctx=ast.Load)
    # number
    elif isinstance(value, (int, float)):
        return ast.Num(n=value)
    # string
    elif isinstance(value, basestring):
        return ast.Str(value)
    # list (recursively transform to an AST)
    elif isinstance(value, list):
        ast_list = []
        for item in value:
            item_ast = value_to_ast(item)
            if item_ast is not None:
                ast_list.append(item_ast)
        return ast.List(elts=ast_list, ctx=ast.Load)
    # color
    elif isinstance(value, Color):
        # call to the Color constructor with this object's values,
        # e.g., Color('red', 0, 50, 100)
        return get_call_ast('Color', [value.name, value.color,
                                      value.shade, value.gray],
                            return_type=TYPE_COLOR)
    # vector
    elif isinstance(value, Vector):
        # call to the Vector constructor with this object's values,
        # e.g., Vector('banana', [105, 1, 27, 3, 0])
        return get_call_ast('Vector', [value.name, value.vector],
                            return_type=TYPE_VECTOR)
    # media
    elif isinstance(value, Media):
        args = [value_to_ast(value.type), value_to_ast(value.value)]
        return get_call_ast('Media', args, return_type=TYPE_MEDIA)
    # unknown
    else:
        raise PyExportError("unknown type of raw value: " + repr(type(value)))
예제 #3
0
    def get_ast(self, *arg_asts, **kwarg_asts):
        """Transform this object into a Python AST. When serialized and
        executed, the AST will do exactly the same as calling this
        object."""

        if Primitive._DEBUG:
            debug_output(repr(self))
            debug_output("  arg_asts: " + repr(arg_asts))
        new_prim = self.fill_slots(arg_asts, kwarg_asts, convert_to_ast=True)
        if not new_prim.are_slots_filled():
            raise PyExportError("not enough arguments")
        if Primitive._DEBUG:
            debug_output("  new_prim.arg_descs: " + repr(new_prim.arg_descs))

        # extract the actual values from the (now constant) arguments
        (new_arg_asts, new_kwarg_asts) = new_prim.get_values_of_filled_slots(
            exportable_only=True)
        if Primitive._DEBUG:
            debug_output("  new_arg_asts: " + repr(new_arg_asts))
            debug_output("end " + repr(self))

        # SPECIAL HANDLING #

        # loops
        if self == LogoCode.prim_loop:
            controller = self._get_loop_controller()
            if controller == Primitive.controller_repeat:
                # 'repeat' loop
                num_repetitions = new_arg_asts[0]
                if num_repetitions.func.id == 'controller_repeat':
                    num_repetitions = num_repetitions.args[0]
                repeat_iter = get_call_ast("range", [num_repetitions])
                # TODO use new variable name in nested loops
                loop_ast = ast.For(target=ast.Name(id="i", ctx=ast.Store),
                                   iter=repeat_iter,
                                   body=new_arg_asts[1],
                                   orelse=[])
                return loop_ast
            else:
                if controller == Primitive.controller_forever:
                    condition_ast = ast.Name(id="True", ctx=ast.Load)
                elif controller == Primitive.controller_while:
                    condition_ast = new_arg_asts[0].args[0]
                elif controller == Primitive.controller_until:
                    pos_cond_ast = new_arg_asts[0].args[0]
                    condition_ast = ast.UnaryOp(op=ast.Not,
                                                operand=pos_cond_ast)
                else:
                    raise PyExportError("unknown loop controller: " +
                                        repr(controller))
                loop_ast = ast.While(test=condition_ast,
                                     body=new_arg_asts[1],
                                     orelse=[])
                # Until always executes its body once.
                if controller == Primitive.controller_until:
                    loop_list = []
                    for arg_ast in new_arg_asts[1]:
                        loop_list.append(arg_ast)
                    loop_list.append(loop_ast)
                    return loop_list
                else:
                    return loop_ast

        # conditionals
        elif self in (LogoCode.prim_if, LogoCode.prim_ifelse):
            test = new_arg_asts[0]
            body = new_arg_asts[1]
            if len(new_arg_asts) > 2:
                orelse = new_arg_asts[2]
            else:
                orelse = []
            if_ast = ast.If(test=test, body=body, orelse=orelse)
            return if_ast

        # boxes
        elif self == LogoCode.prim_set_box:
            target_ast = ast.Subscript(value=BOX_AST,
                                       slice=ast.Index(value=new_arg_asts[0]),
                                       ctx=ast.Store)
            return ast.Assign(targets=[target_ast], value=new_arg_asts[1])
        elif self == LogoCode.prim_get_box:
            return ast.Subscript(value=BOX_AST,
                                 slice=ast.Index(value=new_arg_asts[0]),
                                 ctx=ast.Load)

        # action stacks
        elif self == LogoCode.prim_define_stack:
            return
        elif self == LogoCode.prim_invoke_stack:
            stack_func = ast.Subscript(
                value=ACTION_AST,
                slice=ast.Index(value=new_arg_asts[0]), ctx=ast.Load)
            call_ast = get_call_ast('logo.icall', [stack_func])
            return [call_ast, ast_yield_true()]

        # stop stack
        elif self == LogoCode.prim_stop_stack:
            return ast.Return()

        # sleep/ wait
        elif self == LogoCode.prim_wait:
            return [get_call_ast('sleep', new_arg_asts), ast_yield_true()]

        # standard operators
        elif self.func.__name__ in Primitive.STANDARD_OPERATORS:
            op = Primitive.STANDARD_OPERATORS[self.func.__name__]
            # 'divide': prevent unwanted integer division
            if self == Primitive.divide:
                def _is_float(x):
                    return get_type(x)[0] == TYPE_FLOAT
                if (not _is_float(new_arg_asts[0]) and
                        not _is_float(new_arg_asts[1])):
                    new_arg_asts[0] = get_call_ast('float', [new_arg_asts[0]],
                                                   return_type=TYPE_FLOAT)
            if len(new_arg_asts) == 1:
                if isinstance(op, tuple):
                    op = op[0]
                return ast.UnaryOp(op=op, operand=new_arg_asts[0])
            elif len(new_arg_asts) == 2:
                if isinstance(op, tuple):
                    op = op[1]
                (left, right) = new_arg_asts
                if issubclass(op, ast.boolop):
                    return ast.BoolOp(op=op, values=[left, right])
                elif issubclass(op, ast.cmpop):
                    return ast.Compare(left=left, ops=[op],
                                       comparators=[right])
                else:
                    return ast.BinOp(op=op, left=left, right=right)

        # f(x)
        elif self == LogoCode.prim_myfunction:
            param_asts = []
            for id_ in ['x', 'y', 'z'][:len(new_arg_asts)-1]:
                param_asts.append(ast.Name(id=id_, ctx=ast.Param))
            func_ast = ast_extensions.LambdaWithStrBody(
                body_str=new_arg_asts[0].s, args=param_asts)
            return get_call_ast(func_ast, new_arg_asts[1:],
                                return_type=self.return_type)

        # square root
        elif self == Primitive.square_root:
            return get_call_ast('sqrt', new_arg_asts, new_kwarg_asts,
                                return_type=self.return_type)

        # random
        elif self in (Primitive.random_char, Primitive.random_int):
            uniform_ast = get_call_ast('uniform', new_arg_asts)
            round_ast = get_call_ast('round', [uniform_ast, ast.Num(n=0)])
            int_ast = get_call_ast('int', [round_ast], return_type=TYPE_INT)
            if self == Primitive.random_char:
                chr_ast = get_call_ast('chr', [int_ast], return_type=TYPE_CHAR)
                return chr_ast
            else:
                return int_ast

        # identity
        elif self == Primitive.identity:
            return new_arg_asts[0]

        # constant
        elif self == CONSTANTS.get:
            return TypedSubscript(value=ast.Name(id='CONSTANTS', ctx=ast.Load),
                                  slice_=ast.Index(value=new_arg_asts[0]),
                                  return_type=self.return_type)

        # group of Primitives or sandwich-clamp block
        elif self in (Primitive.group, LogoCode.prim_clamp):
            ast_list = []
            for prim in new_arg_asts[0]:
                if export_me(prim):
                    new_ast = value_to_ast(prim)
                    if isinstance(new_ast, ast.AST):
                        ast_list.append(new_ast)
            return ast_list

        # set turtle
        elif self == LogoCode.prim_turtle:
            text = 'turtle = turtles.get_active_turtle()'
            return [get_call_ast('logo.prim_turtle', new_arg_asts),
                    ast_extensions.ExtraCode(text)]

        elif self == LogoCode.active_turtle:
            text = 'turtle = turtles.get_active_turtle()'
            return ast_extensions.ExtraCode(text)

        # comment
        elif self == Primitive.comment:
            if isinstance(new_arg_asts[0], ast.Str):
                text = ' ' + str(new_arg_asts[0].s)
            else:
                text = ' ' + str(new_arg_asts[0])
            return ast_extensions.Comment(text)

        # print
        elif self == TurtleArtWindow.print_:
            func_name = self.get_name_for_export()
            call_ast = get_call_ast(func_name, new_arg_asts)
            print_ast = ast.Print(values=new_arg_asts[:1], dest=None, nl=True)
            return [call_ast, print_ast]

        # heap
        elif self == LogoCode.get_heap:
            return TypedName(id_='logo.heap', return_type=self.return_type)
        elif self == LogoCode.reset_heap:
            target_ast = ast.Name(id='logo.heap', ctx=ast.Store)
            value_ast = ast.List(elts=[], ctx=ast.Load)
            return ast.Assign(targets=[target_ast], value=value_ast)

        # NORMAL FUNCTION CALL #

        else:
            func_name = self.get_name_for_export()
            return get_call_ast(func_name, new_arg_asts, new_kwarg_asts,
                                return_type=self.return_type)
    def get_ast(self, *arg_asts, **kwarg_asts):
        """Transform this object into a Python AST. When serialized and
        executed, the AST will do exactly the same as calling this
        object."""

        if Primitive._DEBUG:
            debug_output(repr(self))
            debug_output("  arg_asts: " + repr(arg_asts))
        new_prim = self.fill_slots(arg_asts, kwarg_asts, convert_to_ast=True)
        if not new_prim.are_slots_filled():
            raise PyExportError("not enough arguments")
        if Primitive._DEBUG:
            debug_output("  new_prim.arg_descs: " + repr(new_prim.arg_descs))

        # extract the actual values from the (now constant) arguments
        (new_arg_asts, new_kwarg_asts) = new_prim.get_values_of_filled_slots(
            exportable_only=True)
        if Primitive._DEBUG:
            debug_output("  new_arg_asts: " + repr(new_arg_asts))
            debug_output("end " + repr(self))

        # SPECIAL HANDLING #

        # loops
        if self == LogoCode.prim_loop:
            controller = self._get_loop_controller()
            if controller == Primitive.controller_repeat:
                # 'repeat' loop
                num_repetitions = new_arg_asts[0]
                if num_repetitions.func.id == 'controller_repeat':
                    num_repetitions = num_repetitions.args[0]
                repeat_iter = get_call_ast("range", [num_repetitions])
                # TODO use new variable name in nested loops
                loop_ast = ast.For(target=ast.Name(id="i", ctx=ast.Store),
                                   iter=repeat_iter,
                                   body=new_arg_asts[1],
                                   orelse=[])
                return loop_ast
            else:
                if controller == Primitive.controller_forever:
                    condition_ast = ast.Name(id="True", ctx=ast.Load)
                elif controller == Primitive.controller_while:
                    condition_ast = new_arg_asts[0].args[0]
                elif controller == Primitive.controller_until:
                    pos_cond_ast = new_arg_asts[0].args[0]
                    condition_ast = ast.UnaryOp(op=ast.Not,
                                                operand=pos_cond_ast)
                else:
                    raise PyExportError("unknown loop controller: " +
                                        repr(controller))
                loop_ast = ast.While(test=condition_ast,
                                     body=new_arg_asts[1],
                                     orelse=[])
                # Until always executes its body once.
                if controller == Primitive.controller_until:
                    loop_list = []
                    for arg_ast in new_arg_asts[1]:
                        loop_list.append(arg_ast)
                    loop_list.append(loop_ast)
                    return loop_list
                else:
                    return loop_ast

        # conditionals
        elif self in (LogoCode.prim_if, LogoCode.prim_ifelse):
            test = new_arg_asts[0]
            body = new_arg_asts[1]
            if len(new_arg_asts) > 2:
                orelse = new_arg_asts[2]
            else:
                orelse = []
            if_ast = ast.If(test=test, body=body, orelse=orelse)
            return if_ast

        # boxes
        elif self == LogoCode.prim_set_box:
            target_ast = ast.Subscript(value=BOX_AST,
                                       slice=ast.Index(value=new_arg_asts[0]),
                                       ctx=ast.Store)
            return ast.Assign(targets=[target_ast], value=new_arg_asts[1])
        elif self == LogoCode.prim_get_box:
            return ast.Subscript(value=BOX_AST,
                                 slice=ast.Index(value=new_arg_asts[0]),
                                 ctx=ast.Load)

        # action stacks
        elif self == LogoCode.prim_define_stack:
            return
        elif self == LogoCode.prim_invoke_stack:
            stack_func = ast.Subscript(
                value=ACTION_AST,
                slice=ast.Index(value=new_arg_asts[0]), ctx=ast.Load)
            call_ast = get_call_ast('logo.icall', [stack_func])
            return [call_ast, ast_yield_true()]
        elif self == LogoCode.prim_invoke_return_stack:
            # FIXME: Need to return value
            stack_func = ast.Subscript(
                value=ACTION_AST,
                slice=ast.Index(value=new_arg_asts[0]), ctx=ast.Load)
            call_ast = get_call_ast('logo.icall', [stack_func])
            return [call_ast, ast_yield_true()]

        # stop stack
        elif self == LogoCode.prim_stop_stack:
            return ast.Return()

        # sleep/ wait
        elif self == LogoCode.prim_wait:
            return [get_call_ast('sleep', new_arg_asts), ast_yield_true()]

        # standard operators
        elif self.func.__name__ in Primitive.STANDARD_OPERATORS:
            op = Primitive.STANDARD_OPERATORS[self.func.__name__]
            # 'divide': prevent unwanted integer division
            if self == Primitive.divide:
                def _is_float(x):
                    return get_type(x)[0] == TYPE_FLOAT
                if (not _is_float(new_arg_asts[0]) and
                        not _is_float(new_arg_asts[1])):
                    new_arg_asts[0] = get_call_ast('float', [new_arg_asts[0]],
                                                   return_type=TYPE_FLOAT)
            if len(new_arg_asts) == 1:
                if isinstance(op, tuple):
                    op = op[0]
                return ast.UnaryOp(op=op, operand=new_arg_asts[0])
            elif len(new_arg_asts) == 2:
                if isinstance(op, tuple):
                    op = op[1]
                (left, right) = new_arg_asts
                if issubclass(op, ast.boolop):
                    return ast.BoolOp(op=op, values=[left, right])
                elif issubclass(op, ast.cmpop):
                    return ast.Compare(left=left, ops=[op],
                                       comparators=[right])
                else:
                    return ast.BinOp(op=op, left=left, right=right)

        # f(x)
        elif self == LogoCode.prim_myfunction:
            param_asts = []
            for id_ in ['x', 'y', 'z'][:len(new_arg_asts) - 1]:
                param_asts.append(ast.Name(id=id_, ctx=ast.Param))
            func_ast = ast_extensions.LambdaWithStrBody(
                body_str=new_arg_asts[0].s, args=param_asts)
            return get_call_ast(func_ast, new_arg_asts[1:],
                                return_type=self.return_type)

        # square root
        elif self == Primitive.square_root:
            return get_call_ast('sqrt', new_arg_asts, new_kwarg_asts,
                                return_type=self.return_type)

        # random
        elif self in (Primitive.random_char, Primitive.random_int):
            uniform_ast = get_call_ast('uniform', new_arg_asts)
            round_ast = get_call_ast('round', [uniform_ast, ast.Num(n=0)])
            int_ast = get_call_ast('int', [round_ast], return_type=TYPE_INT)
            if self == Primitive.random_char:
                chr_ast = get_call_ast('chr', [int_ast], return_type=TYPE_CHAR)
                return chr_ast
            else:
                return int_ast

        # identity
        elif self == Primitive.identity:
            return new_arg_asts[0]

        # constant
        elif self == CONSTANTS.get:
            return TypedSubscript(value=ast.Name(id='CONSTANTS', ctx=ast.Load),
                                  slice_=ast.Index(value=new_arg_asts[0]),
                                  return_type=self.return_type)

        # group of Primitives or sandwich-clamp block
        elif self in (Primitive.group, LogoCode.prim_clamp):
            ast_list = []
            for prim in new_arg_asts[0]:
                if export_me(prim):
                    new_ast = value_to_ast(prim)
                    if isinstance(new_ast, ast.AST):
                        ast_list.append(new_ast)
            return ast_list

        # set turtle
        elif self == LogoCode.prim_turtle:
            text = 'turtle = turtles.get_active_turtle()'
            return [get_call_ast('logo.prim_turtle', new_arg_asts),
                    ast_extensions.ExtraCode(text)]

        elif self == LogoCode.active_turtle:
            text = 'turtle = turtles.get_active_turtle()'
            return ast_extensions.ExtraCode(text)

        # comment
        elif self == Primitive.comment:
            if isinstance(new_arg_asts[0], ast.Str):
                text = ' ' + str(new_arg_asts[0].s)
            else:
                text = ' ' + str(new_arg_asts[0])
            return ast_extensions.Comment(text)

        # print
        elif self == TurtleArtWindow.print_:
            func_name = self.get_name_for_export()
            call_ast = get_call_ast(func_name, new_arg_asts)
            print_ast = ast.Print(values=new_arg_asts[:1], dest=None, nl=True)
            return [call_ast, print_ast]

        # heap
        elif self == LogoCode.get_heap:
            return TypedName(id_='logo.heap', return_type=self.return_type)
        elif self == LogoCode.reset_heap:
            target_ast = ast.Name(id='logo.heap', ctx=ast.Store)
            value_ast = ast.List(elts=[], ctx=ast.Load)
            return ast.Assign(targets=[target_ast], value=value_ast)

        # NORMAL FUNCTION CALL #

        else:
            func_name = self.get_name_for_export()
            return get_call_ast(func_name, new_arg_asts, new_kwarg_asts,
                                return_type=self.return_type)