/
evo
/
Gtk4-tutorial
Обзор
Документация
Войти
/
evo
/
Gtk4-tutorial
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/turtle/turtle.y
885 строк
22 KB
Toshio Sekiya
Add DND section (section 28).
29 июл 2023, 15:41
29 июл 2023, 15:41
bda0f28
Код
Авторство
О чём код?
/* There are two phases to run the program. 1. parsing and generate a tree. Node is a joint to connect children's node to itself. There are two types of nodes, non-terminal and terminal. Some terminal nodes have their values. Others, keywords or symbols, don't have any values. 2. Execute the program represented by the tree. */ %code top{ #include <stdarg.h> #include <setjmp.h> #include <math.h> #include <glib.h> #include <cairo.h> #include "turtle_parser.h" /* The following line defines 'debug' so that debug information is printed out during the run time. */ /* However it makes the program slow. */ /* If you want to debug on, uncomment the line. */ /* #define debug 1 */ extern cairo_surface_t *surface; /* error reporting */ static void yyerror (char const *s) { /* for syntax error */ g_printerr ("%s from line %d, column %d to line %d, column %d\n",s, yylloc.first_line, yylloc.first_column, yylloc.last_line, yylloc.last_column); } /* Node type */ enum { N_PU, N_PD, N_PW, N_FD, N_TR, N_TL, /* Turn Left version 0.5 */ N_BC, N_FC, N_DP, N_ASSIGN, N_IF, N_RT, N_RS, N_RP, /* Repeat version 0.5 */ N_NUM, N_ID, N_program, N_statement, N_primary_procedure, N_primary_procedure_list, N_procedure_call, N_procedure_definition, N_parameter_list, N_argument_list, N_expression, N_EQ, N_GT, N_LT, N_ADD, N_SUB, N_MUL, N_DIV, N_UMINUS }; #ifdef debug char *node_type_table[] = { "N_PU", "N_PD", "N_PW", "N_FD", "N_TR", "N_TL", /* ver o.5 */ "N_BC", "N_FC", "N_DP", "N_ASSIGN", "N_IF", "N_RT", "N_RS", "N_RP", /* ver 0.5 */ "N_NUM", "N_ID", "N_program", "N_statement", "N_primary_procedure", "N_primary_procedure_list", "N_procedure_call", "N_procedure_definition", "N_parameter_list", "N_argument_list", "N_expression", "N_EQ", "N_GT", "N_LT", "N_ADD", "N_SUB", "N_MUL", "N_DIV", "N_UMINUS" }; #endif } %code requires { int yylex (void); int yyparse (void); void run (void); /* semantic value type */ typedef struct _node_t node_t; struct _node_t { int type; union { struct { node_t *child1, *child2, *child3; } child; char *name; double value; } content; }; } %code { /* The following macro is convenient to get the member of the node. */ #define child1(n) (n)->content.child.child1 #define child2(n) (n)->content.child.child2 #define child3(n) (n)->content.child.child3 #define name(n) (n)->content.name #define value(n) (n)->content.value /* start of nodes */ static node_t *node_top = NULL; /* functions to generate trees */ static node_t *tree1 (int type, node_t *child1, node_t *child2, node_t *child3); static node_t *tree2 (int type, double value); static node_t *tree3 (int type, char *name); } %locations %define api.value.type union /* YYSTYPE, the type of semantic values, is union of following types */ /* key words */ %token PU %token PD %token PW %token FD %token TR %token TL /* ver 0.5 */ %token BC %token FC %token DP %token IF %token RT %token RS %token RP /* ver 0.5 */ /* constant */ %token <double> NUM /* identifier */ %token <char *> ID /* non terminal symbol */ %nterm <node_t *> program %nterm <node_t *> statement %nterm <node_t *> primary_procedure %nterm <node_t *> primary_procedure_list %nterm <node_t *> procedure_definition %nterm <node_t *> parameter_list %nterm <node_t *> argument_list %nterm <node_t *> expression /* logical relation symbol */ %left '=' '<' '>' /* arithmetic symbol */ %left '+' '-' %left '*' '/' %precedence UMINUS /* unary minus */ %% program: statement { node_top = $$ = $1; } | program statement { node_top = $$ = tree1 (N_program, $1, $2, NULL); #ifdef debug if (node_top == NULL) g_printerr ("program: node_top is NULL.\n"); else g_printerr ("program: node_top is NOT NULL.\n"); #endif } ; statement: primary_procedure | procedure_definition ; primary_procedure: PU { $$ = tree1 (N_PU, NULL, NULL, NULL); } | PD { $$ = tree1 (N_PD, NULL, NULL, NULL); } | PW expression { $$ = tree1 (N_PW, $2, NULL, NULL); } | FD expression { $$ = tree1 (N_FD, $2, NULL, NULL); } | TR expression { $$ = tree1 (N_TR, $2, NULL, NULL); } | TL expression { $$ = tree1 (N_TL, $2, NULL, NULL); } /* ver 0.5 */ | BC '(' expression ',' expression ',' expression ')' { $$ = tree1 (N_BC, $3, $5, $7); } | FC '(' expression ',' expression ',' expression ')' { $$ = tree1 (N_FC, $3, $5, $7); } /* assignment */ | ID '=' expression { $$ = tree1 (N_ASSIGN, tree3 (N_ID, $1), $3, NULL); } /* control flow */ | IF '(' expression ')' '{' primary_procedure_list '}' { $$ = tree1 (N_IF, $3, $6, NULL); } | RT { $$ = tree1 (N_RT, NULL, NULL, NULL); } | RS { $$ = tree1 (N_RS, NULL, NULL, NULL); } | RP '(' expression ')' '{' primary_procedure_list '}' { $$ = tree1 (N_RP, $3, $6, NULL); } /* user defined procedure call */ | ID '(' ')' { $$ = tree1 (N_procedure_call, tree3 (N_ID, $1), NULL, NULL); } | ID '(' argument_list ')' { $$ = tree1 (N_procedure_call, tree3 (N_ID, $1), $3, NULL); } ; procedure_definition: DP ID '(' ')' '{' primary_procedure_list '}' { $$ = tree1 (N_procedure_definition, tree3 (N_ID, $2), NULL, $6); } | DP ID '(' parameter_list ')' '{' primary_procedure_list '}' { $$ = tree1 (N_procedure_definition, tree3 (N_ID, $2), $4, $7); } ; parameter_list: ID { $$ = tree3 (N_ID, $1); } | parameter_list ',' ID { $$ = tree1 (N_parameter_list, $1, tree3 (N_ID, $3), NULL); } ; argument_list: expression | argument_list ',' expression { $$ = tree1 (N_argument_list, $1, $3, NULL); } ; primary_procedure_list: primary_procedure | primary_procedure_list primary_procedure { $$ = tree1 (N_primary_procedure_list, $1, $2, NULL); } ; expression: expression '=' expression { $$ = tree1 (N_EQ, $1, $3, NULL); } | expression '>' expression { $$ = tree1 (N_GT, $1, $3, NULL); } | expression '<' expression { $$ = tree1 (N_LT, $1, $3, NULL); } | expression '+' expression { $$ = tree1 (N_ADD, $1, $3, NULL); } | expression '-' expression { $$ = tree1 (N_SUB, $1, $3, NULL); } | expression '*' expression { $$ = tree1 (N_MUL, $1, $3, NULL); } | expression '/' expression { $$ = tree1 (N_DIV, $1, $3, NULL); } | '-' expression %prec UMINUS { $$ = tree1 (N_UMINUS, $2, NULL, NULL); } | '(' expression ')' { $$ = $2; } | ID { $$ = tree3 (N_ID, $1); } | NUM { $$ = tree2 (N_NUM, $1); } ; %% /* Declaration of the runtime error function */ static void runtime_error (char *format, ...); /* Dynamically allocated memories are added to the single list. They will be freed in the finalize function. */ GSList *list = NULL; node_t * tree1 (int type, node_t *child1, node_t *child2, node_t *child3) { node_t *new_node; list = g_slist_prepend (list, g_malloc (sizeof (node_t))); new_node = (node_t *) list->data; new_node->type = type; child1(new_node) = child1; child2(new_node) = child2; child3(new_node) = child3; return new_node; } node_t * tree2 (int type, double value) { node_t *new_node; list = g_slist_prepend (list, g_malloc (sizeof (node_t))); new_node = (node_t *) list->data; new_node->type = type; value(new_node) = value; return new_node; } node_t * tree3 (int type, char *name) { node_t *new_node; list = g_slist_prepend (list, g_malloc (sizeof (node_t))); new_node = (node_t *) list->data; new_node->type = type; name(new_node) = name; return new_node; } /* Symbols are names of procedures and variables. * They are stored in a single symbol table. * The names and types are the keys to search the corresponding objects from the table. */ #define MAX_TABLE_SIZE 100 enum { PROC, VAR }; struct { int type; char *name; union { node_t *node; double value; } object; } table[MAX_TABLE_SIZE]; int tp; void init_table (void) { tp = 0; } int tbl_lookup (int type, char *name) { int i; if (tp == 0) return -1; for (i=0; i<tp; ++i) if (type == table[i].type && strcmp(name, table[i].name) == 0) return i; return -1; } void tbl_install (int type, char *name, node_t *node, double value) { if (tp >= MAX_TABLE_SIZE) runtime_error ("Symbol table overflow.\n"); else if (tbl_lookup (type, name) >= 0) runtime_error ("Name %s is already registered.\n", name); else { table[tp].type = type; table[tp].name = name; if (type == PROC) table[tp++].object.node = node; else table[tp++].object.value = value; } } void proc_install (char *name, node_t *node) { tbl_install (PROC, name, node, 0.0); } void var_install (char *name, double value) { tbl_install (VAR, name, NULL, value); } void var_replace (char *name, double value) { int i; if ((i = tbl_lookup (VAR, name)) >= 0) table[i].object.value = value; else var_install (name, value); } node_t * proc_lookup (char *name) { int i; if ((i = tbl_lookup (PROC, name)) < 0) return NULL; else return table[i].object.node; } gboolean var_lookup (char *name, double *value) { int i; if ((i = tbl_lookup (VAR, name)) < 0) return FALSE; else { *value = table[i].object.value; return TRUE; } } #ifdef debug /* Show the symbol table */ void show_symbol_table (void) { int i; g_printerr ("****** Symbol table ******\n"); g_printerr (" Table pointer (tp) = %d.\n", tp); g_printerr (" ** Contents **\n"); for (i=0; i<tp; ++i) if (table[i].type == PROC) g_printerr ("PROC, %s, %lx\n", table[i].name, (long int) table[i].object.node); else g_printerr ("VAR, %s, %f\n", table[i].name, table[i].object.value); } #endif /* Parameters are stored in an array. * The last parameter is stored in the first element of the array. */ /* Stack */ /* Argument lists are pushed into a stack in runtime. */ /* It ensures recursive call. */ #define MAX_STACK_SIZE 500 struct { char *name; double value; } stack[MAX_STACK_SIZE]; int sp, sp_biggest; void init_stack (void) { sp = sp_biggest = 0; } void stack_push (char *name, double value) { if (sp >= MAX_STACK_SIZE) runtime_error ("Stack overflow.\n"); else { stack[sp].name = name; stack[sp++].value = value; sp_biggest = sp > sp_biggest ? sp : sp_biggest; } } /* If not found, return -1 */ /* If found, return the index */ int stack_search (char *name) { int depth, i; if (sp == 0) return -1; depth = (int) stack[sp-1].value; if (depth + 1 > sp) /* something strange */ runtime_error ("Stack error.\n"); for (i=0; i<depth; ++i) if (strcmp(name, stack[sp-(i+2)].name) == 0) { return sp-(i+2); } return -1; } gboolean stack_lookup (char *name, double *value) { int i; if ((i = stack_search (name)) < 0) return FALSE; else { *value = stack[i].value; return TRUE; } } gboolean stack_replace (char *name, double value) { int i; if ((i = stack_search (name)) < 0) return FALSE; else { stack[i].value = value; return TRUE; } } void stack_return(void) { int depth; if (sp <= 0) return; depth = (int) stack[sp-1].value; if (depth + 1 > sp) /* something strange */ runtime_error ("Stack error.\n"); sp -= (int) depth + 1; } #ifdef debug /* Show the stack */ void show_stack (void) { int i; g_printerr ("****** Stack ******\n"); g_printerr (" Stack pointer (sp) = %d.\n", sp); g_printerr (" ** Contents **\n"); for (i=0; i<sp; ++i) if (stack[i].name) g_printerr ("%s, %f\n", stack[i].name, stack[i].value); else g_printerr ("(null), %f\n", stack[i].value); } #endif /* procedure - return status */ static int proc_level = 0; static int ret_level = 0; /* status of the surface */ static gboolean pen = TRUE; static double angle = 90.0; /* angle starts from x axis and measured counterclockwise */ /* Initially facing to the north */ static double cur_x = 0.0; static double cur_y = 0.0; static double line_width = 2.0; struct color { double red; double green; double blue; }; static struct color bc = {0.95, 0.95, 0.95}; /* white */ static struct color fc = {0.0, 0.0, 0.0}; /* black */ /* cairo */ static cairo_t *cr; gboolean init_cairo (void) { int width, height; cairo_matrix_t matrix; pen = TRUE; angle = 90.0; cur_x = 0.0; cur_y = 0.0; line_width = 2.0; bc.red = 0.95; bc.green = 0.95; bc.blue = 0.95; fc.red = 0.0; fc.green = 0.0; fc.blue = 0.0; if (surface) { width = cairo_image_surface_get_width (surface); height = cairo_image_surface_get_height (surface); 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; cr = cairo_create (surface); cairo_transform (cr, &matrix); cairo_set_source_rgb (cr, bc.red, bc.green, bc.blue); cairo_paint (cr); cairo_set_source_rgb (cr, fc.red, fc.green, fc.blue); cairo_move_to (cr, cur_x, cur_y); return TRUE; } else return FALSE; } void destroy_cairo () { cairo_destroy (cr); } /* ------ RUNTIME ROUTINE ------ */ /* Analyze the tree node of elements. */ /* Eval analyzes an expression node and returns a value of the expression. */ double eval (node_t *node) { double value = 0.0; if (node == NULL) runtime_error ("No expression to evaluate.\n"); #ifdef debug g_printerr ("eval: node type is %s.\n", node_type_table[node->type]); #endif #define calc(op) eval (child1(node)) op eval (child2(node)) switch (node->type) { case N_EQ: value = (double) calc (==); break; case N_GT: value = (double) calc (>); break; case N_LT: value = (double) calc (<); break; case N_ADD: value = calc (+); break; case N_SUB: value = calc (-); break; case N_MUL: value = calc (*); break; case N_DIV: if (eval (child2(node)) == 0.0) runtime_error ("Division by zerp.\n"); else value = calc (/); break; case N_UMINUS: value = -(eval (child1(node))); break; case N_ID: if (! (stack_lookup (name(node), &value)) && ! var_lookup (name(node), &value) ) runtime_error ("Variable %s not defined.\n", name(node)); break; case N_NUM: value = value(node); break; default: runtime_error ("Illegal expression.\n"); } return value; } /* Execute analyzes the tree node except expressions. */ /* For example, statement, procedure and so on. */ /* Such elements don't generate any values. */ /* Return value = 0 or 1 */ /* node_type= N_RT => return 1 */ /* Otherwise => return 0 */ void execute (node_t *node) { double d, x, y; char *name; int n, i; int counter; /* ver 0.5, for repeat procedure */ if (node == NULL) runtime_error ("Node is NULL.\n"); #ifdef debug g_printerr ("execute: node type is %s.\n", node_type_table[node->type]); g_printerr ("----- Current status -----\n"); if (pen) g_printerr (" Pen down.\n"); else g_printerr (" Pen up.\n"); g_printerr (" Angle from x axis is %f.\n", angle); g_printerr (" Current X coordinate is %f.\n", cur_x); g_printerr (" Current Y coordinate is %f.\n", cur_y); g_printerr (" Line width is %f.\n", line_width); g_printerr (" Background color is (%f, %f, %f).\n", bc.red, bc.green, bc.blue); g_printerr (" Foreground color is (%f, %f, %f).\n", fc.red, fc.green, fc.blue); show_symbol_table(); show_stack(); #endif if (proc_level > ret_level) return; switch (node->type) { case N_program: execute (child1(node)); execute (child2(node)); break; case N_PU: pen = FALSE; break; case N_PD: pen = TRUE; break; case N_PW: line_width = eval (child1(node)); /* line width */ break; case N_FD: d = eval (child1(node)); /* distance */ x = d * cos (angle*M_PI/180); y = d * sin (angle*M_PI/180); /* initialize the current point = start point of the line */ cairo_move_to (cr, cur_x, cur_y); cur_x += x; cur_y += y; #ifdef debug g_printerr ("fd: Distance is %f.\n", d); g_printerr ("fd: X-distance is %f.\n", x); g_printerr ("fd: Y-distance is %f.\n", y); g_printerr ("fd: New X coordinate is %f.\n", cur_x); g_printerr ("fd: New Y coordinate is %f.\n", cur_y); #endif cairo_set_line_width (cr, line_width); cairo_set_source_rgb (cr, fc.red, fc.green, fc.blue); if (pen) cairo_line_to (cr, cur_x, cur_y); else cairo_move_to (cr, cur_x, cur_y); cairo_stroke (cr); break; case N_TR: angle -= eval (child1(node)); for (; angle < 0; angle += 360.0); for (; angle>360; angle -= 360.0); break; case N_TL: /* ver 0.5 */ angle += eval (child1(node)); for (; angle < 0; angle += 360.0); for (; angle>360; angle -= 360.0); break; case N_BC: bc.red = eval (child1(node)); bc.green = eval (child2(node)); bc.blue = eval (child3(node)); #define fixcolor(c) c = c < 0 ? 0 : (c > 1 ? 1 : c) fixcolor (bc.red); fixcolor (bc.green); fixcolor (bc.blue); /* clear the shapes and set the background color */ cairo_set_source_rgb (cr, bc.red, bc.green, bc.blue); cairo_paint (cr); break; case N_FC: fc.red = eval (child1(node)); fc.green = eval (child2(node)); fc.blue = eval (child3(node)); fixcolor (fc.red); fixcolor (fc.green); fixcolor (fc.blue); #ifdef debug g_printerr ("fc: Foreground color is (%f, %f, %f).\n", fc.red, fc.green, fc.blue); #endif break; case N_ASSIGN: name = name(child1(node)); d = eval (child2(node)); if (! stack_replace (name, d)) /* First, tries to replace the value in the stack (parameter).*/ var_replace (name, d); /* If the above fails, tries to replace the value in the table. If the variable isn't in the table, installs it, */ break; case N_IF: if (eval (child1(node))) execute (child2(node)); break; case N_RT: ret_level--; break; case N_RS: pen = TRUE; angle = 90.0; cur_x = 0.0; cur_y = 0.0; line_width = 2.0; fc.red = 0.0; fc.green = 0.0; fc.blue = 0.0; /* To change background color, use bc. */ break; case N_RP: /* ver 0.5 */ counter = (int) eval (child1(node)); if (counter < 0) runtime_error ("Repeat number %d is negative.\n", counter); if (counter > 100) runtime_error ("Repeat number %d is too big.\n", counter); for (i=0; i<counter; ++i) execute (child2(node)); break; case N_procedure_call: name = name(child1(node)); node_t *proc = proc_lookup (name); if (! proc) runtime_error ("Procedure %s not defined.\n", name); if (strcmp (name, name(child1(proc))) != 0) runtime_error ("Unexpected error. Procedure %s is called, but invoked procedure is %s.\n", name, name(child1(proc))); /* make tuples (parameter (name), argument (value)) and push them to the stack */ node_t *param_list; node_t *arg_list; param_list = child2(proc); arg_list = child2(node); if (param_list == NULL) { if (arg_list == NULL) { stack_push (NULL, 0.0); /* number of argument == 0 */ } else runtime_error ("Procedure %s has different number of argument and parameter.\n", name); }else { /* Don't change the stack until finish evaluating the arguments. */ #define TEMP_STACK_SIZE 20 char *temp_param[TEMP_STACK_SIZE]; double temp_arg[TEMP_STACK_SIZE]; n = 0; for (; param_list->type == N_parameter_list; param_list = child1(param_list)) { if (arg_list->type != N_argument_list) runtime_error ("Procedure %s has different number of argument and parameter.\n", name); if (n >= TEMP_STACK_SIZE) runtime_error ("Too many parameters. the number must be %d or less.\n", TEMP_STACK_SIZE); temp_param[n] = name(child2(param_list)); temp_arg[n] = eval (child2(arg_list)); arg_list = child1(arg_list); ++n; } if (param_list->type == N_ID && arg_list -> type != N_argument_list) { temp_param[n] = name(param_list); temp_arg[n] = eval (arg_list); if (++n >= TEMP_STACK_SIZE) runtime_error ("Too many parameters. the number must be %d or less.\n", TEMP_STACK_SIZE); temp_param[n] = NULL; temp_arg[n] = (double) n; ++n; } else runtime_error ("Unexpected error.\n"); for (i = 0; i < n; ++i) stack_push (temp_param[i], temp_arg[i]); } ret_level = ++proc_level; execute (child3(proc)); ret_level = --proc_level; stack_return (); break; case N_procedure_definition: name = name(child1(node)); proc_install (name, node); break; case N_primary_procedure_list: execute (child1(node)); execute (child2(node)); break; default: runtime_error ("Unknown statement.\n"); } } static jmp_buf buf; void run (void) { int i; if (! init_cairo()) { g_printerr ("Cairo not initialized.\n"); return; } init_table(); init_stack(); ret_level = proc_level = 1; i = setjmp (buf); if (i == 0) execute(node_top); /* else ... get here by calling longjmp */ #ifdef debug /* statistics */ g_printerr ("------ Statistics ------\n"); g_printerr ("The biggest depth of the symbol table is %d.\n", tp); g_printerr ("The biggest depth of the stack is %d.\n", sp_biggest); #endif destroy_cairo (); g_slist_free_full (g_steal_pointer (&list), g_free); } /* error reporting */ /* format supports only %s, %f and %d */ static void runtime_error (char *format, ...) { va_list args; char *f; char b[3]; char *s; double v; int i; va_start (args, format); for (f = format; *f; f++) { if (*f != '%') { b[0] = *f; b[1] = '\0'; g_printerr ("%s", b); continue; } switch (*++f) { case 's': s = va_arg(args, char *); g_printerr ("%s", s); break; case 'f': v = va_arg(args, double); g_printerr ("%f", v); break; case 'd': i = va_arg(args, int); g_printerr ("%d", i); break; default: b[0] = '%'; b[1] = *f; b[2] = '\0'; g_printerr ("%s", b); break; } } va_end (args); longjmp (buf, 1); }