From 27c87bbc282dcc72fcc137684831551556eb5e24 Mon Sep 17 00:00:00 2001 From: Marion Date: Tue, 10 Sep 2013 22:04:43 +0000 Subject: add Primitives for the 'push', 'pop', and 'is empty heap?' blocks --- diff --git a/TurtleArt/taexportpython.py b/TurtleArt/taexportpython.py index 19cf48e..3f3a03e 100644 --- a/TurtleArt/taexportpython.py +++ b/TurtleArt/taexportpython.py @@ -50,6 +50,7 @@ tw = get_tw() BOX = {} ACTION = {} +heap = [] diff --git a/TurtleArt/talogo.py b/TurtleArt/talogo.py index 20a374d..b496ea8 100644 --- a/TurtleArt/talogo.py +++ b/TurtleArt/talogo.py @@ -805,6 +805,9 @@ class LogoCode: name = float(name) return 'stack3' + str(name) + def get_heap(self): + return self.heap + def clear_value_blocks(self): if not hasattr(self, 'value_blocks_to_update'): return diff --git a/TurtleArt/taprimitive.py b/TurtleArt/taprimitive.py index 70762f1..b9e74bc 100644 --- a/TurtleArt/taprimitive.py +++ b/TurtleArt/taprimitive.py @@ -132,6 +132,8 @@ class Primitive(object): func_name = "canvas." elif self.wants_logocode(): func_name = "logo." + elif self.wants_heap(): + func_name = "heap." elif self.wants_tawindow(): func_name = "tw." # get the name of the function directly from the function itself @@ -270,21 +272,23 @@ class Primitive(object): debug_output("end " + repr(self)) # what does this primitive want as its first argument? - if new_prim.wants_turtle(): - first_arg = global_objects["turtles"].get_active_turtle() - elif new_prim.wants_turtles(): - first_arg = global_objects["turtles"] - elif new_prim.wants_canvas(): - first_arg = global_objects["canvas"] - elif new_prim.wants_logocode(): - first_arg = global_objects["logo"] - elif new_prim.wants_tawindow(): - first_arg = global_objects["window"] - else: - first_arg = None + first_arg = None + if not is_bound_method(new_prim.func): + if new_prim.wants_turtle(): + first_arg = global_objects["turtles"].get_active_turtle() + elif new_prim.wants_turtles(): + first_arg = global_objects["turtles"] + elif new_prim.wants_canvas(): + first_arg = global_objects["canvas"] + elif new_prim.wants_logocode(): + first_arg = global_objects["logo"] + elif new_prim.wants_heap(): + first_arg = global_objects["logo"].heap + elif new_prim.wants_tawindow(): + first_arg = global_objects["window"] # execute the actual function - if first_arg is None or is_bound_instancemethod(new_prim.func): + if first_arg is None: return_value = new_prim.func(*new_args, **new_kwargs) else: return_value = new_prim.func(first_arg, *new_args, **new_kwargs) @@ -464,6 +468,9 @@ class Primitive(object): text = ' ' + str(ast_to_value(new_arg_asts[0])) return ast_extensions.Comment(text) + elif self == LogoCode.get_heap: + return TypedName(id_='heap', return_type=self.return_type) + # NORMAL FUNCTION CALL # else: @@ -520,6 +527,12 @@ class Primitive(object): first argument? """ return self._wants(LogoCode) + def wants_heap(self): + """ Does this Primitive want to get the heap as its first argument? """ + return ((hasattr(self.func, '__self__') and + isinstance(self.func.__self__, list)) or + self.func in list.__dict__.values()) + def wants_tawindow(self): """ Does this Primitive want to get the TurtleArtWindow instance as its first argument? """ @@ -532,10 +545,7 @@ class Primitive(object): return not is_instancemethod(self.func) def _wants(self, theClass): - if is_instancemethod(self.func): - return self.func.im_class == theClass - else: - return False + return is_instancemethod(self.func) and self.func.im_class == theClass # treat the following methods in a special way when converting the # Primitive to an AST diff --git a/TurtleArt/tatype.py b/TurtleArt/tatype.py index 9a8c17d..3a14fbf 100644 --- a/TurtleArt/tatype.py +++ b/TurtleArt/tatype.py @@ -46,11 +46,9 @@ class Type(object): repr(type(other))) return self.value == other.value - def __repr__(self): - return repr(self.constant_name) - def __str__(self): return str(self.constant_name) + __repr__ = __str__ class TypeDisjunction(tuple,Type): @@ -137,7 +135,11 @@ def get_type(x): return (TYPE_STRING, True) # unary operands never change the type of their argument elif isinstance(x, ast.UnaryOp): - return get_type(x.operand) + if issubclass(x.op, ast.Not): + # 'not' always returns a boolean + return (TYPE_BOOL, True) + else: + return get_type(x.operand) # boolean and comparison operators always return a boolean if isinstance(x, (ast.BoolOp, ast.Compare)): return (TYPE_BOOL, True) @@ -159,11 +161,9 @@ def is_instancemethod(method): # TODO how to access the type `instancemethod` directly? return type(method).__name__ == "instancemethod" -def is_bound_instancemethod(method): - return is_instancemethod(method) and method.im_self is not None - -def is_unbound_instancemethod(method): - return is_instancemethod(method) and method.im_self is None +def is_bound_method(method): + return ((is_instancemethod(method) and method.im_self is not None) or + (hasattr(method, '__self__') and method.__self__ is not None)) def is_staticmethod(method): # TODO how to access the type `staticmethod` directly? @@ -311,8 +311,8 @@ def convert(x, new_type, old_type=None, converter=None): if old_type == new_type: return x - # special case: 'box' block as an AST - if isinstance(x, ast.Subscript) and x.value is BOX_AST: + # special case: 'box' block (or 'pop' block) as an AST + if is_an_ast and old_type == TYPE_BOX: new_type_ast = ast.Name(id=new_type.constant_name) return get_call_ast('convert', [x, new_type_ast], return_type=new_type) @@ -378,7 +378,7 @@ class TypedCall(ast.Call,TypedAST): class TypedSubscript(ast.Subscript,TypedAST): - """ Like a Subscript AST, but with a return type """ + """ Like a Subscript AST, but with a type """ def __init__(self, value, slice_, ctx=ast.Load, return_type=None): @@ -387,6 +387,16 @@ class TypedSubscript(ast.Subscript,TypedAST): self._return_type = return_type +class TypedName(ast.Name,TypedAST): + """ Like a Name AST, but with a type """ + + def __init__(self, id_, ctx=ast.Load, return_type=None): + + ast.Name.__init__(self, id=id_, ctx=ctx) + + self._return_type = return_type + + def get_call_ast(func_name, args=None, kwargs=None, return_type=None): """ Return an AST representing the call to a function with the name func_name, passing it the arguments args (given as a list) and the diff --git a/plugins/turtle_blocks_extras/turtle_blocks_extras.py b/plugins/turtle_blocks_extras/turtle_blocks_extras.py index c75cbbd..04f83ff 100644 --- a/plugins/turtle_blocks_extras/turtle_blocks_extras.py +++ b/plugins/turtle_blocks_extras/turtle_blocks_extras.py @@ -39,7 +39,7 @@ from TurtleArt.tautils import (round_int, debug_output, get_path, hat_on_top, listify, data_from_file) from TurtleArt.tajail import (myfunc, myfunc_import) from TurtleArt.taprimitive import (ArgSlot, ConstantArg, Primitive) -from TurtleArt.tatype import (TYPE_BOOL, TYPE_CHAR, TYPE_INT, TYPE_NUMBER, +from TurtleArt.tatype import (TYPE_BOOL, TYPE_BOX, TYPE_CHAR, TYPE_INT, TYPE_OBJECT, TYPE_STRING) @@ -475,7 +475,8 @@ program started')) help_string=_('pushes value onto FILO (first-in \ last-out heap)')) self.tw.lc.def_prim('push', 1, - lambda self, x: primitive_dictionary['push'](x)) + Primitive(self.tw.lc.heap.append, + arg_descs=[ArgSlot(TYPE_OBJECT)])) define_logo_function('tapush', 'to tapush :foo\nmake "taheap fput \ :foo :taheap\nend\nmake "taheap []\n') @@ -516,7 +517,7 @@ end\n') help_string=_('pops value off FILO (first-in \ last-out heap)')) self.tw.lc.def_prim('pop', 0, - lambda self: primitive_dictionary['pop']()) + Primitive(self.tw.lc.heap.pop, return_type=TYPE_BOX)) define_logo_function('tapop', 'to tapop\nif emptyp :taheap [stop]\n\ make "tmp first :taheap\nmake "taheap butfirst :taheap\noutput :tmp\nend\n') @@ -539,8 +540,11 @@ make "tmp first :taheap\nmake "taheap butfirst :taheap\noutput :tmp\nend\n') value_block=True, help_string=_('returns True if heap is empty')) self.tw.lc.def_prim('isheapempty2', 0, - lambda self: - primitive_dictionary['isheapempty2']()) + # Python automatically converts the heap to a boolean in contexts + # where a boolean is needed + Primitive(Primitive.not_, return_type=TYPE_BOOL, + arg_descs=[ConstantArg( + Primitive(self.tw.lc.get_heap, return_type=TYPE_BOOL))])) primitive_dictionary['print'] = self._prim_print palette.add_block('comment', diff --git a/util/codegen.py b/util/codegen.py index 6e4d383..2390daf 100644 --- a/util/codegen.py +++ b/util/codegen.py @@ -398,6 +398,7 @@ class SourceGenerator(NodeVisitor): def visit_Name(self, node): self.write(node.id) + visit_TypedName = visit_Name def visit_Str(self, node): self.write(repr(node.s)) -- cgit v0.9.1