%{ /* * Gnumeric Parser * * (C) 1998 the Free Software Foundation * * Author: * Miguel de Icaza (miguel@gnu.org) */ #include #include #include #include #include "gnumeric.h" #include "number-match.h" /* Allocation with disposal-on-error */ static void *alloc_buffer (int size); static void register_symbol (Symbol *sym); static void alloc_clean (void); static void alloc_glist (GList *l); static void forget_glist (GList *list); static void forget_tree (ExprTree *tree); static void alloc_list_free (void); static void *v_new (void); #define ERROR -1 /* Types of items we know how to dispose on error */ typedef enum { ALLOC_SYMBOL, ALLOC_VALUE, ALLOC_BUFFER, ALLOC_LIST } AllocType; /* How we keep track of them */ typedef struct { AllocType type; void *data; } AllocRec; /* This keeps a list of AllocRecs */ static GList *alloc_list; /* Debugging */ static void dump_tree (ExprTree *tree); /* Bison/Yacc internals */ static int yylex (void); static int yyerror (char *s); #define p_new(type) ((type *) alloc_buffer ((unsigned) sizeof (type))) static ExprTree * build_binop (ExprTree *l, Operation op, ExprTree *r) { ExprTree *res = p_new (ExprTree); res->ref_count = 1; res->u.binary.value_a = l; res->oper = op; res->u.binary.value_b = r; return res; } %} %union { ExprTree *tree; CellRef *cell; GList *list; Sheet *sheetref; } %type exp %type arg_list %token NUMBER STRING FUNCALL CONSTANT CELLREF GTE LTE NE %token SHEETREF %type cellref %left '&' %left '<' '>' '=' GTE LTE NE %left '-' '+' %left '*' '/' %left NEG PLUS %left '!' %right '^' %% line: exp { parser_result = $1; /* dump_tree (parser_result);*/ alloc_list_free (); } | error { alloc_clean (); parser_error = PARSE_ERR_SYNTAX; } ; exp: NUMBER { $$ = $1 } | STRING { $$ = $1 } | cellref { $$ = $1 } | CONSTANT { $$ = $1 } | exp '+' exp { $$ = build_binop ($1, OPER_ADD, $3); } | exp '-' exp { $$ = build_binop ($1, OPER_SUB, $3); } | exp '*' exp { $$ = build_binop ($1, OPER_MULT, $3); } | exp '/' exp { $$ = build_binop ($1, OPER_DIV, $3); } | exp '^' exp { $$ = build_binop ($1, OPER_EXP, $3); } | exp '&' exp { $$ = build_binop ($1, OPER_CONCAT, $3); } | exp '=' exp { $$ = build_binop ($1, OPER_EQUAL, $3); } | exp '<' exp { $$ = build_binop ($1, OPER_LT, $3); } | exp '>' exp { $$ = build_binop ($1, OPER_GT, $3); } | exp GTE exp { $$ = build_binop ($1, OPER_GTE, $3); } | exp NE exp { $$ = build_binop ($1, OPER_NOT_EQUAL, $3); } | exp LTE exp { $$ = build_binop ($1, OPER_LTE, $3); } | '(' exp ')' { $$ = $2; } | '-' exp %prec NEG { $$ = p_new (ExprTree); $$->ref_count = 1; $$->oper = OPER_NEG; $$->u.value = $2; } | '+' exp %prec PLUS { $$ = $2; } | cellref ':' cellref { CellRef a, b; a = $1->u.ref; b = $3->u.ref; $$ = p_new (ExprTree); $$->ref_count = 1; $$->oper = OPER_CONSTANT; $$->u.constant = v_new (); $$->u.constant->type = VALUE_CELLRANGE; $$->u.constant->v.cell_range.cell_a = a; $$->u.constant->v.cell_range.cell_b = b; forget_tree ($1); forget_tree ($3); } | FUNCALL '(' arg_list ')' { $$ = $1; $$->u.function.arg_list = $3; } ; cellref: CELLREF { $$ = $1; } | SHEETREF '!' CELLREF { $$ = $3; $$->u.ref.sheet = $1; } ; arg_list: exp { $$ = g_list_prepend (NULL, $1); alloc_glist ($$); } | exp ',' arg_list { forget_glist ($3); $$ = g_list_prepend ($3, $1); alloc_glist ($$); } | { $$ = NULL; } ; %% static int return_cellref (char *p) { int col_relative = TRUE; int row_relative = TRUE; int col = 0; int row = 0; ExprTree *e; CellRef *ref; /* Try to parse a column */ if (*p == '$'){ col_relative = FALSE; p++; } if (!(toupper (*p) >= 'A' && toupper (*p) <= 'Z')) return 0; col = toupper (*p++) - 'A'; if (toupper (*p) >= 'A' && toupper (*p) <= 'Z') col = (col+1) * ('Z'-'A'+1) + toupper (*p++) - 'A'; /* Try to parse a row */ if (*p == '$'){ row_relative = FALSE; p++; } if (!(*p >= '1' && *p <= '9')) return 0; while (isdigit (*p)){ row = row * 10 + *p - '0'; p++; } row--; /* Ok, parsed successfully, create the return value */ e = p_new (ExprTree); e->ref_count = 1; e->oper = OPER_VAR; ref = &e->u.ref; /* Setup the cell reference information */ if (row_relative) ref->row = row - parser_row; else ref->row = row; if (col_relative) ref->col = col - parser_col; else ref->col = col; ref->col_relative = col_relative; ref->row_relative = row_relative; ref->sheet = parser_sheet; yylval.tree = e; return CELLREF; } static int return_sheetref (Sheet *sheet) { yylval.sheetref = sheet; return SHEETREF; } static int make_string_return (char *string) { ExprTree *e; Value *v; double fv; int type; char *format; e = p_new (ExprTree); e->ref_count = 1; v = v_new (); /* * Try to match the entered text against any * of the known number formating codes, if this * succeeds, we store this as a float + format, * otherwise, we return a string. */ if (format_match (string, &fv, &format)){ v->type = VALUE_FLOAT; v->v.v_float = fv; if (!parser_desired_format) parser_desired_format = format; } else { v->v.str = string_get (string); v->type = VALUE_STRING; } e->oper = OPER_CONSTANT; e->u.constant = v; yylval.tree = e; return STRING; } static int return_symbol (Symbol *sym) { ExprTree *e = p_new (ExprTree); int type = STRING; e->ref_count = 1; symbol_ref (sym); switch (sym->type){ case SYMBOL_FUNCTION: e->oper = OPER_FUNCALL; type = FUNCALL; e->u.function.symbol = sym; e->u.function.arg_list = NULL; break; case SYMBOL_VALUE: case SYMBOL_STRING: { Value *v, *dv; /* Make a copy of the value */ dv = (Value *) sym->data; v = v_new (); value_copy_to (v, dv); e->oper = OPER_CONSTANT; e->u.constant = v; type = CONSTANT; break; } } /* switch */ register_symbol (sym); yylval.tree = e; return type; } /** * try_symbol: * @string: the string to try. * @try_cellref: whether we know for sure if it is not a cellref * * Attempts to figure out what @string refers to. * if @try_cellref is TRUE it will also attempt to match the * string as a cellname reference. */ static int try_symbol (char *string, gboolean try_cellref) { Symbol *sym; int v; if (try_cellref){ v = return_cellref (string); if (v) return v; } sym = symbol_lookup (global_symbol_table, string); if (sym) return return_symbol (sym); else { Sheet *sheet; sheet = sheet_lookup_by_name (parser_sheet, string); if (sheet) return return_sheetref (sheet); } return make_string_return (string); } int yylex (void) { int c; char *p, *tmp; int is_float; while(isspace (*parser_expr)) parser_expr++; c = *parser_expr++; if (c == '(' || c == ',' || c == ')') return c; switch (c){ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.': { ExprTree *e = p_new (ExprTree); Value *v = v_new (); e->ref_count = 1; is_float = c == '.'; p = parser_expr-1; tmp = parser_expr; while (isdigit (*tmp) || (!is_float && *tmp=='.' && ++is_float)) tmp++; if (*tmp == 'e' || *tmp == 'E') { is_float = 1; tmp++; if (*tmp == '-' || *tmp == '+') tmp++; while (isdigit (*tmp)) tmp++; } /* Ok, we have skipped over a number, now load its value */ if (is_float){ v->type = VALUE_FLOAT; float_get_from_range (p, tmp, &v->v.v_float); } else { v->type = VALUE_INTEGER; int_get_from_range (p, tmp, &v->v.v_int); } /* Return the value to the parser */ e->oper = OPER_CONSTANT; e->u.constant = v; yylval.tree = e; parser_expr = tmp; return NUMBER; } case '\'': case '"': { char *string, *s; int v; char quotes_end = c; p = parser_expr; while(*parser_expr && *parser_expr != quotes_end) { if (*parser_expr == '\\' && parser_expr [1]) parser_expr++; parser_expr++; } if(!*parser_expr){ parser_error = PARSE_ERR_NO_QUOTE; return ERROR; } s = string = (char *) alloca (1 + parser_expr - p); while (p != parser_expr){ if (*p== '\\'){ p++; *s++ = *p++; } else *s++ = *p++; } *s = 0; parser_expr++; v = try_symbol (string, FALSE); return v; } } if (isalpha (c) || c == '_' || c == '$'){ char *start = parser_expr - 1; char *str; int len; while (isalnum (*parser_expr) || *parser_expr == '_' || *parser_expr == '$') parser_expr++; len = parser_expr - start; str = alloca (len + 1); strncpy (str, start, len); str [len] = 0; return try_symbol (str, TRUE); } if (c == '\n') return 0; if (c == EOF) return 0; if (c == '<'){ if (*parser_expr == '='){ parser_expr++; return LTE; } if (*parser_expr == '>'){ parser_expr++; return NE; } return c; } if (c == '>'){ if (*parser_expr == '='){ parser_expr++; return GTE; } return c; } return c; } int yyerror (char *s) { printf ("Error: %s\n", s); return 0; } static void alloc_register (void *a_info) { alloc_list = g_list_prepend (alloc_list, a_info); } static void register_symbol (Symbol *sym) { AllocRec *a_info = g_new (AllocRec, 1); a_info->type = ALLOC_SYMBOL; a_info->data = sym; alloc_register (a_info); } void * alloc_buffer (int size) { AllocRec *a_info = g_new (AllocRec, 1); char *res = g_malloc (size); a_info->type = ALLOC_BUFFER; a_info->data = res; alloc_register (a_info); return res; } static void * v_new (void) { AllocRec *a_info = g_new (AllocRec, 1); char *res = g_malloc (sizeof (Value)); a_info->type = ALLOC_VALUE; a_info->data = res; alloc_register (a_info); return res; } static void alloc_clean (void) { GList *l = alloc_list; for (; l; l = l->next){ AllocRec *rec = l->data; switch (rec->type){ case ALLOC_BUFFER: g_free (rec->data); break; case ALLOC_SYMBOL: symbol_unref ((Symbol *)rec->data); break; case ALLOC_VALUE: value_release ((Value *)rec->data); break; case ALLOC_LIST: g_list_free ((GList *) rec->data); break; } g_free (rec); } g_list_free (l); alloc_list = NULL; } static void alloc_list_free (void) { GList *l = alloc_list; for (; l; l = l->next) g_free (l->data); g_list_free (l); alloc_list = NULL; } static void alloc_glist (GList *list) { AllocRec *a_info = g_new (AllocRec, 1); a_info->type = ALLOC_LIST; a_info->data = list; alloc_register (a_info); } static void forget (AllocType type, void *data) { GList *l; for (l = alloc_list; l; l = l->next){ AllocRec *a_info = (AllocRec *) l->data; if (a_info->type == type && a_info->data == data){ alloc_list = g_list_remove_link (alloc_list, l); return; } } } static void forget_glist (GList *list) { forget (ALLOC_LIST, list); } static void forget_tree (ExprTree *tree) { forget (ALLOC_BUFFER, tree); expr_tree_unref (tree); } void value_dump (Value *value) { switch (value->type){ case VALUE_STRING: printf ("STRING: %s\n", value->v.str->str); break; case VALUE_INTEGER: printf ("NUM: %d\n", value->v.v_int); break; case VALUE_FLOAT: printf ("Float: %f\n", value->v.v_float); break; case VALUE_ARRAY: { GList *l; printf ("Array: { "); for (l = value->v.array; l; l = l->next){ value_dump (l->data); } printf ("}\n"); } default: printf ("Unhandled item type\n"); } } void dump_tree (ExprTree *tree) { Symbol *s; CellRef *cr; switch (tree->oper){ case OPER_VAR: cr = &tree->u.constant->v.cell; printf ("Cell: %s%c%s%d\n", cr->col_relative ? "" : "$", cr->col + 'A', cr->row_relative ? "" : "$", cr->row + '1'); return; case OPER_CONSTANT: value_dump (tree->u.constant); return; case OPER_FUNCALL: s = symbol_lookup (global_symbol_table, tree->u.function.symbol->str); printf ("Function call: %s\n", s->str); break; case OPER_EQUAL: case OPER_NOT_EQUAL: case OPER_LT: case OPER_LTE: case OPER_GT: case OPER_GTE: case OPER_ADD: case OPER_SUB: case OPER_MULT: case OPER_DIV: case OPER_EXP: case OPER_CONCAT: dump_tree (tree->u.binary.value_a); dump_tree (tree->u.binary.value_b); switch (tree->oper){ case OPER_ADD: printf ("ADD\n"); break; case OPER_SUB: printf ("SUB\n"); break; case OPER_MULT: printf ("MULT\n"); break; case OPER_DIV: printf ("DIV\n"); break; case OPER_CONCAT: printf ("CONCAT\n"); break; case OPER_EQUAL: printf ("==\n"); break; case OPER_NOT_EQUAL: printf ("!=\n"); break; case OPER_LT: printf ("<\n"); break; case OPER_GT: printf (">\n"); break; case OPER_GTE: printf (">=\n"); break; case OPER_LTE: printf ("<=\n"); break; case OPER_EXP: printf ("EXP\n"); break; default: printf ("Error\n"); } break; case OPER_NEG: dump_tree (tree->u.value); printf ("NEGATIVE\n"); break; } }