Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/doc/primitives-with-arguments.md
blob: 22378985cdff2d0b62d9a6e9f8899956d9ba1b1c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
How to define Primitive objects for blocks with arguments
=========================================================

The tutorials in this document assume that the reader is able to
add simple blocks without arguments to Turtle Art.  Please refer
to the module documentation  of  ../TurtleArt/tabasics.py  for a
tutorial on that.

Example 1: Block with one Argument
----------------------------------

In this example,   we define the `Primitive` object  for a block
that increases the pen color  by a numeric argument   that comes
from another block. In Turtle Art, the block looks like this:

     ,---.___,---------.
    /                   |
    | increment color |=
    \                   |
     `---.___,---------´

When the block  is executed,   we want it to do the same  as the
following statement:

    Turtle.set_pen_color(plus(Turtle.get_pen_color(), ...))

where `...` stands for the output of the block  connected to the
right hand  dock  of  our block.   For arguments  not  known  in
advance,   we need  to insert  a placeholder  in the form  of an
`ArgSlot` object.  An `ArgSlot` object describes some properties
of  the  argument  it  receives.   It  defines  the type  of the
argument,  it knows whether the argument needs to be called  (if
it is callable),  and it knows which callable  (if any)  it must
wrap around the argument before consuming it.  (For more on slot
wrappers,  please refer to the other examples below.)   For this
example,  we can use the default values for the second and third
property  (`True` and `None`,  respectively).   We only need  to
state the first one, the argument type, explicitly:

    prim_inc_color = Primitive(Turtle.set_pen_color,
        arg_descs=[ConstantArg(Primitive(
            Primitive.plus, return_type=TYPE_NUMBER,
                arg_descs=[ConstantArg(Primitive(
                    Turtle.get_pen_color, return_type=TYPE_NUMBER)),
                ArgSlot(TYPE_NUMBER)]))])

    self.tw.lc.def_prim('inc_color', 0, prim_inc_color)

Turtle Art  uses the same type system for argument types  as for
the return types of Primitive objects. If a value block (such as
the number block)   is attached  to  the right hand dock  of the
'increment color' block,   then Turtle Art  matches the value of
that block against the type requirement of the argument slot. If
the attached block  has a Primitive object  (such as  the 'plus'
block),   then that Primitive's return value  is matched against
the required type. If Turtle Art doesn't know how to convert the
attached value to the required type,  it shows the user an error
message during execution.


Example 2: Block with a Slot Wrapper
------------------------------------

In Turtle Art,  moving the turtle backward by x  is the same  as
moving it forward  by negative x (or -x).   In fact,  the 'back'
block uses the same method  (`Turtle.forward`)  as the 'forward'
block.   But the 'back' block  needs to switch  the sign  of its
argument before passing it to `Turtle.forward`. I.e. it needs to
execute the following statement:

    Turtle.forward(minus(...))

where `...` again  stands for the output of the block  connected
to the right hand dock of the 'back' block.   This is where slot
wrappers come in helpful.  A slot wrapper is a Primitive that is
'wrapped around'  an argument  of its 'parent' Primitive.   Slot
wrappers can only be attached to `ArgSlot` objects,  that is, to
arguments that come from other blocks. In the case of the 'back'
block, this looks as follows:

    Primitive(Turtle.forward,
        arg_descs=[ArgSlot(TYPE_NUMBER,
            wrapper=Primitive(
                Primitive.minus, return_type=TYPE_NUMBER,
                    arg_descs=[ArgSlot(TYPE_NUMBER)]))],
        call_afterwards=self.after_move))

When the 'back' block is called,  it passes the argument that it
gets from its right hand dock to the `ArgSlot` object.  That, in
turn, passes it to its wrapper, and then matches the type of the
return value of the wrapper against its type requirement. If the
types match,  the wrapper's return value  is passed back  to the
function of the main Primitive, `Turtle.forward`.

Note  that  slot wrappers  and  Primitive objects  can be nested
inside each other infinitely deeply.


Example 3: Block with a Group of Primitives
-------------------------------------------

Blocks  like the 'clean' block  need to do  several things  in a
row.  E.g., the 'clean' block needs to tell the plugins that the
screen is being cleared, it needs to stop media execution, clear
the screen, and reset all turtles.  It takes no block arguments,
so it looks like this in Turtle Art:

     ,---.___,---.
    /             \
    |   clean     |
    \             /
     `---.___,---´

To execute a series  of several Primitives,  we need to define a
'group' of Primitives. This 'group' is itself a Primitive, using
the special function `Primitive.group`.   When called,  it loops
over its arguments  and calls them successively.   The Primitive
object for the 'clean' block looks like this:

    Primitive(Primitive.group, arg_descs=[ConstantArg([
        Primitive(self.tw.clear_plugins),
        Primitive(self.tw.lc.prim_clear_helper,
                  export_me=False),
        Primitive(self.tw.canvas.clearscreen),
        Primitive(self.tw.turtles.reset_turtles)])])