/
evo
/
Gtk4-tutorial
Обзор
Документация
Войти
/
evo
/
Gtk4-tutorial
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
v0.8
src/turtle/turtle.y
858 строк
21 KB
Toshio Sekiya
Add sec23.
06 мар 2021, 18:19
06 мар 2021, 18:19
e85d355
Код
Авторство
О чём код?
/* There are two phases to run the program. 1. parsing and generate a tree. Node is a joint to connect childrens' node to itself. There are two types of nodes, nonterminal 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 "turtle.h" /* error reporting */ static void yyerror (char const *s) { /* for syntax error */ g_print ("%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_BC, N_FC, N_DP, N_ASSIGN, N_IF, N_RT, N_RS, 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_BC", "N_FC", "N_DP", "N_ASSIGN", "N_IF", "N_RT", "N_RS", "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 BC %token FC %token DP %token IF %token RT %token RS /* constant */ %token <double> NUM /* identirier */ %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_print ("program: node_top is NULL.\n"); else g_print ("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); } | 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); } /* 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 }; typedef union _object_t object_t; union _object_t { node_t *node; double value; }; struct { int type; char *name; object_t 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, object_t object) { 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 = object.node; else table[tp++].object.value = object.value; } } void proc_install (char *name, node_t *node) { object_t object; object.node = node; tbl_install (PROC, name, object); } void var_install (char *name, double value) { object_t object; object.value = value; tbl_install (VAR, name, object); } 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_print ("****** Symbol table ******\n"); g_print (" Table pointer (tp) = %d.\n", tp); g_print (" ** Contents **\n"); for (i=0; i<tp; ++i) if (table[i].type == PROC) g_print ("PROC, %s, %lx\n", table[i].name, (long int) table[i].object.node); else g_print ("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_print ("****** Stack ******\n"); g_print (" Stack pointer (sp) = %d.\n", sp); g_print (" ** Contents **\n"); for (i=0; i<sp; ++i) if (stack[i].name) g_print ("%s, %f\n", stack[i].name, stack[i].value); else g_print ("(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; 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; width = cairo_image_surface_get_width (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.yx = 0.0; matrix.yy = -1.0; matrix.y0 = (double) height / 2.0; if (surface) { 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_print ("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; if (node == NULL) runtime_error ("Node is NULL.\n"); #ifdef debug g_print ("execute: node type is %s.\n", node_type_table[node->type]); g_print ("----- Current status -----\n"); if (pen) g_print (" Pen down.\n"); else g_print (" Pen up.\n"); g_print (" Angle from x axis is %f.\n", angle); g_print (" Current X coordinate is %f.\n", cur_x); g_print (" Current Y coordinate is %f.\n", cur_y); g_print (" Line width is %f.\n", line_width); g_print (" Background color is (%f, %f, %f).\n", bc.red, bc.green, bc.blue); g_print (" 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_print ("fd: Distance is %f.\n", d); g_print ("fd: X-distance is %f.\n", x); g_print ("fd: Y-distance is %f.\n", y); g_print ("fd: New X coordinate is %f.\n", cur_x); g_print ("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_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_print ("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_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_print ("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_print ("------ Statistics ------\n"); g_print ("The biggest depth of the symbol table is %d.\n", tp); g_print ("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_print ("%s", b); continue; } switch (*++f) { case 's': s = va_arg(args, char *); g_print ("%s", s); break; case 'f': v = va_arg(args, double); g_print("%f", v); break; case 'd': i = va_arg(args, int); g_print("%d", i); break; default: b[0] = '%'; b[1] = *f; b[2] = '\0'; g_print ("%s", b); break; } } va_end (args); longjmp (buf, 1); }