diff options
Diffstat (limited to 'TurtleArt/tabasics.py')
-rw-r--r-- | TurtleArt/tabasics.py | 970 |
1 files changed, 370 insertions, 600 deletions
diff --git a/TurtleArt/tabasics.py b/TurtleArt/tabasics.py index 08d0864..a80b3ae 100644 --- a/TurtleArt/tabasics.py +++ b/TurtleArt/tabasics.py @@ -20,67 +20,95 @@ #THE SOFTWARE. ''' -This file contains the constants that by-in-large determine the -behavior of Turtle Art. Notably, the block palettes are defined -below. If you want to add a new block to Turtle Art, you could -simply add a block of code to this file or to turtle_block_plugin.py, -which contains additional blocks. (Even better, write your own plugin!!) +This file contains the constants that by-in-large determine the +behavior of Turtle Art. Notably, the block palettes are defined +below. If you want to add a new block to Turtle Art, you could +simply add a block of code to this file or to +../plugins/turtle_blocks_extras/turtle_blocks_extras.py , +which contains additional blocks. (Even better, write your own +plugin!!) Adding a new palette is simply a matter of: + palette = make_palette('mypalette', # the name of your palette colors=["#00FF00", "#00A000"], help_string=_('Palette of my custom commands')) -For example, if we want to add a new turtle command, 'uturn', we'd use the -add_block method in the Palette class. +For example, if we want to add a new turtle command, 'uturn', +we'd use the `add_block` method in the Palette class. + palette.add_block('uturn', # the name of your block style='basic-style', # the block style label=_('u turn'), # the label for the block prim_name='uturn', # code reference (see below) help_string=_('turns the turtle 180 degrees')) - # Next, you need to define what your block will do: - # def_prim takes 3 arguments: the primitive name, the number of - # arguments -- 0 in this case -- and the function to call -- in this - # case, we define the _prim_uturn function to set heading += 180. - self.tw.lc.def_prim('uturn', 0, lambda self: self._prim_uturn) - def _prim_uturn(self): - value = self.tw.turtles.get_active_turtle().get_heading() + 180 - self.tw.turtles.get_active_turtle().set_heading(value) +Next, you need to define what your block will do: def_prim takes +3 arguments: the primitive name, the number of arguments --- 0 +in this case --- and a Primitive object. A Primitive object +represents the statement to be executed when the block is +executed in Turtle Art. For the 'uturn' block, we would like the +statement to look roughly like this: + + Turtle.set_heading(plus(Turtle.get_heading(), 180)) + +Formally, a Primitive object consists of a function, its return +type, and descriptions of its arguments and keyword arguments. +The return type is not a Python type, but a type from Turtle +Art's internal type system. All available types are defined as +constants in tatype.py . + +In this case, we know in advance which arguments each function +gets, so we can use ConstantArg objects as argument descrip- +tions. (For examples where the arguments come from other blocks, +please refer to ../doc/primitives-with-arguments.md .) Note that +Primitive objects can be arguments to other Primitive objects. +This leads to the following tree-like structure for our 'uturn' +block: + + prim_uturn = Primitive(Turtle.set_heading, + arg_descs=[ConstantArg(Primitive( + Primitive.plus, return_type=TYPE_NUMBER, + arg_descs=[ConstantArg(Primitive( + Turtle.get_heading, return_type=TYPE_NUMBER)), + ConstantArg(180)]))], + call_afterwards=self.after_uturn) + + self.tw.lc.def_prim('uturn', 0, prim_uturn) + + # somewhere else in the same class: + def after_uturn(self, value): if self.tw.lc.update_values: self.tw.lc.update_label_value('heading', value) -That's it. When you next run Turtle Art, you will have a 'uturn' block -on the 'mypalette' palette. +The `call_afterwards` attribute is a simple function that is +called just after executing the block. It is often used for +updating GUI labels. -You will have to create icons for the palette-selector buttons. These -are kept in the icons subdirectory. You need two icons: -mypaletteoff.svg and mypaletteon.svg, where 'mypalette' is the same -string as the entry you used in instantiating the Palette class. Note -that the icons should be the same size (55x55) as the others. (This is -the default icon size for Sugar toolbars.) -''' +That's it. When you next run Turtle Art, you will have a 'uturn' +block on the 'mypalette' palette. -from time import time, sleep -from math import sqrt -from random import uniform +You will have to create icons for the palette-selector buttons. +These are kept in the 'icons' subdirectory. You need two icons: +mypaletteoff.svg and mypaletteon.svg, where 'mypalette' is the +same string as the entry you used in instantiating the Palette +object. Note that the icons should be the same size (55x55) as +the others. (This is the default icon size for Sugar toolbars.) +''' +from time import time from gettext import gettext as _ from tapalette import (make_palette, define_logo_function) -from talogo import (primitive_dictionary, logoerror) -from tautils import (convert, chr_to_ord, round_int, strtype, debug_output) +from talogo import primitive_dictionary from taconstants import (Color, CONSTANTS) -from taprimitive import Primitive +from taprimitive import (ArgSlot, ConstantArg, or_, Primitive) +from tatype import (TYPE_BOOL, TYPE_BOX, TYPE_CHAR, TYPE_COLOR, TYPE_FLOAT, + TYPE_INT, TYPE_NUMBER, TYPE_NUMERIC_STRING, TYPE_OBJECT, + TYPE_STRING) from taturtle import Turtle - - -def _num_type(x): - ''' Is x a number type? ''' - if isinstance(x, (int, float)): - return True - return False +from tautils import debug_output def _millisecond(): @@ -95,14 +123,13 @@ class Palettes(): self.tw = turtle_window self.prim_cache = { - "check_number": Primitive(self.check_number, export_me=False), - "convert_value_for_move": Primitive(self.convert_value_for_move, - export_me=False), - "convert_for_cmp": Primitive(Primitive.convert_for_cmp, - constant_args={'decimal_point': self.tw.decimal_point}), - "convert_to_number": Primitive(Primitive.convert_to_number, - constant_args={'decimal_point': self.tw.decimal_point}) - } # avoid several Primitives of the same function + "minus": Primitive(Primitive.minus, + return_type=TYPE_NUMBER, + arg_descs=[ArgSlot(TYPE_NUMBER)]), + "ord": Primitive(ord, + return_type=TYPE_INT, + arg_descs=[ArgSlot(TYPE_CHAR)]) + } # avoid several Primitives of the same function self._turtle_palette() @@ -138,11 +165,10 @@ class Palettes(): logo_command='forward', help_string=_('moves turtle forward')) self.tw.lc.def_prim( - 'forward', - 1, + 'forward', 1, Primitive(Turtle.forward, - slot_wrappers={0: self.prim_cache["convert_value_for_move"]}, - call_afterwards=self.after_move)) + arg_descs=[ArgSlot(TYPE_NUMBER)], + call_afterwards=self.after_move)) palette.add_block('back', style='basic-style-1arg', @@ -151,12 +177,12 @@ class Palettes(): default=100, logo_command='back', help_string=_('moves turtle backward')) - self.tw.lc.def_prim('back', 1, + self.tw.lc.def_prim( + 'back', 1, Primitive(Turtle.forward, - slot_wrappers={0: Primitive(Primitive.minus, - slot_wrappers={0: self.prim_cache["convert_value_for_move"] - })}, - call_afterwards=self.after_move)) + arg_descs=[ArgSlot(TYPE_NUMBER, + wrapper=self.prim_cache["minus"])], + call_afterwards=self.after_move)) palette.add_block('clean', style='basic-style-extended-vertical', @@ -165,15 +191,18 @@ class Palettes(): logo_command='clean', help_string=_('clears the screen and reset the \ turtle')) - self.tw.lc.def_prim( - 'clean', - 0, - Primitive(Primitive.group, constant_args={0: [ - Primitive(self.tw.clear_plugins, call_me=False), - Primitive(self.tw.lc.prim_clear_helper, call_me=False, - export_me=False), - Primitive(self.tw.canvas.clearscreen, call_me=False), - Primitive(self.tw.turtles.reset_turtles, call_me=False)]})) + self.tw.lc.def_prim('clean', 0, + Primitive(Primitive.group, arg_descs=[ + ConstantArg([ + Primitive(self.tw.clear_plugins), + Primitive(self.tw.lc.stop_playing_media), + Primitive(self.tw.lc.reset_scale), + Primitive(self.tw.lc.reset_timer), + Primitive(self.tw.lc.clear_value_blocks), + Primitive(self.tw.lc.reset_internals), + Primitive(self.tw.canvas.clearscreen), + Primitive(self.tw.turtles.reset_turtles) + ])])) palette.add_block('left', style='basic-style-1arg', @@ -186,9 +215,9 @@ in degrees)')) self.tw.lc.def_prim( 'left', 1, Primitive(Turtle.right, - slot_wrappers={0: Primitive(Primitive.minus, - slot_wrappers={0: self.prim_cache["check_number"]})}, - call_afterwards=self.after_right)) + arg_descs=[ArgSlot(TYPE_NUMBER, + wrapper=self.prim_cache["minus"])], + call_afterwards=self.after_right)) palette.add_block('right', style='basic-style-1arg', @@ -199,11 +228,10 @@ in degrees)')) help_string=_('turns turtle clockwise (angle in \ degrees)')) self.tw.lc.def_prim( - 'right', - 1, + 'right', 1, Primitive(Turtle.right, - slot_wrappers={0: self.prim_cache["check_number"]}, - call_afterwards=self.after_right)) + arg_descs=[ArgSlot(TYPE_NUMBER)], + call_afterwards=self.after_right)) palette.add_block('arc', style='basic-style-2arg', @@ -213,11 +241,10 @@ degrees)')) logo_command='taarc', help_string=_('moves turtle along an arc')) self.tw.lc.def_prim( - 'arc', - 2, + 'arc', 2, Primitive(Turtle.arc, - slot_wrappers={0: Primitive(float, export_me=False), - 1: Primitive(float, export_me=False)}, + arg_descs=[ArgSlot(TYPE_NUMBER), + ArgSlot(TYPE_NUMBER)], call_afterwards=self.after_arc)) define_logo_function('taarc', 'to taarc :a :r\nrepeat round :a \ [right 1 forward (0.0175 * :r)]\nend\n') @@ -231,17 +258,12 @@ degrees)')) help_string=_('moves turtle to position xcor, ycor; \ (0, 0) is in the center of the screen.')) self.tw.lc.def_prim( - 'setxy2', - 2, + 'setxy2', 2, Primitive(Turtle.set_xy, - slot_wrappers={(0, 2): Primitive(Primitive.make_tuple, - slot_wrappers={0:self.prim_cache["convert_value_for_move"], - 1:self.prim_cache["convert_value_for_move"] - })}, - call_afterwards=self.after_move)) + arg_descs=[ArgSlot(TYPE_NUMBER), ArgSlot(TYPE_NUMBER)], + call_afterwards=self.after_move)) define_logo_function('tasetxy', 'to tasetxy :x :y\nsetxy :x :y\nend\n') - primitive_dictionary['set'] = self._prim_set palette.add_block('seth', style='basic-style-1arg', label=_('set heading'), @@ -251,11 +273,11 @@ degrees)')) help_string=_('sets the heading of the turtle (0 is \ towards the top of the screen.)')) self.tw.lc.def_prim( - 'seth', - 1, + 'seth', 1, Primitive(Turtle.set_heading, - slot_wrappers={0: Primitive(float, export_me=False)}, - call_afterwards=lambda value: self.after_set('heading',value))) + arg_descs=[ArgSlot(TYPE_NUMBER)], + call_afterwards=lambda value: self.after_set( + 'heading', value))) palette.add_block('xcor', style='box-style', @@ -266,13 +288,11 @@ the turtle (can be used in place of a number block)'), prim_name='xcor', logo_command='xcor') self.tw.lc.def_prim( - 'xcor', - 0, - Primitive(Primitive.divide, constant_args={ - 0: Primitive(Turtle.get_x, constant_args={ - 0: Primitive(self.tw.turtles.get_active_turtle, - export_me=False)}), - 1: Primitive(self.tw.get_coord_scale)})) + 'xcor', 0, + Primitive(Primitive.divide, return_type=TYPE_FLOAT, + arg_descs=[ConstantArg(Primitive(Turtle.get_x)), + ConstantArg(Primitive( + self.tw.get_coord_scale))])) palette.add_block('ycor', style='box-style', @@ -283,13 +303,11 @@ the turtle (can be used in place of a number block)'), prim_name='ycor', logo_command='ycor') self.tw.lc.def_prim( - 'ycor', - 0, - Primitive(Primitive.divide, constant_args={ - 0: Primitive(Turtle.get_y, constant_args={ - 0: Primitive(self.tw.turtles.get_active_turtle, - export_me=False)}), - 1: Primitive(self.tw.get_coord_scale)})) + 'ycor', 0, + Primitive(Primitive.divide, return_type=TYPE_FLOAT, + arg_descs=[ConstantArg(Primitive(Turtle.get_y)), + ConstantArg(Primitive( + self.tw.get_coord_scale))])) palette.add_block('heading', style='box-style', @@ -299,7 +317,9 @@ turtle (can be used in place of a number block)'), value_block=True, prim_name='heading', logo_command='heading') - self.tw.lc.def_prim('heading', 0, Primitive(Turtle.get_heading)) + self.tw.lc.def_prim('heading', 0, + Primitive( + Turtle.get_heading, return_type=TYPE_NUMBER)) palette.add_block('turtle-label', hidden=True, @@ -307,7 +327,6 @@ turtle (can be used in place of a number block)'), label=['turtle']) # Deprecated - primitive_dictionary['move'] = self._prim_move palette.add_block('setxy', hidden=True, style='basic-style-2arg', @@ -318,11 +337,11 @@ turtle (can be used in place of a number block)'), help_string=_('moves turtle to position xcor, ycor; \ (0, 0) is in the center of the screen.')) self.tw.lc.def_prim( - 'setxy', - 2, - lambda self, x, y: primitive_dictionary['move']( - self.tw.turtles.get_active_turtle().set_xy, (x, y), - pendown=False)) + 'setxy', 2, + Primitive(Turtle.set_xy, + arg_descs=[ArgSlot(TYPE_NUMBER), ArgSlot(TYPE_NUMBER)], + kwarg_descs={'pendown': ConstantArg(False)}, + call_afterwards=self.after_move)) define_logo_function('tasetxypenup', 'to tasetxypenup :x :y\npenup\n\ setxy :x :y\npendown\nend\n') @@ -345,9 +364,9 @@ setxy :x :y\npendown\nend\n') help_string=_('fills the background with (color, \ shade)')) self.tw.lc.def_prim( - 'fillscreen', - 2, - Primitive(self.tw.canvas.fillscreen)) + 'fillscreen', 2, + Primitive(self.tw.canvas.fillscreen, + arg_descs=[ArgSlot(TYPE_NUMBER), ArgSlot(TYPE_NUMBER)])) palette.add_block('fillscreen2', style='basic-style-3arg', @@ -359,9 +378,10 @@ shade)')) help_string=_('fills the background with (color, \ shade)')) self.tw.lc.def_prim( - 'fillscreen2', - 3, - Primitive(self.tw.canvas.fillscreen_with_gray)) + 'fillscreen2', 3, + Primitive(self.tw.canvas.fillscreen_with_gray, + arg_descs=[ArgSlot(TYPE_NUMBER), ArgSlot(TYPE_NUMBER), + ArgSlot(TYPE_NUMBER)])) define_logo_function('tasetbackground', 'to tasetbackground :color \ :shade\ntasetshade :shade\nsetbackground :color\nend\n') @@ -375,10 +395,12 @@ shade)')) help_string=_('sets color of the line drawn by the \ turtle')) self.tw.lc.def_prim( - 'setcolor', - 1, + 'setcolor', 1, Primitive(Turtle.set_color, - call_afterwards=lambda value: self.after_set('color', value))) + arg_descs=[or_(ArgSlot(TYPE_COLOR), + ArgSlot(TYPE_NUMBER))], + call_afterwards=lambda value: self.after_set( + 'color', value))) palette.add_block('setshade', style='basic-style-1arg', @@ -389,10 +411,11 @@ turtle')) help_string=_('sets shade of the line drawn by the \ turtle')) self.tw.lc.def_prim( - 'setshade', - 1, + 'setshade', 1, Primitive(Turtle.set_shade, - call_afterwards=lambda value: self.after_set('shade', value))) + arg_descs=[ArgSlot(TYPE_NUMBER)], + call_afterwards=lambda value: self.after_set( + 'shade', value))) palette.add_block('setgray', style='basic-style-1arg', @@ -402,10 +425,11 @@ turtle')) help_string=_('sets gray level of the line drawn by \ the turtle')) self.tw.lc.def_prim( - 'setgray', - 1, + 'setgray', 1, Primitive(Turtle.set_gray, - call_afterwards=lambda value: self.after_set('gray', value))) + arg_descs=[ArgSlot(TYPE_NUMBER)], + call_afterwards=lambda value: self.after_set( + 'gray', value))) palette.add_block('color', style='box-style', @@ -415,7 +439,9 @@ in place of a number block)'), value_block=True, prim_name='color', logo_command='pencolor') - self.tw.lc.def_prim('color', 0, Primitive(Turtle.get_color)) + self.tw.lc.def_prim('color', 0, + Primitive( + Turtle.get_color, return_type=TYPE_NUMBER)) palette.add_block('shade', style='box-style', @@ -433,7 +459,9 @@ in place of a number block)'), used in place of a number block)'), value_block=True, prim_name='gray') - self.tw.lc.def_prim('gray', 0, Primitive(Turtle.get_gray)) + self.tw.lc.def_prim('gray', 0, + Primitive( + Turtle.get_gray, return_type=TYPE_NUMBER)) palette.add_block('penup', style='basic-style-extended-vertical', @@ -442,9 +470,8 @@ used in place of a number block)'), logo_command='penup', help_string=_('Turtle will not draw when moved.')) self.tw.lc.def_prim( - 'penup', - 0, - Primitive(Turtle.set_pen_state, constant_args={0: False})) + 'penup', 0, + Primitive(Turtle.set_pen_state, arg_descs=[ConstantArg(False)])) palette.add_block('pendown', style='basic-style-extended-vertical', @@ -453,9 +480,8 @@ used in place of a number block)'), logo_command='pendown', help_string=_('Turtle will draw when moved.')) self.tw.lc.def_prim( - 'pendown', - 0, - Primitive(Turtle.set_pen_state, constant_args={0: True})) + 'pendown', 0, + Primitive(Turtle.set_pen_state, arg_descs=[ConstantArg(True)])) palette.add_block('penstate', style='boolean-block-style', @@ -463,9 +489,8 @@ used in place of a number block)'), prim_name='penstate', help_string=_('returns True if pen is down')) self.tw.lc.def_prim( - 'penstate', - 0, - Primitive(Turtle.get_pen_state)) + 'penstate', 0, + Primitive(Turtle.get_pen_state, return_type=TYPE_BOOL)) palette.add_block('setpensize', style='basic-style-1arg', @@ -478,7 +503,9 @@ turtle')) self.tw.lc.def_prim( 'setpensize', 1, Primitive(Turtle.set_pen_size, - call_afterwards=lambda val: self.after_set('pensize', val))) + arg_descs=[ArgSlot(TYPE_NUMBER)], + call_afterwards=lambda val: self.after_set( + 'pensize', val))) define_logo_function('tasetpensize', 'to tasetpensize :a\nsetpensize round :a\nend\n') @@ -488,10 +515,7 @@ turtle')) prim_name='startfill', help_string=_('starts filled polygon (used with end \ fill block)')) - self.tw.lc.def_prim( - 'startfill', - 0, - Primitive(Turtle.start_fill)) + self.tw.lc.def_prim('startfill', 0, Primitive(Turtle.start_fill)) palette.add_block('stopfill', style='basic-style-extended-vertical', @@ -499,10 +523,7 @@ fill block)')) prim_name='stopfill', help_string=_('completes filled polygon (used with \ start fill block)')) - self.tw.lc.def_prim( - 'stopfill', - 0, - Primitive(Turtle.stop_fill)) + self.tw.lc.def_prim('stopfill', 0, Primitive(Turtle.stop_fill)) palette.add_block('pensize', style='box-style', @@ -513,9 +534,8 @@ in place of a number block)'), prim_name='pensize', logo_command='pensize') self.tw.lc.def_prim( - 'pensize', - 0, - Primitive(Turtle.get_pen_size)) + 'pensize', 0, + Primitive(Turtle.get_pen_size, return_type=TYPE_NUMBER)) define_logo_function('tapensize', 'to tapensize\noutput first round \ pensize\nend\n') @@ -528,18 +548,10 @@ pensize\nend\n') colors=["#00FFFF", "#00A0A0"], help_string=_('Palette of pen colors')) - self._make_constant(palette, 'red', _('red'), CONSTANTS['red']) - self._make_constant(palette, 'orange', _('orange'), - CONSTANTS['orange']) - self._make_constant(palette, 'yellow', _('yellow'), - CONSTANTS['yellow']) - self._make_constant(palette, 'green', _('green'), CONSTANTS['green']) - self._make_constant(palette, 'cyan', _('cyan'), CONSTANTS['cyan']) - self._make_constant(palette, 'blue', _('blue'), CONSTANTS['blue']) - self._make_constant(palette, 'purple', _('purple'), - CONSTANTS['purple']) - self._make_constant(palette, 'white', _('white'), CONSTANTS['white']) - self._make_constant(palette, 'black', _('black'), CONSTANTS['black']) + color_names = ('red', 'orange', 'yellow', 'green', 'cyan', 'blue', + 'purple', 'white', 'black') + for name in color_names: + self._make_constant(palette, name, _(name), name) # In order to map Turtle Art colors to the standard UCB Logo palette, # we need to define a somewhat complex set of functions. @@ -631,10 +643,16 @@ tasetshade :shade \n') prim_name='plus', logo_command='sum', help_string=_('adds two alphanumeric inputs')) - self.tw.lc.def_prim('plus', 2, - # TODO re-enable use with lists - Primitive(Primitive.plus, slot_wrappers={ - (0, 2): Primitive(Primitive.convert_for_plus)})) + self.tw.lc.def_prim( + 'plus', 2, + # add up two numbers ... + or_(Primitive(Primitive.plus, return_type=TYPE_NUMBER, + arg_descs=[ArgSlot(TYPE_NUMBER), + ArgSlot(TYPE_NUMBER)]), + # ... or concatenate two strings + Primitive(Primitive.plus, return_type=TYPE_STRING, + arg_descs=[ArgSlot(TYPE_STRING), + ArgSlot(TYPE_STRING)]))) palette.add_block('minus2', style='number-style-porch', @@ -644,17 +662,10 @@ tasetshade :shade \n') logo_command='taminus', help_string=_('subtracts bottom numeric input from \ top numeric input')) - self.tw.lc.def_prim('minus', 2, - # TODO re-enable use with lists - Primitive(Primitive.minus, slot_wrappers={ - 0: Primitive(self.check_number, - export_me=False, - slot_wrappers={ - 0: self.prim_cache["convert_to_number"]}), - 1: Primitive(self.check_number, - export_me=False, - slot_wrappers={ - 0: self.prim_cache["convert_to_number"]})})) + self.tw.lc.def_prim( + 'minus', 2, + Primitive(Primitive.minus, return_type=TYPE_NUMBER, + arg_descs=[ArgSlot(TYPE_NUMBER), ArgSlot(TYPE_NUMBER)])) define_logo_function('taminus', 'to taminus :y :x\noutput sum :x \ minus :y\nend\n') @@ -665,17 +676,10 @@ minus :y\nend\n') prim_name='product', logo_command='product', help_string=_('multiplies two numeric inputs')) - self.tw.lc.def_prim('product', 2, - # TODO re-enable use with lists - Primitive(Primitive.multiply, slot_wrappers={ - 0: Primitive(self.check_number, - export_me=False, - slot_wrappers={ - 0: self.prim_cache["convert_to_number"]}), - 1: Primitive(self.check_number, - export_me=False, - slot_wrappers={ - 0: self.prim_cache["convert_to_number"]})})) + self.tw.lc.def_prim( + 'product', 2, + Primitive(Primitive.multiply, return_type=TYPE_NUMBER, + arg_descs=[ArgSlot(TYPE_NUMBER), ArgSlot(TYPE_NUMBER)])) palette.add_block('division2', style='number-style-porch', @@ -685,20 +689,10 @@ minus :y\nend\n') logo_command='quotient', help_string=_('divides top numeric input \ (numerator) by bottom numeric input (denominator)')) - self.tw.lc.def_prim('division', 2, - # TODO re-enable use with lists - Primitive(Primitive.divide, slot_wrappers={ - 0: Primitive(self.check_number, - export_me=False, - slot_wrappers={ - 0: self.prim_cache["convert_to_number"]}), - 1: Primitive(self.check_non_zero, - export_me=False, - slot_wrappers={ - 0: Primitive(self.check_number, - export_me=False, - slot_wrappers={ - 0: self.prim_cache["convert_to_number"]})})})) + self.tw.lc.def_prim( + 'division', 2, + Primitive(Primitive.divide, return_type=TYPE_NUMBER, + arg_descs=[ArgSlot(TYPE_NUMBER), ArgSlot(TYPE_NUMBER)])) palette.add_block('identity2', style='number-style-1arg', @@ -707,7 +701,25 @@ minus :y\nend\n') prim_name='id', help_string=_('identity operator used for extending \ blocks')) - self.tw.lc.def_prim('id', 1, Primitive(Primitive.identity)) + self.tw.lc.def_prim( + 'id', 1, + # preserve the Type of the argument: try less general types first + or_(Primitive(Primitive.identity, return_type=TYPE_NUMERIC_STRING, + arg_descs=[ArgSlot(TYPE_NUMERIC_STRING)]), + Primitive(Primitive.identity, return_type=TYPE_CHAR, + arg_descs=[ArgSlot(TYPE_CHAR)]), + Primitive(Primitive.identity, return_type=TYPE_COLOR, + arg_descs=[ArgSlot(TYPE_COLOR)]), + Primitive(Primitive.identity, return_type=TYPE_FLOAT, + arg_descs=[ArgSlot(TYPE_FLOAT)]), + Primitive(Primitive.identity, return_type=TYPE_INT, + arg_descs=[ArgSlot(TYPE_INT)]), + Primitive(Primitive.identity, return_type=TYPE_NUMBER, + arg_descs=[ArgSlot(TYPE_NUMBER)]), + Primitive(Primitive.identity, return_type=TYPE_STRING, + arg_descs=[ArgSlot(TYPE_STRING)]), + Primitive(Primitive.identity, return_type=TYPE_OBJECT, + arg_descs=[ArgSlot(TYPE_OBJECT)]))) palette.add_block('remainder2', style='number-style-porch', @@ -716,19 +728,10 @@ blocks')) prim_name='remainder', logo_command='remainder', help_string=_('modular (remainder) operator')) - self.tw.lc.def_prim('remainder', 2, - Primitive(Primitive.modulo, slot_wrappers={ - 0: Primitive(self.check_number, - export_me=False, - slot_wrappers={ - 0: self.prim_cache["convert_to_number"]}), - 1: Primitive(self.check_non_zero, - export_me=False, - slot_wrappers={ - 0: Primitive(self.check_number, - export_me=False, - slot_wrappers={ - 0: self.prim_cache["convert_to_number"]})})})) + self.tw.lc.def_prim( + 'remainder', 2, + Primitive(Primitive.modulo, return_type=TYPE_NUMBER, + arg_descs=[ArgSlot(TYPE_NUMBER), ArgSlot(TYPE_NUMBER)])) palette.add_block('sqrt', style='number-style-1arg', @@ -737,16 +740,11 @@ blocks')) prim_name='sqrt', logo_command='tasqrt', help_string=_('calculates square root')) - self.tw.lc.def_prim('sqrt', 1, - Primitive(sqrt, - slot_wrappers={0: Primitive(self.check_non_negative, - slot_wrappers={0: Primitive(self.check_number, - slot_wrappers={0: - self.prim_cache["convert_to_number"]}, - export_me=False)}, - export_me=False)})) - - primitive_dictionary['random'] = self._prim_random + self.tw.lc.def_prim( + 'sqrt', 1, + Primitive(Primitive.square_root, return_type=TYPE_FLOAT, + arg_descs=[ArgSlot(TYPE_NUMBER)])) + palette.add_block('random', style='number-style-block', label=[_('random'), _('min'), _('max')], @@ -755,9 +753,19 @@ blocks')) logo_command='tarandom', help_string=_('returns random number between \ minimum (top) and maximum (bottom) values')) + self.tw.lc.def_prim( - 'random', 2, lambda self, x, y: primitive_dictionary['random']( - x, y)) + 'random', 2, + or_( # random character ... + Primitive(Primitive.random_char, return_type=TYPE_CHAR, + arg_descs=[ + ArgSlot(TYPE_INT, + wrapper=self.prim_cache["ord"]), + ArgSlot(TYPE_INT, + wrapper=self.prim_cache["ord"])]), + # ... or random number + Primitive(Primitive.random_int, return_type=TYPE_INT, + arg_descs=[ArgSlot(TYPE_INT), ArgSlot(TYPE_INT)]))) define_logo_function('tarandom', 'to tarandom :min :max\n \ output (random (:max - :min)) + :min\nend\n') @@ -777,10 +785,17 @@ operators')) prim_name='greater?', logo_command='greater?', help_string=_('logical greater-than operator')) - self.tw.lc.def_prim('greater?', 2, - Primitive(Primitive.greater, - slot_wrappers={0: self.prim_cache["convert_for_cmp"], - 1: self.prim_cache["convert_for_cmp"]})) + self.tw.lc.def_prim( + 'greater?', 2, + Primitive(Primitive.greater, return_type=TYPE_BOOL, + arg_descs=or_([ArgSlot(TYPE_COLOR), + ArgSlot(TYPE_COLOR)], + [ArgSlot(TYPE_NUMBER), + ArgSlot(TYPE_NUMBER)], + [ArgSlot(TYPE_STRING), + ArgSlot(TYPE_STRING)], + [ArgSlot(TYPE_OBJECT), + ArgSlot(TYPE_OBJECT)]))) palette.add_block('less2', style='compare-porch-style', @@ -790,10 +805,17 @@ operators')) prim_name='less?', logo_command='less?', help_string=_('logical less-than operator')) - self.tw.lc.def_prim('less?', 2, - Primitive(Primitive.less, - slot_wrappers={0: self.prim_cache["convert_for_cmp"], - 1: self.prim_cache["convert_for_cmp"]})) + self.tw.lc.def_prim( + 'less?', 2, + Primitive(Primitive.less, return_type=TYPE_BOOL, + arg_descs=or_([ArgSlot(TYPE_COLOR), + ArgSlot(TYPE_COLOR)], + [ArgSlot(TYPE_NUMBER), + ArgSlot(TYPE_NUMBER)], + [ArgSlot(TYPE_STRING), + ArgSlot(TYPE_STRING)], + [ArgSlot(TYPE_OBJECT), + ArgSlot(TYPE_OBJECT)]))) palette.add_block('equal2', style='compare-style', @@ -803,10 +825,17 @@ operators')) prim_name='equal?', logo_command='equal?', help_string=_('logical equal-to operator')) - self.tw.lc.def_prim('equal?', 2, - Primitive(Primitive.equals, - slot_wrappers={0: self.prim_cache["convert_for_cmp"], - 1: self.prim_cache["convert_for_cmp"]})) + self.tw.lc.def_prim( + 'equal?', 2, + Primitive(Primitive.equals, return_type=TYPE_BOOL, + arg_descs=or_([ArgSlot(TYPE_COLOR), + ArgSlot(TYPE_COLOR)], + [ArgSlot(TYPE_NUMBER), + ArgSlot(TYPE_NUMBER)], + [ArgSlot(TYPE_STRING), + ArgSlot(TYPE_STRING)], + [ArgSlot(TYPE_OBJECT), + ArgSlot(TYPE_OBJECT)]))) palette.add_block('not', style='not-style', @@ -814,7 +843,10 @@ operators')) prim_name='not', logo_command='not', help_string=_('logical NOT operator')) - self.tw.lc.def_prim('not', 1, Primitive(Primitive.not_)) + self.tw.lc.def_prim( + 'not', 1, + Primitive(Primitive.not_, return_type=TYPE_BOOL, + arg_descs=[ArgSlot(TYPE_BOOL)])) palette.add_block('and2', style='boolean-style', @@ -824,7 +856,9 @@ operators')) special_name=_('and'), help_string=_('logical AND operator')) self.tw.lc.def_prim( - 'and', 2, Primitive(Primitive.and_)) + 'and', 2, + Primitive(Primitive.and_, return_type=TYPE_BOOL, + arg_descs=[ArgSlot(TYPE_BOOL), ArgSlot(TYPE_BOOL)])) palette.add_block('or2', style='boolean-style', @@ -834,7 +868,9 @@ operators')) special_name=_('or'), help_string=_('logical OR operator')) self.tw.lc.def_prim( - 'or', 2, Primitive(Primitive.or_)) + 'or', 2, + Primitive(Primitive.or_, return_type=TYPE_BOOL, + arg_descs=[ArgSlot(TYPE_BOOL), ArgSlot(TYPE_BOOL)])) def _flow_palette(self): ''' The basic Turtle Art flow palette ''' @@ -845,7 +881,6 @@ operators')) colors=["#FFC000", "#A08000"], help_string=_('Palette of flow operators')) - primitive_dictionary['wait'] = self._prim_wait palette.add_block('wait', style='basic-style-1arg', label=_('wait'), @@ -854,9 +889,11 @@ operators')) logo_command='wait', help_string=_('pauses program execution a specified \ number of seconds')) - self.tw.lc.def_prim('wait', 1, primitive_dictionary['wait'], True) + self.tw.lc.def_prim( + 'wait', 1, + Primitive(self.tw.lc.prim_wait, arg_descs=[ArgSlot(TYPE_NUMBER)]), + True) - primitive_dictionary['forever'] = self._prim_forever palette.add_block('forever', style='clamp-style', label=_('forever'), @@ -864,13 +901,13 @@ number of seconds')) default=[None, None], logo_command='forever', help_string=_('loops forever')) - self.tw.lc.def_prim('forever', 1, - Primitive(self.tw.lc.prim_loop, - constant_args={0: Primitive(Primitive.controller_forever, - call_me=False)}), + self.tw.lc.def_prim( + 'forever', 1, + Primitive(self.tw.lc.prim_loop, arg_descs=[ConstantArg(Primitive( + Primitive.controller_forever)), ArgSlot(TYPE_OBJECT, + call_arg=False)]), True) - primitive_dictionary['repeat'] = self._prim_repeat palette.add_block('repeat', style='clamp-style-1arg', label=_('repeat'), @@ -879,12 +916,14 @@ number of seconds')) logo_command='repeat', special_name=_('repeat'), help_string=_('loops specified number of times')) - self.tw.lc.def_prim('repeat', 2, + self.tw.lc.def_prim( + 'repeat', 2, Primitive(self.tw.lc.prim_loop, - slot_wrappers={0: Primitive(Primitive.controller_repeat, - slot_wrappers={0: Primitive(self.tw.lc.int, - slot_wrappers={0: self.prim_cache["check_number"] - })})}), + arg_descs=[ArgSlot( + TYPE_OBJECT, + wrapper=Primitive(Primitive.controller_repeat, + arg_descs=[ArgSlot(TYPE_INT)])), + ArgSlot(TYPE_OBJECT, call_arg=False)]), True) palette.add_block('if', @@ -896,7 +935,11 @@ number of seconds')) logo_command='if', help_string=_('if-then operator that uses boolean \ operators from Numbers palette')) - self.tw.lc.def_prim('if', 2, Primitive(self.tw.lc.prim_if), True) + self.tw.lc.def_prim( + 'if', 2, + Primitive(self.tw.lc.prim_if, + arg_descs=[ArgSlot(TYPE_BOOL), ArgSlot(TYPE_OBJECT)]), + True) palette.add_block('ifelse', hidden=True, # Too big to fit palette @@ -908,8 +951,12 @@ operators from Numbers palette')) special_name=_('if then else'), help_string=_('if-then-else operator that uses \ boolean operators from Numbers palette')) - self.tw.lc.def_prim('ifelse', 3, Primitive(self.tw.lc.prim_ifelse), - True) + self.tw.lc.def_prim( + 'ifelse', 3, + Primitive(self.tw.lc.prim_ifelse, + arg_descs=[ArgSlot(TYPE_BOOL), ArgSlot(TYPE_OBJECT), + ArgSlot(TYPE_OBJECT)]), + True) # macro palette.add_block('ifthenelse', @@ -926,7 +973,8 @@ boolean operators from Numbers palette')) prim_name='nop', special_name=_('horizontal space'), help_string=_('jogs stack right')) - self.tw.lc.def_prim('nop', 0, + self.tw.lc.def_prim( + 'nop', 0, Primitive(Primitive.do_nothing, export_me=False)) palette.add_block('vspace', @@ -935,18 +983,19 @@ boolean operators from Numbers palette')) prim_name='nop', special_name=_('vertical space'), help_string=_('jogs stack down')) - self.tw.lc.def_prim('nop', 0, + self.tw.lc.def_prim( + 'nop', 0, Primitive(Primitive.do_nothing, export_me=False)) - primitive_dictionary['stopstack'] = self._prim_stopstack palette.add_block('stopstack', style='basic-style-tail', label=_('stop action'), prim_name='stopstack', logo_command='stop', help_string=_('stops current action')) - self.tw.lc.def_prim('stopstack', 0, - lambda self: primitive_dictionary['stopstack']()) + self.tw.lc.def_prim( + 'stopstack', 0, + Primitive(self.tw.lc.prim_stop_stack)) def _blocks_palette(self): ''' The basic Turtle Art blocks palette ''' @@ -964,12 +1013,13 @@ boolean operators from Numbers palette')) logo_command='to start\n', help_string=_('connects action to toolbar run \ buttons')) - self.tw.lc.def_prim('start', 0, - Primitive(Primitive.group, constant_args={0: [ - Primitive(self.tw.lc.prim_start, call_me=False, + self.tw.lc.def_prim( + 'start', 0, + Primitive(Primitive.group, arg_descs=[ConstantArg([ + Primitive(self.tw.lc.prim_start, export_me=False), Primitive(self.tw.lc.prim_define_stack, - constant_args={0: 'start'}, call_me=False)]})) + arg_descs=[ConstantArg('start')])])])) palette.add_block('string', style='box-style', @@ -986,9 +1036,14 @@ buttons')) default=_('action'), logo_command='to action', help_string=_('top of nameable action stack')) - self.tw.lc.def_prim('nop3', 1, Primitive(self.tw.lc.prim_define_stack)) + self.tw.lc.def_prim( + 'nop3', 1, + Primitive(self.tw.lc.prim_define_stack, + arg_descs=[ArgSlot(TYPE_STRING)])) - primitive_dictionary['stack'] = Primitive(self.tw.lc.prim_invoke_stack) + primitive_dictionary['stack'] = Primitive( + self.tw.lc.prim_invoke_stack, + arg_descs=[ArgSlot(TYPE_STRING)]) palette.add_block('stack', style='basic-style-1arg', label=_('action'), @@ -997,10 +1052,8 @@ buttons')) logo_command='action', default=_('action'), help_string=_('invokes named action stack')) - self.tw.lc.def_prim('stack', 1, - Primitive(self.tw.lc.prim_invoke_stack), True) + self.tw.lc.def_prim('stack', 1, primitive_dictionary['stack'], True) - primitive_dictionary['setbox'] = Primitive(self.tw.lc.prim_set_box) palette.add_block('storeinbox1', hidden=True, style='basic-style-1arg', @@ -1010,8 +1063,10 @@ buttons')) string_or_number=True, logo_command='make "box1', help_string=_('stores numeric value in Variable 1')) - self.tw.lc.def_prim('storeinbox1', 1, - Primitive(self.tw.lc.prim_set_box, constant_args={0: 'box1'})) + self.tw.lc.def_prim( + 'storeinbox1', 1, + Primitive(self.tw.lc.prim_set_box, + arg_descs=[ConstantArg('box1'), ArgSlot(TYPE_OBJECT)])) palette.add_block('storeinbox2', hidden=True, @@ -1022,8 +1077,10 @@ buttons')) string_or_number=True, logo_command='make "box2', help_string=_('stores numeric value in Variable 2')) - self.tw.lc.def_prim('storeinbox2', 1, - Primitive(self.tw.lc.prim_set_box, constant_args={0: 'box2'})) + self.tw.lc.def_prim( + 'storeinbox2', 1, + Primitive(self.tw.lc.prim_set_box, + arg_descs=[ConstantArg('box2'), ArgSlot(TYPE_OBJECT)])) palette.add_block('box1', hidden=True, @@ -1033,8 +1090,10 @@ buttons')) logo_command=':box1', help_string=_('Variable 1 (numeric value)'), value_block=True) - self.tw.lc.def_prim('box1', 0, - Primitive(self.tw.lc.prim_get_box, constant_args={0: 'box1'})) + self.tw.lc.def_prim( + 'box1', 0, + Primitive(self.tw.lc.prim_get_box, return_type=TYPE_BOX, + arg_descs=[ConstantArg('box1')])) palette.add_block('box2', hidden=True, @@ -1044,9 +1103,14 @@ buttons')) logo_command=':box2', help_string=_('Variable 2 (numeric value)'), value_block=True) - self.tw.lc.def_prim('box2', 0, - Primitive(self.tw.lc.prim_get_box, constant_args={0: 'box2'})) + self.tw.lc.def_prim( + 'box2', 0, + Primitive(self.tw.lc.prim_get_box, return_type=TYPE_BOX, + arg_descs=[ConstantArg('box2')])) + primitive_dictionary['setbox'] = Primitive( + self.tw.lc.prim_set_box, + arg_descs=[ArgSlot(TYPE_OBJECT), ArgSlot(TYPE_OBJECT)]) palette.add_block('storein', style='basic-style-2arg', label=[_('store in'), _('box'), _('value')], @@ -1056,10 +1120,12 @@ buttons')) default=[_('my box'), 100], help_string=_('stores numeric value in named \ variable')) - self.tw.lc.def_prim('storeinbox', 2, - Primitive(self.tw.lc.prim_set_box)) + self.tw.lc.def_prim('storeinbox', 2, primitive_dictionary['setbox']) - primitive_dictionary['box'] = Primitive(self.tw.lc.prim_get_box) + primitive_dictionary['box'] = Primitive( + self.tw.lc.prim_get_box, + return_type=TYPE_BOX, + arg_descs=[ArgSlot(TYPE_OBJECT)]) palette.add_block('box', style='number-style-1strarg', hidden=True, @@ -1070,8 +1136,7 @@ variable')) logo_command='box', value_block=True, help_string=_('named variable (numeric value)')) - self.tw.lc.def_prim('box', 1, - Primitive(self.tw.lc.prim_get_box)) + self.tw.lc.def_prim('box', 1, primitive_dictionary['box']) palette.add_block('hat1', hidden=True, @@ -1080,9 +1145,10 @@ variable')) prim_name='nop1', logo_command='to stack1\n', help_string=_('top of Action 1 stack')) - self.tw.lc.def_prim('nop1', 0, + self.tw.lc.def_prim( + 'nop1', 0, Primitive(self.tw.lc.prim_define_stack, - constant_args={0: 'stack1'})) + arg_descs=[ConstantArg('stack1')])) palette.add_block('hat2', hidden=True, @@ -1091,9 +1157,10 @@ variable')) prim_name='nop2', logo_command='to stack2\n', help_string=_('top of Action 2 stack')) - self.tw.lc.def_prim('nop2', 0, + self.tw.lc.def_prim( + 'nop2', 0, Primitive(self.tw.lc.prim_define_stack, - constant_args={0: 'stack2'})) + arg_descs=[ConstantArg('stack2')])) palette.add_block('stack1', hidden=True, @@ -1102,9 +1169,10 @@ variable')) prim_name='stack1', logo_command='stack1', help_string=_('invokes Action 1 stack')) - self.tw.lc.def_prim('stack1', 0, + self.tw.lc.def_prim( + 'stack1', 0, Primitive(self.tw.lc.prim_invoke_stack, - constant_args={0: 'stack1'}), + arg_descs=[ConstantArg('stack1')]), True) palette.add_block('stack2', @@ -1114,9 +1182,10 @@ variable')) prim_name='stack2', logo_command='stack2', help_string=_('invokes Action 2 stack')) - self.tw.lc.def_prim('stack2', 0, + self.tw.lc.def_prim( + 'stack2', 0, Primitive(self.tw.lc.prim_invoke_stack, - constant_args={0: 'stack2'}), + arg_descs=[ConstantArg('stack2')]), True) def _trash_palette(self): @@ -1143,15 +1212,7 @@ variable')) label=_('clear all'), help_string=_('move all blocks to trash')) - # Block primitives - - def _prim_clear(self): - self.tw.lc.prim_clear() - self.tw.turtles.reset_turtles() - - def _prim_and(self, x, y): - ''' Logical and ''' - return x & y + # Callbacks to update labels after executing a block def after_arc(self, *ignored_args): if self.tw.lc.update_values: @@ -1167,57 +1228,7 @@ variable')) 'heading', self.tw.turtles.get_active_turtle().get_heading()) - def _prim_box(self, x): - ''' Retrieve value from named box ''' - if isinstance(convert(x, float, False), float): - if int(float(x)) == x: - x = int(x) - try: - return self.tw.lc.boxes['box3' + str(x)] - except KeyError: - raise logoerror("#emptybox") - - def _prim_forever(self, blklist): - ''' Do list forever ''' - while True: - self.tw.lc.icall(self.tw.lc.evline, blklist[:]) - yield True - if self.tw.lc.procstop: - break - self.tw.lc.ireturn() - yield True - - def convert_value_for_move(self, value): - ''' Perform type conversion and other preprocessing on the parameter, - so it can be passed to the 'move' primitive. ''' - if value is None: - return value - - def _convert_to_float(val): - if not _num_type(val): - raise logoerror("#notanumber") - return float(val) - - if isinstance(value, (tuple, list)): - (val1, val2) = value - val1_float = _convert_to_float(val1) - val2_float = _convert_to_float(val2) - value_converted = (val1_float, val2_float) - else: - value_converted = _convert_to_float(value) - return value_converted - - def _prim_move(self, cmd, value1, pendown=True, - reverse=False): - ''' Turtle moves by method specified in value1 ''' - - value1_conv = self.convert_value_for_move(value1) - - cmd(value1_conv, pendown=pendown) - - self.after_move() - - def after_move(self, *ignored_args): + def after_move(self, *ignored_args, **ignored_kwargs): ''' Update labels after moving the turtle ''' if self.tw.lc.update_values: self.tw.lc.update_label_value( @@ -1229,279 +1240,38 @@ variable')) self.tw.turtles.get_active_turtle().get_xy()[1] / self.tw.coord_scale) - def _prim_or(self, x, y): - ''' Logical or ''' - return x | y - - def _prim_repeat(self, num, blklist): - ''' Repeat list num times. ''' - if not _num_type(num): - raise logoerror("#notanumber") - num = self.tw.lc.int(num) - for i in range(num): - self.tw.lc.icall(self.tw.lc.evline, blklist[:]) - yield True - if self.tw.lc.procstop: - break - self.tw.lc.ireturn() - yield True - - def check_number(self, value): - ''' Check if value is a number. If yes, return the value. If no, - raise a logoerror. ''' - if not _num_type(value): - raise logoerror("#notanumber") - return value - - def check_non_negative(self, x, msg="#negroot"): - ''' Raise a logoerror iff x is negative. Otherwise, return x - unchanged. - msg -- the name of the logoerror message ''' - if x < 0: - raise logoerror(msg) - return x - - def check_non_zero(self, x, msg="#zerodivide"): - ''' Raise a logoerror iff x is zero. Otherwise, return x - unchanged. - msg -- the name of the logoerror message ''' - if x == 0: - raise logoerror(msg) - return x - def after_right(self, *ignored_args): if self.tw.lc.update_values: self.tw.lc.update_label_value( 'heading', self.tw.turtles.get_active_turtle().get_heading()) - def _prim_set(self, name, cmd, value=None): - ''' Set a value and update the associated value blocks ''' - if value is not None: - cmd(value) - if self.tw.lc.update_values: - self.tw.lc.update_label_value(name, value) - def after_set(self, name, value=None): ''' Update the associated value blocks ''' if value is not None: if self.tw.lc.update_values: self.tw.lc.update_label_value(name, value) - def _prim_setbox(self, name, x, val): - ''' Define value of named box ''' - if x is not None: - if isinstance(convert(x, float, False), float): - if int(float(x)) == x: - x = int(x) - self.tw.lc.boxes[name + str(x)] = val - if self.tw.lc.update_values: - self.tw.lc.update_label_value('box', val, label=x) - else: - self.tw.lc.boxes[name] = val - if self.tw.lc.update_values: - self.tw.lc.update_label_value(name, val) - - def _prim_stack(self, x): - ''' Process a named stack ''' - if isinstance(convert(x, float, False), float): - if int(float(x)) == x: - x = int(x) - if 'stack3' + str(x) not in self.tw.lc.stacks or \ - self.tw.lc.stacks['stack3' + str(x)] is None: - raise logoerror("#nostack") - self.tw.lc.icall(self.tw.lc.evline, - self.tw.lc.stacks['stack3' + str(x)][:]) - yield True - self.tw.lc.procstop = False - self.tw.lc.ireturn() - yield True - - def _prim_stack1(self): - ''' Process Stack 1 ''' - if self.tw.lc.stacks['stack1'] is None: - raise logoerror("#nostack") - self.tw.lc.icall(self.tw.lc.evline, - self.tw.lc.stacks['stack1'][:]) - yield True - self.tw.lc.procstop = False - self.tw.lc.ireturn() - yield True - - def _prim_stack2(self): - ''' Process Stack 2 ''' - if self.tw.lc.stacks['stack2'] is None: - raise logoerror("#nostack") - self.tw.lc.icall(self.tw.lc.evline, self.tw.lc.stacks['stack2'][:]) - yield True - self.tw.lc.procstop = False - self.tw.lc.ireturn() - yield True - - def _prim_stopstack(self): - ''' Stop execution of a stack ''' - self.tw.lc.procstop = True - - def _prim_wait(self, wait_time): - ''' Show the turtle while we wait ''' - self.tw.turtles.get_active_turtle().show() - endtime = _millisecond() + wait_time * 1000. - while _millisecond() < endtime: - sleep(wait_time / 10.) - yield True - self.tw.turtles.get_active_turtle().hide() - self.tw.lc.ireturn() - yield True - - # Math primitives - - def _prim_careful_divide(self, x, y): - ''' Raise error on divide by zero ''' - if isinstance(x, list) and _num_type(y): - z = [] - for i in range(len(x)): - try: - z.append(x[i] / y) - except ZeroDivisionError: - raise logoerror("#zerodivide") - return z - try: - return x / y - except ZeroDivisionError: - raise logoerror("#zerodivide") - except TypeError: - try: - return self._string_to_num(x) / self._string_to_num(y) - except ZeroDivisionError: - raise logoerror("#zerodivide") - except ValueError: - raise logoerror("#syntaxerror") - except TypeError: - raise logoerror("#notanumber") - - def _prim_plus(self, x, y): - ''' Add numbers, concat strings ''' - if isinstance(x, Color): - x = int(x) - if isinstance(y, Color): - y = int(y) - if _num_type(x) and _num_type(y): - return(x + y) - elif isinstance(x, list) and isinstance(y, list): - z = [] - for i in range(len(x)): - z.append(x[i] + y[i]) - return(z) - else: - if _num_type(x): - xx = str(round_int(x)) - else: - xx = str(x) - if _num_type(y): - yy = str(round_int(y)) - else: - yy = str(y) - return(xx + yy) - - def _prim_minus(self, x, y): - ''' Numerical subtraction ''' - if _num_type(x) and _num_type(y): - return(x - y) - elif isinstance(x, list) and isinstance(y, list): - z = [] - for i in range(len(x)): - z.append(x[i] - y[i]) - return(z) - try: - return self._string_to_num(x) - self._string_to_num(y) - except TypeError: - raise logoerror("#notanumber") - - def _prim_product(self, x, y): - ''' Numerical multiplication ''' - if _num_type(x) and _num_type(y): - return(x * y) - elif isinstance(x, list) and _num_type(y): - z = [] - for i in range(len(x)): - z.append(x[i] * y) - return(z) - elif isinstance(y, list) and _num_type(x): - z = [] - for i in range(len(y)): - z.append(y[i] * x) - return(z) - try: - return self._string_to_num(x) * self._string_to_num(y) - except TypeError: - raise logoerror("#notanumber") - - def _prim_mod(self, x, y): - ''' Numerical mod ''' - if _num_type(x) and _num_type(y): - return(x % y) - try: - return self._string_to_num(x) % self._string_to_num(y) - except TypeError: - raise logoerror("#notanumber") - except ValueError: - raise logoerror("#syntaxerror") - - def _prim_random(self, x, y): - ''' Random integer ''' - if _num_type(x) and _num_type(y): - return(int(round(uniform(x, y), 0))) - xx, xflag = chr_to_ord(x) - yy, yflag = chr_to_ord(y) - if xflag and yflag: - return chr(int(round(uniform(xx, yy), 0))) - if not xflag: - xx = self._string_to_num(x) - if not yflag: - yy = self._string_to_num(y) - try: - return(int(round(uniform(xx, yy), 0))) - except TypeError: - raise logoerror("#notanumber") - # Utilities - def _string_to_num(self, x): - ''' Try to convert a string to a number ''' - if isinstance(x, (int, float)): - return(x) - try: - return int(ord(x)) - except TypeError: - pass - if isinstance(x, list): - raise logoerror("#syntaxerror") - if isinstance(x, Color): - return int(x) - xx = convert(x.replace(self.tw.decimal_point, '.'), float) - if isinstance(xx, float): - return xx - else: - xx, xflag = chr_to_ord(x) - if xflag: - return xx - else: - raise logoerror("#syntaxerror") - - def _make_constant(self, palette, block_name, label, constant): + def _make_constant(self, palette, block_name, label, constant_key): ''' Factory for constant blocks ''' + constant = CONSTANTS[constant_key] if isinstance(constant, Color): if constant.color is not None: - value = str(constant.color) + logo_command = str(constant.color) else: # Black or White - value = '0 tasetshade %d' % (constant.shade) + logo_command = '0 tasetshade %d' % (constant.shade) + return_type = TYPE_COLOR else: - value = constant + logo_command = constant + return_type = TYPE_NUMBER palette.add_block(block_name, style='box-style', label=label, prim_name=block_name, - logo_command=value) + logo_command=logo_command) self.tw.lc.def_prim(block_name, 0, - Primitive(Primitive.identity, constant_args={0: constant})) + Primitive(CONSTANTS.get, return_type=return_type, + arg_descs=[ConstantArg(constant_key)])) |