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 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)