Modify section 24.

This commit is contained in:
Toshio Sekiya 2021-06-27 21:20:18 +09:00
parent 41acf318fc
commit febe14a151
8 changed files with 216 additions and 150 deletions

View file

@ -3,7 +3,7 @@ Up: [Readme.md](../Readme.md), Prev: [Section 23](sec23.md), Next: [Section 25]
# Tiny turtle graphics interpreter # Tiny turtle graphics interpreter
A program `turtle` is an example with the combination of TfeTextView and GtkDrawingArea objects. A program `turtle` is an example with the combination of TfeTextView and GtkDrawingArea objects.
It is a very small interpreter but it provides a way to draw fractal curves. It is a very small interpreter but it provides a tool to draw fractal curves.
The following diagram is a Koch curve, which is a famous example of fractal curves. The following diagram is a Koch curve, which is a famous example of fractal curves.
![Koch curve](../src/turtle/image/turtle_koch.png) ![Koch curve](../src/turtle/image/turtle_koch.png)
@ -50,6 +50,18 @@ You can read these files into `turtle` editor by clicking on the `Open` button.
Turtle uses TfeTextView and GtkDrawingArea. Turtle uses TfeTextView and GtkDrawingArea.
It is similar to `color` program in the previous section. It is similar to `color` program in the previous section.
1. A user inputs/reads a turtle program into the buffer in the TfeTextView instance.
2. The user clicks on the "Run" button.
3. The parser reads the program and generates tree-structured data.
4. The interpriter reads the data and executes it step by step.
And it draws shapes on a surface.
The surface is different from the surface of the GtkDrawingArea widget.
5. The widget is added to the queue.
It will be redrawn with the drawing function.
The function just copies the surface, which is drawn by the interpreter, into the surface of the GtkDrawingArea.
![Parser, interpreter and drawing function](../image/turtle.png)
The body of the interpreter is written with flex and bison. The body of the interpreter is written with flex and bison.
The codes are not thread safe. The codes are not thread safe.
So the handler of "clicked" signal of the `Run` button prevents from reentering. So the handler of "clicked" signal of the `Run` button prevents from reentering.
@ -79,37 +91,42 @@ So the handler of "clicked" signal of the `Run` button prevents from reentering.
22 } 22 }
23 finalize_flex (); 23 finalize_flex ();
24 } 24 }
25 gtk_widget_queue_draw (GTK_WIDGET (da)); 25 g_free (contents);
26 busy = FALSE; 26 gtk_widget_queue_draw (GTK_WIDGET (da));
27 } 27 busy = FALSE;
28 28 }
29 static void 29
30 resize_cb (GtkDrawingArea *drawing_area, int width, int height, gpointer user_data) { 30 static void
31 if (surface) 31 resize_cb (GtkDrawingArea *drawing_area, int width, int height, gpointer user_data) {
32 cairo_surface_destroy (surface); 32 if (surface)
33 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); 33 cairo_surface_destroy (surface);
34 } 34 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
35 }
~~~ ~~~
- 8-13: The static value `busy` holds a status of the interpreter. - 8-13: The static value `busy` holds a status of the interpreter.
If it is `TRUE`, the interpreter is running and it is not possible to call the interpreter again because it's not a re-entrant program. If it is `TRUE`, the interpreter is running and it is not possible to call the interpreter again because it's not a re-entrant program.
If it is `FALSE`, it is safe to call the interpreter. If it is `FALSE`, it is safe to call the interpreter.
- 14: Now it is about to call the interpreter so changes `busy` to be TRUE. - 14: Now it is about to call the interpreter so it changes `busy` to TRUE.
- 15-16: Gets the contents of GtkTextBuffer. - 15-16: Gets the contents of `tb`.
- 17: The variable `surface` is a static variable. - 17: The variable `surface` is a static variable.
It points to a `cairo_surface_t` object. It points to a `cairo_surface_t` instance.
It is generated when the GtkDrawingArea object is realized and whenever it is resized. It is created when the GtkDrawingArea instance is realized and whenever it is resized.
Therefore, `surface` isn't NULL usually. Therefore, `surface` isn't NULL usually.
But if it is NULL, the interpreter won't be called. But if it is NULL, the interpreter won't be called.
- 18: Initializes lexical analyzer. - 18: Initializes lexical analyzer.
- 19: Calls parser. - 19: Calls parser.
Parser analyze the program codes syntactically and generate a tree structured data. Parser analyzes the program codes syntactically and generate a tree structured data.
- 20-22: If the parser successfully parsed, it calls `run` (runtime routine). - 20-22: If the parser successfully parsed, it calls `run` (runtime routine).
- 23: finalize the lexical analyzer. - 23: finalizes the lexical analyzer.
- 25: Add the drawing area object to the queue to draw. - 25: frees `contents`.
- 26: The interpreter program has finished so `busy` is now FALSE. - 26: Adds the drawing area widget to the queue to draw.
- 27: The interpreter program has finished so `busy` is now changed to FALSE.
- 29-34: A handler of "resized" signal. - 29-34: A handler of "resized" signal.
It generates or regenerates a surface object. If `surface` isn't NULL, it destroys the old surface.
Then it creates a new surface.
Its size is the same as the surface of the GtkDrawingArea instance.
Other part of `turtleapplication.c` is almost same as the codes of `colorapplication.c` in the previous section. Other part of `turtleapplication.c` is almost same as the codes of `colorapplication.c` in the previous section.
The codes of `turtleapplication.c` is in the [turtle directory](../src/turtle). The codes of `turtleapplication.c` is in the [turtle directory](../src/turtle).
@ -127,8 +144,8 @@ The turtle recognizes the program above and works as follows.
- Generally, a program consists of tokens. - Generally, a program consists of tokens.
Tokens are "distance", "=", "100", "fd", "*" and "2" in the above example.. Tokens are "distance", "=", "100", "fd", "*" and "2" in the above example..
- The parser calls `yylex` to read a token in the source file. - The parser calls a function `yylex` to read a token in the source file.
The `yylex` returns a code which is called "token kind" and sets a global variable `yylval` with a value, which is called a semantic value. `yylex` returns a code which is called "token kind" and sets a global variable `yylval` with a value, which is called a semantic value.
The type of `yylval` is union and `yylval.ID` is string and `yylval.NUM` is double. The type of `yylval` is union and `yylval.ID` is string and `yylval.NUM` is double.
There are seven tokens in the program so `yylex` is called seven times. There are seven tokens in the program so `yylex` is called seven times.
@ -151,7 +168,7 @@ This part of `turtle` is called parser.
![turtle parser tree](../image/turtle_parser_tree.png) ![turtle parser tree](../image/turtle_parser_tree.png)
- `turtle` analyzes the tree and executes it. - `turtle` analyzes the tree and executes it.
This part of `turtle` is called runtime routine. This part of `turtle` is called runtime routine or interpreter.
The tree consists of rectangles and line segments between the rectangles. The tree consists of rectangles and line segments between the rectangles.
The rectangles are called nodes. The rectangles are called nodes.
For example, N\_PROGRAM, N\_ASSIGN, N\_FD and N\_MUL are nodes. For example, N\_PROGRAM, N\_ASSIGN, N\_FD and N\_MUL are nodes.
@ -175,19 +192,22 @@ Multiplies 100 by 2 and gets 200.
Then `turtle` goes back to N_FD. Then `turtle` goes back to N_FD.
6. Now `turtle` knows the distance is 200. 6. Now `turtle` knows the distance is 200.
It moves the cursor forward by 200 pixels. It moves the cursor forward by 200 pixels.
The segment is drawn on the surface (`surface`).
8. There are no node follows. 8. There are no node follows.
Runtime routine returns to the main routine. Runtime routine returns to the function `run_cb`.
- `turtle` draws a segment on GtkDrawingArea then stops. - `run_cb` calls `gtk_widget_queue_draw` and put the GtkDrawingArea widget to the queue.
- The system redraws the widget.
At that time drawing function `draw_func` is called.
The function copies the surface (`surface`) to the surface in the GtkDrawingArea.
Most turtle programs are more complicated than the example above. Actual turtle program is more complicated than the example above.
So, `turtle` does much more work to interpret programs. However, what turtle does is basically the same.
However, basically it works by the same way above.
Interpretation consists of three parts. Interpretation consists of three parts.
- Lexical analysis - Lexical analysis
- Syntax Parsing and tree generation - Syntax Parsing and tree generation
- Interpret the tree and execute commands. - Interpretation and execution of the tree.
## Compilation flow ## Compilation flow
@ -260,7 +280,8 @@ The argument `turtleparser[1]` refers to `tirtle_parser.h` which is the second o
### What does flex do? ### What does flex do?
Flex creates lexical analyzer from flex source file. Flex creates lexical analyzer from flex source file.
Flex source file is a text file and its syntactic rule will be explained later. Flex source file is a text file.
Its syntactic rule will be explained later.
Generated lexical analyzer is a C source file. Generated lexical analyzer is a C source file.
It is also called scanner. It is also called scanner.
It reads a text file, which is a source file of a program language, and gets variable names, numbers and symbols. It reads a text file, which is a source file of a program language, and gets variable names, numbers and symbols.
@ -280,7 +301,7 @@ The words `fc`, `pd`, `distance`, `angle`, `tr`, `1`, `0`, `100` and `90` are ca
The characters '`(`' (left parenthesis), '`,`' (comma), '`)`' (right parenthesis) and '`=`' (equal sign) are called symbols. The characters '`(`' (left parenthesis), '`,`' (comma), '`)`' (right parenthesis) and '`=`' (equal sign) are called symbols.
( Sometimes those symbols called tokens, too.) ( Sometimes those symbols called tokens, too.)
Flex reads `turtle.lex` and generates a scanner. Flex reads `turtle.lex` and generates the C source file of a scanner.
The file `turtle.lex` specifies tokens, symbols and the behavior which corresponds to each token or symbol. The file `turtle.lex` specifies tokens, symbols and the behavior which corresponds to each token or symbol.
Turtle.lex isn't a big program. Turtle.lex isn't a big program.
@ -368,8 +389,6 @@ They are definitions, rules and user code sections.
### Definitions section ### Definitions section
First, look at the definitions section.
- 1-12: Lines between "%top{" and "}" are C source codes. - 1-12: Lines between "%top{" and "}" are C source codes.
They will be copied to the top of the generated C source file. They will be copied to the top of the generated C source file.
- 2-3: The function `strlen`, in line 62, is defined in `string.h` - 2-3: The function `strlen`, in line 62, is defined in `string.h`
@ -390,6 +409,10 @@ You can leave out such definitions here and use regular expressions in rules sec
This section is the most important part. This section is the most important part.
Rules consist of patterns and actions. Rules consist of patterns and actions.
The patterns are regular expressions or names surrounded by braces.
The names must be defined in the definitions section.
The definition of the regular expression is written in the flex documentation.
For example, line 37 is a rule. For example, line 37 is a rule.
- `{REAL_NUMBER}` is a pattern - `{REAL_NUMBER}` is a pattern
@ -407,23 +430,23 @@ The scanner generated by flex and C compiler has `yylex` function.
If `yylex` is called and the input is "123.4", then it works as follows. If `yylex` is called and the input is "123.4", then it works as follows.
1. A string "123.4" matches `{REAL_NUMBER}`. 1. A string "123.4" matches `{REAL_NUMBER}`.
2. Update the location variable `ncolumn` and `yylloc`. 2. Update the location variable `ncolumn` and `yylloc`with `get_location`.
3. `atof` converts the string "123.4" to double sized floating point number 123.4. 3. `atof` converts the string "123.4" to double type number 123.4.
4. It is assigned to `yylval.NUM`. 4. It is assigned to `yylval.NUM`.
5. `yylex` returns `NUM` to the caller. 5. `yylex` returns `NUM` to the caller.
Then the caller knows the input is `NUM` (number), and its value is 123.4. Then the caller knows the input is `NUM` (number), and its value is 123.4.
- 19-55: Rules section. - 19-55: Rules section.
- 20: Comment begins `#` followed by any characters except newline. - 20: The symbol `.` (dot) matches any character except newline.
Therefore, a comment begins `#` followed by any characters except newline.
No action happens. No action happens.
- 21: White space just increases a variable `ncolumn` by one. - 21: White space just increases a variable `ncolumn` by one.
- 22: Tab is assumed to be equal to eight spaces. - 22: Tab is assumed to be equal to eight spaces.
- 23: New line increases a variable `nline` by one and resets `ncolumn`. - 23: New line increases a variable `nline` by one and resets `ncolumn`.
- 25-35: Keywords just updates the location variables `ncolumn` and `yylloc`, and return the codes of the keywords. - 25-35: Keywords just updates the location variables `ncolumn` and `yylloc`, and return the codes of the keywords.
- 37: Real number constant. - 37: Real number constant.
- 38: Identifier is defined in line 17. - 38: `IDENTIFIER` is defined in line 17.
It begins alphabet followed by zero or more alphabet or digit.
The location variables are updated and the name of the identifier is assigned to `yylval.ID`. The location variables are updated and the name of the identifier is assigned to `yylval.ID`.
The memory of the name is allocated by the function `g_strdup`. The memory of the name is allocated by the function `g_strdup`.
The memory is registered to the list (GSlist type list). The memory is registered to the list (GSlist type list).
@ -440,11 +463,10 @@ This section is just copied to C source file.
- 58-63: A function `get_location`. - 58-63: A function `get_location`.
The location of the input is recorded to `nline` and `ncolumn`. The location of the input is recorded to `nline` and `ncolumn`.
These two variables are for the scanner. A variable `yylloc` is referred by the parser.
A variable `yylloc` is shared by the scanner and the parser.
It is a C structure and has four members, `first_line`, `first_column`, `last_line` and `last_column`. It is a C structure and has four members, `first_line`, `first_column`, `last_line` and `last_column`.
They point the start and end of the current input text. They point the start and end of the current input text.
- 65: `YY_BUFFER_STATE` is a type of the pointer points the input buffer. - 65: `YY_BUFFER_STATE` is a pointer points the input buffer.
- 67-70: `init_flex` is called by `run_cb` signal handler, which is called when `Run` button is clicked on. - 67-70: `init_flex` is called by `run_cb` signal handler, which is called when `Run` button is clicked on.
`run_cb` calls `init_flex` with one argument which is the copy of the content of GtkTextBuffer. `run_cb` calls `init_flex` with one argument which is the copy of the content of GtkTextBuffer.
`yy_scan_string` sets the input buffer to read from the text. `yy_scan_string` sets the input buffer to read from the text.
@ -505,7 +527,7 @@ It doesn't return any values.
Programmers can define their own procedures. Programmers can define their own procedures.
On the other hand, `fc` is a built-in procedure. On the other hand, `fc` is a built-in procedure.
Such procedures are called primary procedures. Such procedures are called primary procedures.
It is described in Bison source code like: It is described in bison source code like:
~~~ ~~~
primary_procedure: FC '(' expression ',' expression ',' expression ')'; primary_procedure: FC '(' expression ',' expression ',' expression ')';
@ -526,8 +548,7 @@ The first line is:
FC '(' NUM ',' NUM ',' NUM ')'; FC '(' NUM ',' NUM ',' NUM ')';
~~~ ~~~
You can find this is a primary_procedure easily. The parser analyzes the turtle source code and if the input matches the definition above, the parser recognizes it as a primary procedure.
The parser of the turtle language analyzes the turtle source code in the same way.
The grammar of turtle is described in the [document](turtle_doc.md). The grammar of turtle is described in the [document](turtle_doc.md).
The following is an extract from the document. The following is an extract from the document.
@ -850,7 +871,7 @@ This type is shared by the scanner file and the parser implementation file.
The error report function `yyerror` uses it so that it can inform the location that error occurs. The error report function `yyerror` uses it so that it can inform the location that error occurs.
`%define api.value.type union` generates semantic value type with tokens and nterms and inserts it to the header file. `%define api.value.type union` generates semantic value type with tokens and nterms and inserts it to the header file.
The inserted part is shown in the previous section as the extracts that shows the value type (YYSTYPE). The inserted part is shown in the previous subsection as the extracts that shows the value type (YYSTYPE).
`%token` and `%nterm` directives define tokens and non terminal symbols respectively. `%token` and `%nterm` directives define tokens and non terminal symbols respectively.
@ -977,7 +998,7 @@ There's no action specified.
Then, the default action is executed. Then, the default action is executed.
It is ` $$ = $1`. It is ` $$ = $1`.
- `primary_procedure` is `FD` followed by expression. - `primary_procedure` is `FD` followed by expression.
The action calls `tree1` and assign its return value to `$$`. The action calls `tree1` and assigns its return value to `$$`.
`tree1` makes a tree node. `tree1` makes a tree node.
The tree node has type and union of three pointers to children nodes, string or double. The tree node has type and union of three pointers to children nodes, string or double.
~~~ ~~~
@ -989,7 +1010,7 @@ node --+-- type
~~~ ~~~
- `tree1` assigns the four arguments to type, child1, child2 and child3 members. - `tree1` assigns the four arguments to type, child1, child2 and child3 members.
- `expression` is `NUM`. - `expression` is `NUM`.
- `tree2` assigns the two arguments to type and a double member. - `tree2` makes a tree node. The paremeters of `tree2` are a type and a semantic value.
Suppose the parser reads the following program. Suppose the parser reads the following program.
@ -1098,15 +1119,15 @@ expression:
### Epilogue ### Epilogue
The epilogue is written in C language and copied to the parser implementation file. The epilogue is written in C language and copied to the parser implementation file.
Generally, you can put anything into epilogue. Generally, you can put anything into the epilogue.
In the case of turtle interpreter, the runtime routine and some other functions are in the epilogue. In the case of turtle interpreter, the runtime routine and some other functions are in the epilogue.
#### Functions to generate tree nodes #### Functions to create tree nodes
There are three functions, `tree1`, `tree2` and `tree3`. There are three functions, `tree1`, `tree2` and `tree3`.
- `tree1` creates a node and sets the node type and pointers to its three children (NULL is possible). - `tree1` creates a node and sets the node type and pointers to its three children (NULL is possible).
- `tree2` creates a node and sets the node type and a value. - `tree2` creates a node and sets the node type and a value (double).
- `tree3` creates a node and sets the node type and a pointer to a string. - `tree3` creates a node and sets the node type and a pointer to a string.
Each function gets memories first and build a node on them. Each function gets memories first and build a node on them.
@ -1313,7 +1334,8 @@ We can know the amount of elements used in the array during the runtime.
The purpose of the variable is to find appropriate `MAX_STACK_SIZE`. The purpose of the variable is to find appropriate `MAX_STACK_SIZE`.
It will be unnecessary in the future version if the stack is implemented with better data structure and memory allocation. It will be unnecessary in the future version if the stack is implemented with better data structure and memory allocation.
The runtime routine push data to the stack when it executes a procedure call node. The runtime routine push data to the stack when it executes a node of a procedure call.
(The type of the node is `N_procedure_call`.)
~~~ ~~~
dp drawline (angle, distance) { ... ... ... } dp drawline (angle, distance) { ... ... ... }
@ -1353,7 +1375,7 @@ It searches only the parameters of the latest procedure.
It returns TRUE and sets the argument `value` to point the value, if the variable has been found. It returns TRUE and sets the argument `value` to point the value, if the variable has been found.
Otherwise it returns FALSE. Otherwise it returns FALSE.
- `stack_replace` replaces the value of the variable in the stack. - `stack_replace` replaces the value of the variable in the stack.
If it successes, it returns TRUE. Otherwise returns FALSE. If it succeeds, it returns TRUE. Otherwise returns FALSE.
- `stack_return` throws away the latest parameters. - `stack_return` throws away the latest parameters.
The stack pointer goes back to the point before the latest procedure call so that it points to parameters of the previous called procedure. The stack pointer goes back to the point before the latest procedure call so that it points to parameters of the previous called procedure.
@ -1430,7 +1452,9 @@ It is initialized in `turtleapplication.c`.
The runtime routine has its own cairo context. The runtime routine has its own cairo context.
This is different from the cairo of GtkDrawingArea. This is different from the cairo of GtkDrawingArea.
Runtime routine draws a shape on the `surface` with the cairo context. Runtime routine draws a shape on the `surface` with the cairo context.
After runtime routine returns to `run_cb`, the drawing function `draw_func` copies the `surface` to the surface in the GtkDrawingArea object. After runtime routine returns to `run_cb`, `run_cb` adds the GtkDrawingArea widget to the queue to redraw.
When the widget is redraw,the drawing function `draw_func` is called.
It copies the `surface` to the surface in the GtkDrawingArea object.
`turtle.y` has two functions `init_cairo` and `destroy_cairo`. `turtle.y` has two functions `init_cairo` and `destroy_cairo`.
@ -1492,6 +1516,8 @@ static cairo_t *cr;
gboolean gboolean
init_cairo (void) { init_cairo (void) {
int width, height; int width, height;
cairo_matrix_t matrix;
pen = TRUE; pen = TRUE;
angle = 90.0; angle = 90.0;
cur_x = 0.0; cur_x = 0.0;
@ -1500,13 +1526,12 @@ init_cairo (void) {
bc.red = 0.95; bc.green = 0.95; bc.blue = 0.95; bc.red = 0.95; bc.green = 0.95; bc.blue = 0.95;
fc.red = 0.0; fc.green = 0.0; fc.blue = 0.0; fc.red = 0.0; fc.green = 0.0; fc.blue = 0.0;
if (surface) {
width = cairo_image_surface_get_width (surface); width = cairo_image_surface_get_width (surface);
height = cairo_image_surface_get_height (surface); height = cairo_image_surface_get_height (surface);
cairo_matrix_t matrix;
matrix.xx = 1.0; matrix.xy = 0.0; matrix.x0 = (double) width / 2.0; matrix.xx = 1.0; matrix.xy = 0.0; matrix.x0 = (double) width / 2.0;
matrix.yx = 0.0; matrix.yy = -1.0; matrix.y0 = (double) height / 2.0; matrix.yx = 0.0; matrix.yy = -1.0; matrix.y0 = (double) height / 2.0;
if (surface) {
cr = cairo_create (surface); cr = cairo_create (surface);
cairo_transform (cr, &matrix); cairo_transform (cr, &matrix);
cairo_set_source_rgb (cr, bc.red, bc.green, bc.blue); cairo_set_source_rgb (cr, bc.red, bc.green, bc.blue);
@ -1587,7 +1612,7 @@ double value = 0.0;
#### Execute function #### Execute function
Primary procedures and procedure definitions are analyzed and carried out by a function `execute`. Primary procedures and procedure definitions are analyzed and executed by the function `execute`.
It doesn't return any values. It doesn't return any values.
It calls itself recursively. It calls itself recursively.
The process of `N_RT` and `N_procedure_call` is complicated. The process of `N_RT` and `N_procedure_call` is complicated.
@ -1783,7 +1808,7 @@ When the parser reads the fifth line in the example, it creates nodes like this:
When the runtime routine meets `N_procedure_call` node, it behaves like this: When the runtime routine meets `N_procedure_call` node, it behaves like this:
1. Searches the symbol table for the procedure by the name. 1. Searches the symbol table for the procedure with the name.
2. Gets pointers to the node to parameters and the node to the body. 2. Gets pointers to the node to parameters and the node to the body.
3. Creates a temporary stack. 3. Creates a temporary stack.
Makes a tuple of each parameter name and argument value. Makes a tuple of each parameter name and argument value.

View file

@ -83,14 +83,14 @@ fd 100
The command `tr` is "Turn Right". The command `tr` is "Turn Right".
The argument is angle with degrees. The argument is angle with degrees.
Therefore, `tr 90` means "Turn right by 90 degrees". Therefore, `tr 90` means "Turn right by 90 degrees".
If you click on `run`button, then two line segment appears. If you click on the `run`button, then two line segments appears.
One is vertical and the other is horizontal. One is vertical and the other is horizontal.
![Two line segments on the surface](../src/turtle/image/turtle2.png) ![Two line segments on the surface](../src/turtle/image/turtle2.png)
## Background and foreground color ## Background and foreground color
Colors are specified by RGB. Colors are specified with RGB.
A vector (r, g, b) denotes RGB color. A vector (r, g, b) denotes RGB color.
Each of the elements is a real number between 0 and 1. Each of the elements is a real number between 0 and 1.
@ -129,7 +129,7 @@ Statements are executed in the order from the top to the end
## Comment and spaces ## Comment and spaces
Characters between `#` (hash mark) and `\n` (new line) inclusive are comment. Characters between `#` (hash mark) and `\n` (new line) inclusive are comment.
Characters between `#` and `EOF` (end of file) are also comment. If the comment is at the end of the file, the trailing new line can be left out.
Comments are ignored. Comments are ignored.
~~~ ~~~
@ -141,7 +141,7 @@ tr 120<NEW LINE>
fd 100 # Now a triangle appears.<EOF> fd 100 # Now a triangle appears.<EOF>
~~~ ~~~
\<NEW LINE\> and \<EOF\> are newline code and end of file respectively. \<NEW LINE\> and \<EOF\> indicate newline code and end of file respectively.
The comments in the line 1, 2, 3 and 6 are correct syntactically. The comments in the line 1, 2, 3 and 6 are correct syntactically.
Spaces (white space, tab and new line) are ignored. Spaces (white space, tab and new line) are ignored.
@ -150,14 +150,15 @@ Tabs are recognized as eight spaces to calculate the column number.
## Variables and expressions ## Variables and expressions
Variable begins alphabet followed by alphabet or digit except key words like `fd`, `tr` and so on. Variable begins alphabet followed by alphabet or digit.
`Distance` and `angle5` can be variables, but `1step` isn't a variable because the first character isn't alphabet. Key words like `fd`, `tr` can't be variables.
`Distance` and `angle5` are variables, but `1step` isn't a variable because the first character isn't alphabet.
Variable names are case sensitive. Variable names are case sensitive.
Variables keep real numbers. Variables keep real numbers.
Their type is the same as `double` in C language. Their type is the same as `double` in C language.
Integers are casted to real numbers automatically. Integers are casted to real numbers automatically.
So 1 and 1.0 are the same value. So 1 and 1.0 are the same value.
Numbers begins digits, not signs (`+` or `-`). Numbers begin digits, not signs (`+` or `-`).
- 100, 20.34 and 0.01 are numbers - 100, 20.34 and 0.01 are numbers
- +100 isn't a number. It causes syntax error. Use 100 instead. - +100 isn't a number. It causes syntax error. Use 100 instead.
@ -176,9 +177,9 @@ Assignment is a statement.
Most of statements begin with commands like `fd`. Most of statements begin with commands like `fd`.
Assignment is the only exception. Assignment is the only exception.
This program draws a line segment of 100 pixels long. The example above draws a line segment of 100 pixels long.
You can use variables in any places in expressions. You can use variables in expressions.
There are 8 kinds of calculations available. There are 8 kinds of calculations available.
- addition: x + y - addition: x + y
@ -289,7 +290,7 @@ repeat (4)
Repeat is called in the body of repeat. Repeat is called in the body of repeat.
The call to itself is a recursive call. The call to itself is a recursive call.
Parameters are generated each time the procedure is called. Parameters are created and set each time the procedure is called.
So, parameter `n` is 4 at the first call but it is 3 at the second call. So, parameter `n` is 4 at the first call but it is 3 at the second call.
Each time the procedure is called, the parameter `n` decreases by one. Each time the procedure is called, the parameter `n` decreases by one.
Finally, it becomes less than zero, then the procedures return. Finally, it becomes less than zero, then the procedures return.
@ -314,7 +315,7 @@ It is the first stage.
The second stage adds two shorter line segments at the endpoint of the original segment. The second stage adds two shorter line segments at the endpoint of the original segment.
The new segment has 70 percent length to the original segment and the orientation is +30 or -30 degrees different. The new segment has 70 percent length to the original segment and the orientation is +30 or -30 degrees different.
The third stage adds two shorter line segments to the second stage line segments. The third stage adds two shorter line segments to the second stage line segments.
And repeats this several times. And repeats it several times.
This repeating is programmed by recursive call. This repeating is programmed by recursive call.
Two more examples are shown here. Two more examples are shown here.
@ -345,8 +346,8 @@ Keywords:
identifiers and numbers: identifiers and numbers:
- identifier: This is used for the name of variables, parameters and procedures. - identifier: This is used for the name of variables, parameters and procedures.
It is expressed ` [a-zA-Z][a-zA-Z0-9]*` by regular expression. It is expressed ` [a-zA-Z][a-zA-Z0-9]*` in regular expression.
- number: This is expressed `(0|[1-9][0-9]*)(\.[0-9]+)?` by regular expression. - number: This is expressed `(0|[1-9][0-9]*)(\.[0-9]+)?` in regular expression.
It doesn't have `+` or `-` sign because they bring some syntactic confusion. It doesn't have `+` or `-` sign because they bring some syntactic confusion.
However negative number such as `-10` can be recognized as unary minus and a number. However negative number such as `-10` can be recognized as unary minus and a number.
@ -373,9 +374,10 @@ Delimiters
Comments and spaces: Comments and spaces:
- comment: This is characters between `#` and new line inclusive. - comment: This is characters between `#` and new line inclusive.
If a comment is at the end of the file, the trailing new line can be left out.
- white space: - white space:
- horizontal tab: tab is recognized as eight spaces. - horizontal tab: tab is recognized as eight spaces.
- new line: This is the end of a line and denoted by '\\n'. - new line: This is the end of a line.
These characters are used to separate tokens explicitly. These characters are used to separate tokens explicitly.
They doesn't have any syntactic meaning and are ignored by the parser. They doesn't have any syntactic meaning and are ignored by the parser.

BIN
image/turtle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
image/turtle.xcf Normal file

Binary file not shown.

View file

@ -1,7 +1,7 @@
# Tiny turtle graphics interpreter # Tiny turtle graphics interpreter
A program `turtle` is an example with the combination of TfeTextView and GtkDrawingArea objects. A program `turtle` is an example with the combination of TfeTextView and GtkDrawingArea objects.
It is a very small interpreter but it provides a way to draw fractal curves. It is a very small interpreter but it provides a tool to draw fractal curves.
The following diagram is a Koch curve, which is a famous example of fractal curves. The following diagram is a Koch curve, which is a famous example of fractal curves.
![Koch curve](turtle/image/turtle_koch.png){width=8cm height=5.11cm} ![Koch curve](turtle/image/turtle_koch.png){width=8cm height=5.11cm}
@ -54,6 +54,18 @@ You can read these files into `turtle` editor by clicking on the `Open` button.
Turtle uses TfeTextView and GtkDrawingArea. Turtle uses TfeTextView and GtkDrawingArea.
It is similar to `color` program in the previous section. It is similar to `color` program in the previous section.
1. A user inputs/reads a turtle program into the buffer in the TfeTextView instance.
2. The user clicks on the "Run" button.
3. The parser reads the program and generates tree-structured data.
4. The interpriter reads the data and executes it step by step.
And it draws shapes on a surface.
The surface is different from the surface of the GtkDrawingArea widget.
5. The widget is added to the queue.
It will be redrawn with the drawing function.
The function just copies the surface, which is drawn by the interpreter, into the surface of the GtkDrawingArea.
![Parser, interpreter and drawing function](../image/turtle.png)
The body of the interpreter is written with flex and bison. The body of the interpreter is written with flex and bison.
The codes are not thread safe. The codes are not thread safe.
So the handler of "clicked" signal of the `Run` button prevents from reentering. So the handler of "clicked" signal of the `Run` button prevents from reentering.
@ -65,22 +77,26 @@ turtle/turtleapplication.c run_cb resize_cb
- 8-13: The static value `busy` holds a status of the interpreter. - 8-13: The static value `busy` holds a status of the interpreter.
If it is `TRUE`, the interpreter is running and it is not possible to call the interpreter again because it's not a re-entrant program. If it is `TRUE`, the interpreter is running and it is not possible to call the interpreter again because it's not a re-entrant program.
If it is `FALSE`, it is safe to call the interpreter. If it is `FALSE`, it is safe to call the interpreter.
- 14: Now it is about to call the interpreter so changes `busy` to be TRUE. - 14: Now it is about to call the interpreter so it changes `busy` to TRUE.
- 15-16: Gets the contents of GtkTextBuffer. - 15-16: Gets the contents of `tb`.
- 17: The variable `surface` is a static variable. - 17: The variable `surface` is a static variable.
It points to a `cairo_surface_t` object. It points to a `cairo_surface_t` instance.
It is generated when the GtkDrawingArea object is realized and whenever it is resized. It is created when the GtkDrawingArea instance is realized and whenever it is resized.
Therefore, `surface` isn't NULL usually. Therefore, `surface` isn't NULL usually.
But if it is NULL, the interpreter won't be called. But if it is NULL, the interpreter won't be called.
- 18: Initializes lexical analyzer. - 18: Initializes lexical analyzer.
- 19: Calls parser. - 19: Calls parser.
Parser analyze the program codes syntactically and generate a tree structured data. Parser analyzes the program codes syntactically and generate a tree structured data.
- 20-22: If the parser successfully parsed, it calls `run` (runtime routine). - 20-22: If the parser successfully parsed, it calls `run` (runtime routine).
- 23: finalize the lexical analyzer. - 23: finalizes the lexical analyzer.
- 25: Add the drawing area object to the queue to draw. - 25: frees `contents`.
- 26: The interpreter program has finished so `busy` is now FALSE. - 26: Adds the drawing area widget to the queue to draw.
- 27: The interpreter program has finished so `busy` is now changed to FALSE.
- 29-34: A handler of "resized" signal. - 29-34: A handler of "resized" signal.
It generates or regenerates a surface object. If `surface` isn't NULL, it destroys the old surface.
Then it creates a new surface.
Its size is the same as the surface of the GtkDrawingArea instance.
Other part of `turtleapplication.c` is almost same as the codes of `colorapplication.c` in the previous section. Other part of `turtleapplication.c` is almost same as the codes of `colorapplication.c` in the previous section.
The codes of `turtleapplication.c` is in the [turtle directory](turtle). The codes of `turtleapplication.c` is in the [turtle directory](turtle).
@ -98,8 +114,8 @@ The turtle recognizes the program above and works as follows.
- Generally, a program consists of tokens. - Generally, a program consists of tokens.
Tokens are "distance", "=", "100", "fd", "*" and "2" in the above example.. Tokens are "distance", "=", "100", "fd", "*" and "2" in the above example..
- The parser calls `yylex` to read a token in the source file. - The parser calls a function `yylex` to read a token in the source file.
The `yylex` returns a code which is called "token kind" and sets a global variable `yylval` with a value, which is called a semantic value. `yylex` returns a code which is called "token kind" and sets a global variable `yylval` with a value, which is called a semantic value.
The type of `yylval` is union and `yylval.ID` is string and `yylval.NUM` is double. The type of `yylval` is union and `yylval.ID` is string and `yylval.NUM` is double.
There are seven tokens in the program so `yylex` is called seven times. There are seven tokens in the program so `yylex` is called seven times.
@ -122,7 +138,7 @@ This part of `turtle` is called parser.
![turtle parser tree](../image/turtle_parser_tree.png){width=12cm height=5.34cm} ![turtle parser tree](../image/turtle_parser_tree.png){width=12cm height=5.34cm}
- `turtle` analyzes the tree and executes it. - `turtle` analyzes the tree and executes it.
This part of `turtle` is called runtime routine. This part of `turtle` is called runtime routine or interpreter.
The tree consists of rectangles and line segments between the rectangles. The tree consists of rectangles and line segments between the rectangles.
The rectangles are called nodes. The rectangles are called nodes.
For example, N\_PROGRAM, N\_ASSIGN, N\_FD and N\_MUL are nodes. For example, N\_PROGRAM, N\_ASSIGN, N\_FD and N\_MUL are nodes.
@ -146,19 +162,22 @@ Multiplies 100 by 2 and gets 200.
Then `turtle` goes back to N_FD. Then `turtle` goes back to N_FD.
6. Now `turtle` knows the distance is 200. 6. Now `turtle` knows the distance is 200.
It moves the cursor forward by 200 pixels. It moves the cursor forward by 200 pixels.
The segment is drawn on the surface (`surface`).
8. There are no node follows. 8. There are no node follows.
Runtime routine returns to the main routine. Runtime routine returns to the function `run_cb`.
- `turtle` draws a segment on GtkDrawingArea then stops. - `run_cb` calls `gtk_widget_queue_draw` and put the GtkDrawingArea widget to the queue.
- The system redraws the widget.
At that time drawing function `draw_func` is called.
The function copies the surface (`surface`) to the surface in the GtkDrawingArea.
Most turtle programs are more complicated than the example above. Actual turtle program is more complicated than the example above.
So, `turtle` does much more work to interpret programs. However, what turtle does is basically the same.
However, basically it works by the same way above.
Interpretation consists of three parts. Interpretation consists of three parts.
- Lexical analysis - Lexical analysis
- Syntax Parsing and tree generation - Syntax Parsing and tree generation
- Interpret the tree and execute commands. - Interpretation and execution of the tree.
## Compilation flow ## Compilation flow
@ -213,7 +232,8 @@ The argument `turtleparser[1]` refers to `tirtle_parser.h` which is the second o
### What does flex do? ### What does flex do?
Flex creates lexical analyzer from flex source file. Flex creates lexical analyzer from flex source file.
Flex source file is a text file and its syntactic rule will be explained later. Flex source file is a text file.
Its syntactic rule will be explained later.
Generated lexical analyzer is a C source file. Generated lexical analyzer is a C source file.
It is also called scanner. It is also called scanner.
It reads a text file, which is a source file of a program language, and gets variable names, numbers and symbols. It reads a text file, which is a source file of a program language, and gets variable names, numbers and symbols.
@ -233,7 +253,7 @@ The words `fc`, `pd`, `distance`, `angle`, `tr`, `1`, `0`, `100` and `90` are ca
The characters '`(`' (left parenthesis), '`,`' (comma), '`)`' (right parenthesis) and '`=`' (equal sign) are called symbols. The characters '`(`' (left parenthesis), '`,`' (comma), '`)`' (right parenthesis) and '`=`' (equal sign) are called symbols.
( Sometimes those symbols called tokens, too.) ( Sometimes those symbols called tokens, too.)
Flex reads `turtle.lex` and generates a scanner. Flex reads `turtle.lex` and generates the C source file of a scanner.
The file `turtle.lex` specifies tokens, symbols and the behavior which corresponds to each token or symbol. The file `turtle.lex` specifies tokens, symbols and the behavior which corresponds to each token or symbol.
Turtle.lex isn't a big program. Turtle.lex isn't a big program.
@ -246,8 +266,6 @@ They are definitions, rules and user code sections.
### Definitions section ### Definitions section
First, look at the definitions section.
- 1-12: Lines between "%top{" and "}" are C source codes. - 1-12: Lines between "%top{" and "}" are C source codes.
They will be copied to the top of the generated C source file. They will be copied to the top of the generated C source file.
- 2-3: The function `strlen`, in line 62, is defined in `string.h` - 2-3: The function `strlen`, in line 62, is defined in `string.h`
@ -268,6 +286,10 @@ You can leave out such definitions here and use regular expressions in rules sec
This section is the most important part. This section is the most important part.
Rules consist of patterns and actions. Rules consist of patterns and actions.
The patterns are regular expressions or names surrounded by braces.
The names must be defined in the definitions section.
The definition of the regular expression is written in the flex documentation.
For example, line 37 is a rule. For example, line 37 is a rule.
- `{REAL_NUMBER}` is a pattern - `{REAL_NUMBER}` is a pattern
@ -285,23 +307,23 @@ The scanner generated by flex and C compiler has `yylex` function.
If `yylex` is called and the input is "123.4", then it works as follows. If `yylex` is called and the input is "123.4", then it works as follows.
1. A string "123.4" matches `{REAL_NUMBER}`. 1. A string "123.4" matches `{REAL_NUMBER}`.
2. Update the location variable `ncolumn` and `yylloc`. 2. Update the location variable `ncolumn` and `yylloc`with `get_location`.
3. `atof` converts the string "123.4" to double sized floating point number 123.4. 3. `atof` converts the string "123.4" to double type number 123.4.
4. It is assigned to `yylval.NUM`. 4. It is assigned to `yylval.NUM`.
5. `yylex` returns `NUM` to the caller. 5. `yylex` returns `NUM` to the caller.
Then the caller knows the input is `NUM` (number), and its value is 123.4. Then the caller knows the input is `NUM` (number), and its value is 123.4.
- 19-55: Rules section. - 19-55: Rules section.
- 20: Comment begins `#` followed by any characters except newline. - 20: The symbol `.` (dot) matches any character except newline.
Therefore, a comment begins `#` followed by any characters except newline.
No action happens. No action happens.
- 21: White space just increases a variable `ncolumn` by one. - 21: White space just increases a variable `ncolumn` by one.
- 22: Tab is assumed to be equal to eight spaces. - 22: Tab is assumed to be equal to eight spaces.
- 23: New line increases a variable `nline` by one and resets `ncolumn`. - 23: New line increases a variable `nline` by one and resets `ncolumn`.
- 25-35: Keywords just updates the location variables `ncolumn` and `yylloc`, and return the codes of the keywords. - 25-35: Keywords just updates the location variables `ncolumn` and `yylloc`, and return the codes of the keywords.
- 37: Real number constant. - 37: Real number constant.
- 38: Identifier is defined in line 17. - 38: `IDENTIFIER` is defined in line 17.
It begins alphabet followed by zero or more alphabet or digit.
The location variables are updated and the name of the identifier is assigned to `yylval.ID`. The location variables are updated and the name of the identifier is assigned to `yylval.ID`.
The memory of the name is allocated by the function `g_strdup`. The memory of the name is allocated by the function `g_strdup`.
The memory is registered to the list (GSlist type list). The memory is registered to the list (GSlist type list).
@ -318,11 +340,10 @@ This section is just copied to C source file.
- 58-63: A function `get_location`. - 58-63: A function `get_location`.
The location of the input is recorded to `nline` and `ncolumn`. The location of the input is recorded to `nline` and `ncolumn`.
These two variables are for the scanner. A variable `yylloc` is referred by the parser.
A variable `yylloc` is shared by the scanner and the parser.
It is a C structure and has four members, `first_line`, `first_column`, `last_line` and `last_column`. It is a C structure and has four members, `first_line`, `first_column`, `last_line` and `last_column`.
They point the start and end of the current input text. They point the start and end of the current input text.
- 65: `YY_BUFFER_STATE` is a type of the pointer points the input buffer. - 65: `YY_BUFFER_STATE` is a pointer points the input buffer.
- 67-70: `init_flex` is called by `run_cb` signal handler, which is called when `Run` button is clicked on. - 67-70: `init_flex` is called by `run_cb` signal handler, which is called when `Run` button is clicked on.
`run_cb` calls `init_flex` with one argument which is the copy of the content of GtkTextBuffer. `run_cb` calls `init_flex` with one argument which is the copy of the content of GtkTextBuffer.
`yy_scan_string` sets the input buffer to read from the text. `yy_scan_string` sets the input buffer to read from the text.
@ -383,7 +404,7 @@ It doesn't return any values.
Programmers can define their own procedures. Programmers can define their own procedures.
On the other hand, `fc` is a built-in procedure. On the other hand, `fc` is a built-in procedure.
Such procedures are called primary procedures. Such procedures are called primary procedures.
It is described in Bison source code like: It is described in bison source code like:
~~~ ~~~
primary_procedure: FC '(' expression ',' expression ',' expression ')'; primary_procedure: FC '(' expression ',' expression ',' expression ')';
@ -404,8 +425,7 @@ The first line is:
FC '(' NUM ',' NUM ',' NUM ')'; FC '(' NUM ',' NUM ',' NUM ')';
~~~ ~~~
You can find this is a primary_procedure easily. The parser analyzes the turtle source code and if the input matches the definition above, the parser recognizes it as a primary procedure.
The parser of the turtle language analyzes the turtle source code in the same way.
The grammar of turtle is described in the [document](turtle/turtle_doc.src.md). The grammar of turtle is described in the [document](turtle/turtle_doc.src.md).
The following is an extract from the document. The following is an extract from the document.
@ -740,7 +760,7 @@ This type is shared by the scanner file and the parser implementation file.
The error report function `yyerror` uses it so that it can inform the location that error occurs. The error report function `yyerror` uses it so that it can inform the location that error occurs.
`%define api.value.type union` generates semantic value type with tokens and nterms and inserts it to the header file. `%define api.value.type union` generates semantic value type with tokens and nterms and inserts it to the header file.
The inserted part is shown in the previous section as the extracts that shows the value type (YYSTYPE). The inserted part is shown in the previous subsection as the extracts that shows the value type (YYSTYPE).
`%token` and `%nterm` directives define tokens and non terminal symbols respectively. `%token` and `%nterm` directives define tokens and non terminal symbols respectively.
@ -867,7 +887,7 @@ There's no action specified.
Then, the default action is executed. Then, the default action is executed.
It is ` $$ = $1`. It is ` $$ = $1`.
- `primary_procedure` is `FD` followed by expression. - `primary_procedure` is `FD` followed by expression.
The action calls `tree1` and assign its return value to `$$`. The action calls `tree1` and assigns its return value to `$$`.
`tree1` makes a tree node. `tree1` makes a tree node.
The tree node has type and union of three pointers to children nodes, string or double. The tree node has type and union of three pointers to children nodes, string or double.
~~~ ~~~
@ -879,7 +899,7 @@ node --+-- type
~~~ ~~~
- `tree1` assigns the four arguments to type, child1, child2 and child3 members. - `tree1` assigns the four arguments to type, child1, child2 and child3 members.
- `expression` is `NUM`. - `expression` is `NUM`.
- `tree2` assigns the two arguments to type and a double member. - `tree2` makes a tree node. The paremeters of `tree2` are a type and a semantic value.
Suppose the parser reads the following program. Suppose the parser reads the following program.
@ -994,15 +1014,15 @@ expression:
### Epilogue ### Epilogue
The epilogue is written in C language and copied to the parser implementation file. The epilogue is written in C language and copied to the parser implementation file.
Generally, you can put anything into epilogue. Generally, you can put anything into the epilogue.
In the case of turtle interpreter, the runtime routine and some other functions are in the epilogue. In the case of turtle interpreter, the runtime routine and some other functions are in the epilogue.
#### Functions to generate tree nodes #### Functions to create tree nodes
There are three functions, `tree1`, `tree2` and `tree3`. There are three functions, `tree1`, `tree2` and `tree3`.
- `tree1` creates a node and sets the node type and pointers to its three children (NULL is possible). - `tree1` creates a node and sets the node type and pointers to its three children (NULL is possible).
- `tree2` creates a node and sets the node type and a value. - `tree2` creates a node and sets the node type and a value (double).
- `tree3` creates a node and sets the node type and a pointer to a string. - `tree3` creates a node and sets the node type and a pointer to a string.
Each function gets memories first and build a node on them. Each function gets memories first and build a node on them.
@ -1209,7 +1229,8 @@ We can know the amount of elements used in the array during the runtime.
The purpose of the variable is to find appropriate `MAX_STACK_SIZE`. The purpose of the variable is to find appropriate `MAX_STACK_SIZE`.
It will be unnecessary in the future version if the stack is implemented with better data structure and memory allocation. It will be unnecessary in the future version if the stack is implemented with better data structure and memory allocation.
The runtime routine push data to the stack when it executes a procedure call node. The runtime routine push data to the stack when it executes a node of a procedure call.
(The type of the node is `N_procedure_call`.)
~~~ ~~~
dp drawline (angle, distance) { ... ... ... } dp drawline (angle, distance) { ... ... ... }
@ -1249,7 +1270,7 @@ It searches only the parameters of the latest procedure.
It returns TRUE and sets the argument `value` to point the value, if the variable has been found. It returns TRUE and sets the argument `value` to point the value, if the variable has been found.
Otherwise it returns FALSE. Otherwise it returns FALSE.
- `stack_replace` replaces the value of the variable in the stack. - `stack_replace` replaces the value of the variable in the stack.
If it successes, it returns TRUE. Otherwise returns FALSE. If it succeeds, it returns TRUE. Otherwise returns FALSE.
- `stack_return` throws away the latest parameters. - `stack_return` throws away the latest parameters.
The stack pointer goes back to the point before the latest procedure call so that it points to parameters of the previous called procedure. The stack pointer goes back to the point before the latest procedure call so that it points to parameters of the previous called procedure.
@ -1326,7 +1347,9 @@ It is initialized in `turtleapplication.c`.
The runtime routine has its own cairo context. The runtime routine has its own cairo context.
This is different from the cairo of GtkDrawingArea. This is different from the cairo of GtkDrawingArea.
Runtime routine draws a shape on the `surface` with the cairo context. Runtime routine draws a shape on the `surface` with the cairo context.
After runtime routine returns to `run_cb`, the drawing function `draw_func` copies the `surface` to the surface in the GtkDrawingArea object. After runtime routine returns to `run_cb`, `run_cb` adds the GtkDrawingArea widget to the queue to redraw.
When the widget is redraw,the drawing function `draw_func` is called.
It copies the `surface` to the surface in the GtkDrawingArea object.
`turtle.y` has two functions `init_cairo` and `destroy_cairo`. `turtle.y` has two functions `init_cairo` and `destroy_cairo`.
@ -1388,6 +1411,8 @@ static cairo_t *cr;
gboolean gboolean
init_cairo (void) { init_cairo (void) {
int width, height; int width, height;
cairo_matrix_t matrix;
pen = TRUE; pen = TRUE;
angle = 90.0; angle = 90.0;
cur_x = 0.0; cur_x = 0.0;
@ -1396,13 +1421,12 @@ init_cairo (void) {
bc.red = 0.95; bc.green = 0.95; bc.blue = 0.95; bc.red = 0.95; bc.green = 0.95; bc.blue = 0.95;
fc.red = 0.0; fc.green = 0.0; fc.blue = 0.0; fc.red = 0.0; fc.green = 0.0; fc.blue = 0.0;
if (surface) {
width = cairo_image_surface_get_width (surface); width = cairo_image_surface_get_width (surface);
height = cairo_image_surface_get_height (surface); height = cairo_image_surface_get_height (surface);
cairo_matrix_t matrix;
matrix.xx = 1.0; matrix.xy = 0.0; matrix.x0 = (double) width / 2.0; matrix.xx = 1.0; matrix.xy = 0.0; matrix.x0 = (double) width / 2.0;
matrix.yx = 0.0; matrix.yy = -1.0; matrix.y0 = (double) height / 2.0; matrix.yx = 0.0; matrix.yy = -1.0; matrix.y0 = (double) height / 2.0;
if (surface) {
cr = cairo_create (surface); cr = cairo_create (surface);
cairo_transform (cr, &matrix); cairo_transform (cr, &matrix);
cairo_set_source_rgb (cr, bc.red, bc.green, bc.blue); cairo_set_source_rgb (cr, bc.red, bc.green, bc.blue);
@ -1483,7 +1507,7 @@ double value = 0.0;
#### Execute function #### Execute function
Primary procedures and procedure definitions are analyzed and carried out by a function `execute`. Primary procedures and procedure definitions are analyzed and executed by the function `execute`.
It doesn't return any values. It doesn't return any values.
It calls itself recursively. It calls itself recursively.
The process of `N_RT` and `N_procedure_call` is complicated. The process of `N_RT` and `N_procedure_call` is complicated.
@ -1679,7 +1703,7 @@ When the parser reads the fifth line in the example, it creates nodes like this:
When the runtime routine meets `N_procedure_call` node, it behaves like this: When the runtime routine meets `N_procedure_call` node, it behaves like this:
1. Searches the symbol table for the procedure by the name. 1. Searches the symbol table for the procedure with the name.
2. Gets pointers to the node to parameters and the node to the body. 2. Gets pointers to the node to parameters and the node to the body.
3. Creates a temporary stack. 3. Creates a temporary stack.
Makes a tuple of each parameter name and argument value. Makes a tuple of each parameter name and argument value.

View file

@ -516,6 +516,8 @@ static cairo_t *cr;
gboolean gboolean
init_cairo (void) { init_cairo (void) {
int width, height; int width, height;
cairo_matrix_t matrix;
pen = TRUE; pen = TRUE;
angle = 90.0; angle = 90.0;
cur_x = 0.0; cur_x = 0.0;
@ -524,13 +526,12 @@ init_cairo (void) {
bc.red = 0.95; bc.green = 0.95; bc.blue = 0.95; bc.red = 0.95; bc.green = 0.95; bc.blue = 0.95;
fc.red = 0.0; fc.green = 0.0; fc.blue = 0.0; fc.red = 0.0; fc.green = 0.0; fc.blue = 0.0;
if (surface) {
width = cairo_image_surface_get_width (surface); width = cairo_image_surface_get_width (surface);
height = cairo_image_surface_get_height (surface); height = cairo_image_surface_get_height (surface);
cairo_matrix_t matrix;
matrix.xx = 1.0; matrix.xy = 0.0; matrix.x0 = (double) width / 2.0; matrix.xx = 1.0; matrix.xy = 0.0; matrix.x0 = (double) width / 2.0;
matrix.yx = 0.0; matrix.yy = -1.0; matrix.y0 = (double) height / 2.0; matrix.yx = 0.0; matrix.yy = -1.0; matrix.y0 = (double) height / 2.0;
if (surface) {
cr = cairo_create (surface); cr = cairo_create (surface);
cairo_transform (cr, &matrix); cairo_transform (cr, &matrix);
cairo_set_source_rgb (cr, bc.red, bc.green, bc.blue); cairo_set_source_rgb (cr, bc.red, bc.green, bc.blue);

View file

@ -83,14 +83,14 @@ fd 100
The command `tr` is "Turn Right". The command `tr` is "Turn Right".
The argument is angle with degrees. The argument is angle with degrees.
Therefore, `tr 90` means "Turn right by 90 degrees". Therefore, `tr 90` means "Turn right by 90 degrees".
If you click on `run`button, then two line segment appears. If you click on the `run`button, then two line segments appears.
One is vertical and the other is horizontal. One is vertical and the other is horizontal.
![Two line segments on the surface](image/turtle2.png){width=8cm height=5.11cm} ![Two line segments on the surface](image/turtle2.png){width=8cm height=5.11cm}
## Background and foreground color ## Background and foreground color
Colors are specified by RGB. Colors are specified with RGB.
A vector (r, g, b) denotes RGB color. A vector (r, g, b) denotes RGB color.
Each of the elements is a real number between 0 and 1. Each of the elements is a real number between 0 and 1.
@ -129,7 +129,7 @@ Statements are executed in the order from the top to the end
## Comment and spaces ## Comment and spaces
Characters between `#` (hash mark) and `\n` (new line) inclusive are comment. Characters between `#` (hash mark) and `\n` (new line) inclusive are comment.
Characters between `#` and `EOF` (end of file) are also comment. If the comment is at the end of the file, the trailing new line can be left out.
Comments are ignored. Comments are ignored.
~~~ ~~~
@ -141,7 +141,7 @@ tr 120<NEW LINE>
fd 100 # Now a triangle appears.<EOF> fd 100 # Now a triangle appears.<EOF>
~~~ ~~~
\<NEW LINE\> and \<EOF\> are newline code and end of file respectively. \<NEW LINE\> and \<EOF\> indicate newline code and end of file respectively.
The comments in the line 1, 2, 3 and 6 are correct syntactically. The comments in the line 1, 2, 3 and 6 are correct syntactically.
Spaces (white space, tab and new line) are ignored. Spaces (white space, tab and new line) are ignored.
@ -150,14 +150,15 @@ Tabs are recognized as eight spaces to calculate the column number.
## Variables and expressions ## Variables and expressions
Variable begins alphabet followed by alphabet or digit except key words like `fd`, `tr` and so on. Variable begins alphabet followed by alphabet or digit.
`Distance` and `angle5` can be variables, but `1step` isn't a variable because the first character isn't alphabet. Key words like `fd`, `tr` can't be variables.
`Distance` and `angle5` are variables, but `1step` isn't a variable because the first character isn't alphabet.
Variable names are case sensitive. Variable names are case sensitive.
Variables keep real numbers. Variables keep real numbers.
Their type is the same as `double` in C language. Their type is the same as `double` in C language.
Integers are casted to real numbers automatically. Integers are casted to real numbers automatically.
So 1 and 1.0 are the same value. So 1 and 1.0 are the same value.
Numbers begins digits, not signs (`+` or `-`). Numbers begin digits, not signs (`+` or `-`).
- 100, 20.34 and 0.01 are numbers - 100, 20.34 and 0.01 are numbers
- +100 isn't a number. It causes syntax error. Use 100 instead. - +100 isn't a number. It causes syntax error. Use 100 instead.
@ -176,9 +177,9 @@ Assignment is a statement.
Most of statements begin with commands like `fd`. Most of statements begin with commands like `fd`.
Assignment is the only exception. Assignment is the only exception.
This program draws a line segment of 100 pixels long. The example above draws a line segment of 100 pixels long.
You can use variables in any places in expressions. You can use variables in expressions.
There are 8 kinds of calculations available. There are 8 kinds of calculations available.
- addition: x + y - addition: x + y
@ -289,7 +290,7 @@ repeat (4)
Repeat is called in the body of repeat. Repeat is called in the body of repeat.
The call to itself is a recursive call. The call to itself is a recursive call.
Parameters are generated each time the procedure is called. Parameters are created and set each time the procedure is called.
So, parameter `n` is 4 at the first call but it is 3 at the second call. So, parameter `n` is 4 at the first call but it is 3 at the second call.
Each time the procedure is called, the parameter `n` decreases by one. Each time the procedure is called, the parameter `n` decreases by one.
Finally, it becomes less than zero, then the procedures return. Finally, it becomes less than zero, then the procedures return.
@ -314,7 +315,7 @@ It is the first stage.
The second stage adds two shorter line segments at the endpoint of the original segment. The second stage adds two shorter line segments at the endpoint of the original segment.
The new segment has 70 percent length to the original segment and the orientation is +30 or -30 degrees different. The new segment has 70 percent length to the original segment and the orientation is +30 or -30 degrees different.
The third stage adds two shorter line segments to the second stage line segments. The third stage adds two shorter line segments to the second stage line segments.
And repeats this several times. And repeats it several times.
This repeating is programmed by recursive call. This repeating is programmed by recursive call.
Two more examples are shown here. Two more examples are shown here.
@ -345,8 +346,8 @@ Keywords:
identifiers and numbers: identifiers and numbers:
- identifier: This is used for the name of variables, parameters and procedures. - identifier: This is used for the name of variables, parameters and procedures.
It is expressed ` [a-zA-Z][a-zA-Z0-9]*` by regular expression. It is expressed ` [a-zA-Z][a-zA-Z0-9]*` in regular expression.
- number: This is expressed `(0|[1-9][0-9]*)(\.[0-9]+)?` by regular expression. - number: This is expressed `(0|[1-9][0-9]*)(\.[0-9]+)?` in regular expression.
It doesn't have `+` or `-` sign because they bring some syntactic confusion. It doesn't have `+` or `-` sign because they bring some syntactic confusion.
However negative number such as `-10` can be recognized as unary minus and a number. However negative number such as `-10` can be recognized as unary minus and a number.
@ -373,9 +374,10 @@ Delimiters
Comments and spaces: Comments and spaces:
- comment: This is characters between `#` and new line inclusive. - comment: This is characters between `#` and new line inclusive.
If a comment is at the end of the file, the trailing new line can be left out.
- white space: - white space:
- horizontal tab: tab is recognized as eight spaces. - horizontal tab: tab is recognized as eight spaces.
- new line: This is the end of a line and denoted by '\\n'. - new line: This is the end of a line.
These characters are used to separate tokens explicitly. These characters are used to separate tokens explicitly.
They doesn't have any syntactic meaning and are ignored by the parser. They doesn't have any syntactic meaning and are ignored by the parser.

View file

@ -30,6 +30,7 @@ run_cb (GtkWidget *btnr) {
} }
finalize_flex (); finalize_flex ();
} }
g_free (contents);
gtk_widget_queue_draw (GTK_WIDGET (da)); gtk_widget_queue_draw (GTK_WIDGET (da));
busy = FALSE; busy = FALSE;
} }
@ -51,6 +52,13 @@ close_cb (GtkWidget *btnc) {
gtk_window_destroy (GTK_WINDOW (win)); gtk_window_destroy (GTK_WINDOW (win));
} }
static gboolean
close_request_cb (GtkWindow *win, gpointer user_data) {
if (surface)
cairo_surface_destroy (surface);
return FALSE;
}
static void static void
show_filename (TfeTextView *tv) { show_filename (TfeTextView *tv) {
GFile *file; GFile *file;
@ -62,10 +70,11 @@ show_filename (TfeTextView *tv) {
filename = g_file_get_basename (file); filename = g_file_get_basename (file);
title = g_strdup_printf ("Turtle (%s)", filename); title = g_strdup_printf ("Turtle (%s)", filename);
g_free (filename); g_free (filename);
} else
title = "Turtle";
g_object_unref (file); g_object_unref (file);
} else
title = g_strdup ("Turtle");
gtk_window_set_title (GTK_WINDOW (win), title); gtk_window_set_title (GTK_WINDOW (win), title);
g_free (title);
} }
static void static void
@ -84,12 +93,12 @@ draw_func (GtkDrawingArea *drawing_area, cairo_t *cr, int width, int height, gpo
} }
static void static void
activate (GApplication *application) { app_activate (GApplication *application) {
gtk_widget_show (win); gtk_widget_show (win);
} }
static void static void
startup (GApplication *application) { app_startup (GApplication *application) {
GtkApplication *app = GTK_APPLICATION (application); GtkApplication *app = GTK_APPLICATION (application);
GtkBuilder *build; GtkBuilder *build;
@ -99,6 +108,7 @@ startup (GApplication *application) {
tv = GTK_WIDGET (gtk_builder_get_object (build, "tv")); tv = GTK_WIDGET (gtk_builder_get_object (build, "tv"));
da = GTK_WIDGET (gtk_builder_get_object (build, "da")); da = GTK_WIDGET (gtk_builder_get_object (build, "da"));
g_object_unref(build); g_object_unref(build);
g_signal_connect (win, "close-request", G_CALLBACK (close_request_cb), NULL);
g_signal_connect (GTK_DRAWING_AREA (da), "resize", G_CALLBACK (resize_cb), NULL); g_signal_connect (GTK_DRAWING_AREA (da), "resize", G_CALLBACK (resize_cb), NULL);
g_signal_connect (tv, "change-file", G_CALLBACK (show_filename), NULL); g_signal_connect (tv, "change-file", G_CALLBACK (show_filename), NULL);
gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (da), draw_func, NULL, NULL); gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (da), draw_func, NULL, NULL);
@ -111,15 +121,17 @@ GdkDisplay *display;
gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER); gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER);
} }
#define APPLICATION_ID "com.github.ToshioCP.turtle"
int int
main (int argc, char **argv) { main (int argc, char **argv) {
GtkApplication *app; GtkApplication *app;
int stat; int stat;
app = gtk_application_new ("com.github.ToshioCP.turtle", G_APPLICATION_FLAGS_NONE); app = gtk_application_new (APPLICATION_ID, G_APPLICATION_FLAGS_NONE);
g_signal_connect (app, "startup", G_CALLBACK (startup), NULL); g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL); g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
stat =g_application_run (G_APPLICATION (app), argc, argv); stat =g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app); g_object_unref (app);