/*************************************************************************** expression-parser.c - description ------------------- begin : Wednesday June 21 2000 email : tboldt@attglobal.net Author : Terry D. Boldt ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ /* * Functions to parse arthmetic expressions * 6-21-2000 */ /* Modified to support functions - Summer, 2002 -- jsled@asynchronous.org */ /* expression parser/evaluator use: * * Before describing the parser per se, I want to describe the * structures used to contain the results returned from the * parser. The structure is defined in "finvar.h": * * typedef struct var_store *var_store_ptr; * * typedef struct var_store { * char *variable_name; * char use_flag; * char assign_flag; * void *value; * var_strore_ptr next_var; * } var_store; * * The "use_flag" variable is for internal use of the parser and can * be ignored by the user. The "variable_name" variable possibly * points to a string containing the name of the value returned, a * "variable name". If NULL, then this is a temporary value. The * "value" variable points to a user defined structure containing the * numeric value of the variable. * * As well, variables now have a VarStoreType, to distinguish between numeric * and string values, as we want string arguments to functions. * * In designing and writing the parser, I decided early on that the * parser should be an "expression parser/evaluator" and that the * actual arithmetic was the responsibility of the caller/user. * * I decided that the parser should be totally independent of the * numeric representation used, and thus the exact details of how the * arithmetic was performed. To accomplish this, four functions are * supplied by the user/caller: * * 1: trans_numeric - this function translates the text string into a * numeric in the desired representation and returns a pointer to the * representation as a (void *) this function has four parameters * passed: * * 1: digit_str -- the actual text string of the * numeric to be converted to the internal * representation * * 2: radix_point -- the ASCII character used to * represent the radix point * * 3: group character -- the ASCII character used * to separate and group digits to the left of the * radix * * 4: rstr -- a pointer to a location in which to * return a pointer to the first character not * part of the numeric string translated If this * pointer is NULL, do not return a value. This * parameter is the same as the second parameter * of the standard C library functions "strtod" or * "strtol" * * 2: numeric_ops - this function does the actual arithmetic on two * numeric quantities in internal representation. It has three * parameters passed: * * 1: op_sym -- the numeric operation to be * performed. The possible values are defined * in "finvar.h" and are: * * ADD_OP - addition * SUB_OP - subtraction * DIV_OP - division * MUL_OP - multiplication * ASN_OP - assignment * * 2: left_value - the left hand operand of the * binary operator * * 3: right_value - the right hand operand of * the binary operator Note: left_value and * right_value are passed as (void *). This * function is responsible for casting to the * proper type to use. Note: this function should * make no assumptions about overwriting or * re-using either left_value or right_value, * except for ASN_OP. Both values passed must be * left unchanged by any operation except ASN_OP. * This function is also responsible for * allocating/freeing memory as necessary to * perform the designated function and returning * the result. I STRONGLY suggest that the result * be returned in dynamically allocated memory. If * static memory is used, the parser has no means * of copying the returned result or managing * static memory to prevent overwriting the result * and invalidating the result. * * 3: negate_numeric - this function negates the value passed (as a (void *)) * * 4: free_numeric - this function is responsible for freeing memory * used by the internal numeric representation. * * 5: func_op - this function is responsible for handling function calls. * * I have included the file "numeric_ops.c" containing the above * functions for the usual "double" and "int" representation of * numerics. The functions perform integer or floating point * operations as appropriate for the string entered by the user. The * division operation is done in "double" since I do not think that * anybody really wants (9 / 2) to equal 4 instead of 4.5 for * financial operations. These functions use the structure defined in * finvar.h: * * typedef struct numeric *numeric_ptr; * typedef struct numeric { * char type; * union { * long int int_value; * double dbl_value; * } value; * } numeric; * * to contain all numeric values. The variable "type" in this * structure can have the values: * * INT_TYPE * DBL_TYPE * * which are defined in "finvar.h". * * All "named variables", variables defined by the user for storing * intermediate results for future reference/use, and temporary * variables used by the parser use the variable storage structure, * var_store, defined above. The result of parsing and evaluating the * string passed are returned in a variable storage structure * specified by the caller. * * If the returned variable value is not named, i.e., "variable_name * == NULL", then the user/caller is responsible for freeing the * memory used by the internal representation of the numeric value. * If, however, "variable_name != NULL", freeing the memory used by * the internal numeric representation will cause a segmentation fault * later, when the parser attempts to free the memory through a call * to "free_numeric". In addition, freeing the memory will probably * invalidate the numeric value contained therein and lead to * pernicuous results when the value is used. * * If "variable_name != NULL", the user/caller should never attempt to * free this memory, that is the sole responsibility of the parser. * * It may be that the calling function has certain "variables" that * need to be "pre-defined" for the user to manipulate. In essence * the function "pre-defining" variables sets up a linked list of * variable storage structures with the proper "names" and numeric * values. The number of "pre-defined" variables and a pointer to the * structure array is passed to the parser in the initialization * call. After the parser is eventually exited, the calling function * is responsible for freeing any memory used by the "pre-defined" * variables and their final numeric representation. * * There may also be strings in the expression, by quoting them in '"' * characters. These are intended to be passed literally into functions; the * result of using a string in a numeric operation is undefined. Presently, * the expression-parser code does not check the variable types during * parsing or evaluation. * * A second design goal of the parser was that it should be callable * concurrently by multiple modules independently. That each module * should be capable of using differing "pre-defined" variables and * user defined variables and even internal numeric representations. * To that end the calling module must first initialize the parser * with a call to "init_parser". This call creates the parser * internal structure for subsequent calls to the parser proper. The * structure created and returned must then be passed to subsequent * calls to the parser. When no further calls to the parser are to be * made, the module then calls "exit_parser" with the pointer returned * by "init_parser", so that the parser may release dynamically * allocated memory. * * The parser recognizes the following binary operators: * * + * - * / * * * = * += * -= * /= * *= * * In addition, the unary operators * * + * - * * are recognized. All numerics are initially recognized as positive * numbers. If negative, the unary '-' operator is applied. This saves * the logic of having to recognize strings as * * -123 * * The logic recognizes "-" and "123" separately. The '-' unary * operator is then applied to negate the numeric. This also has the * advantage that the same logic can be used for * * -123 * +123.45 * +uvar * -uvar * * In each case, the appropriate unary operator is applied to obtain * the desired * result with no increase in the parsing logic. Thus * keeping things as simple as possible. * * The parser also follows the C practice that the assignment * operators return a value. Thus, allowing multiple assignments and * assignment within expressions. The following expressions are all * valid: * * nni = 123 * hnk = nni = 23.45 * jkl = 5 * (nj = 68.9) * * The first time variables are used in an expression, they are * initialized to zero, 0. Thus, even if the following variables have * not been assigned a value previously, the following expressions are * valid: * * nni *= 123 * above results in zero in nni * jk += 45.6 * above results in 45.6 in jk * 56.8 - tyh * result of above is 56.8 * tgh - 45.7 * above the same as * -45.7 * * After parsing the above expressions the variables nni, jk, tyh and * tgh would all be defined. * * Functions are invoked with expressions of the format * * [_a-zA-Z]( <argument_0> : <argument_1> : ... : <argument_n> ) * * where each argument can itself be a sub-expression [arithmetic operation * or function call]. * * * There are six parser functions needed to use the parser/evaluator: * * Note: in the last five functions, in the function parameter (void * *vp), "vp" is the pointer returned by the "init_parser" function. * * void *init_parser(var_store_ptr predefined_vars, * gchar *radix_point, * gchar *group_char, * void *trans_numeric(char *digit_str, * gchar *radix_point, * gchar *group_char, * char **rstr), * void *numeric_ops(char op_sym, * void *left_value, * void *right_value), * void *negate_numeric(void *value), * void free_numeric(void *numeric_value), * void *func_op(const char *fname, int argc, void **argv)); * * This function is called by the module/function/whatever to * initialize the parser. The parser returns a pointer to a * structure that contains all relevant information for * parsing strings. The pointer is returned as (void *) * since all information is and should remain pertinent only * to the parser. The calling function(s) should never rely on * manipulating any information inside this structure * directly, since it may and could change in the future. -- * The first parameter is a pointer to the first element in * a linked list of "pre-defined" variables the caller wishes * to use with subsequent calls to the parser. -- The second * parameter is the radix character to use in numeric strings * in subsequent calls to the parser. -- the third parameter * is the optional character used for grouping digits to the * left of the radix. -- The fourth, fifth, sixth and seventh * parameters are the functions I described above for the * internal numeric representation desired by the calling * function(s). * * void exit_parser( * void *vp); * * This function is called to exit the parser and free all * dynamically allocated memory used by the parser for an * internal stack and user defined variables. * * unsigned get_parse_error( * void *vp); * * If the parser is successful in complete parsing and * evaluating the string passed to 'parse_string' below, that * functions returns a NULL pointer. If, however, an error is * encountered in parsing/evaluating the string, the * 'parse_string' function returns a pointer to the character * which caused the error. This call returns an unsigned * integer designating the error encountered. The possible * values are defined in the "finvar.h" file. * * var_store_ptr parser_get_vars( * void *vp) * * This function returns a pointer to the first element of a * linked list of variable storage structures containing the * user defined named variables if any exist. NULL is * returned if none exist. The calling function should not * alter the variable names. The numeric values may be * altered if the calling function author really knows what * they are doing. * * unsigned delete_var( * char *var_name, * void *vp); * * This function will delete the user defined named variable * with a name identical to the name string passed in the * first parameter. If no user defined variable exists with an * identical name, zero, 0, is returned. If the delete * operation is successful, one, 1, is returned. * * char *parse_string( * var_store_ptr value, * char *string, * void *vp); * * This function parses the string passed in the second * parameter and returns a pointer to the last character not * recognized upon a parsing error. If no error occurred, NULL * is returned. The first parameter is a pointer to a variable * storage structure to contain the result of the * parser/evaluator. * * Note: The parser/evaluator uses a simple recursive descent * parser. I decided on this type for the simple reason that for a * simple four function calculator a recursive descent parser is, in * my opinion, the easiest to construct. I also think that recursive * descent parsers are easier for the human to understand and thus * maintain. * * Also, the parser uses a stack which is dynamically allocated in * memory and can grow as needed. I have not provided any mechanism * for shrinking the stack. The initial stack size is set at 50 * slots. I really do not anticipate that under normal and even most * extreme cases, that it will ever approach that size in actual * use. Under "normal" operation, the stack will probably never exceed * 3 or 4 slots in size and 50 slots is probably an overkill for * normal use. However, since the stack is pointers and not entire * structures, a stack size of 50 slots is not that much memory and * can be tolerated by most users. Thus, a mechanism for shrinking the * stack will probably never be needed. */ #include <config.h> #include <ctype.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <glib.h> #include "qof.h" #define EXPRESSION_PARSER_STATICS #include "finvar.h" #define MAX_FUNC_ARG_LEN 255 /* structure to hold parser environment - environment particular to * each caller */ typedef struct parser_env { unsigned stack_cnt; unsigned stack_size; var_store_ptr *stack; var_store_ptr predefined_vars; var_store_ptr named_vars; var_store_ptr unnamed_vars; const char *parse_str; gchar *radix_point; gchar *group_char; char name[128]; char Token; char asn_op; char *tokens; char *token_tail; ParseError error_code; void *numeric_value; void *(*trans_numeric) (const char *digit_str, gchar *radix_point, gchar *group_char, char **rstr); void *(*numeric_ops) (char op_sym, void *left_value, void *right_value); void *(*negate_numeric) (void *value); void (*free_numeric) (void *numeric_value); void *(*func_op)( const char *fname, int argc, void **argv ); } parser_env; #include "finproto.h" #include "fin_static_proto.h" #include "fin_spl_protos.h" #define FN_TOKEN 'F' #define ARG_TOKEN ':' #define VAR_TOKEN 'V' #define NUM_TOKEN 'I' #define STR_TOKEN '"' #define STACK_INIT 50 #define UNNAMED_VARS 100 #define NAMED_INCR 5 static char allowed_operators[] = "+-*/()=:"; parser_env_ptr init_parser (var_store_ptr predefined_vars, gchar *radix_point, gchar *group_char, void *trans_numeric (const char *digit_str, gchar *radix_point, gchar *group_char, char **rstr), void *numeric_ops (char op_sym, void *left_value, void *right_value), void *negate_numeric (void *value), void free_numeric (void *numeric_value), void *func_op( const char *fname, int argc, void **argv )) { parser_env_ptr pe = g_new0 (parser_env, 1); pe->predefined_vars = predefined_vars; pe->stack = g_new0 (var_store_ptr, STACK_INIT); pe->stack_size = STACK_INIT; pe->radix_point = radix_point; pe->group_char = group_char; pe->numeric_value = NULL; pe->trans_numeric = trans_numeric; pe->numeric_ops = numeric_ops; pe->negate_numeric = negate_numeric; pe->free_numeric = free_numeric; pe->func_op = func_op; return pe; } /* init_parser */ void exit_parser (parser_env_ptr pe) { var_store_ptr vars, bv; if (pe == NULL) return; for (vars = pe->named_vars; vars; vars = bv) { g_free (vars->variable_name); vars->variable_name = NULL; if (vars->value) pe->free_numeric (vars->value); vars->value = NULL; bv = vars->next_var; g_free (vars); } /* endfor */ pe->named_vars = NULL; g_free (pe->stack); pe->stack = NULL; g_free (pe->tokens); pe->tokens = NULL; pe->token_tail = NULL; if (pe->numeric_value) pe->free_numeric (pe->numeric_value); pe->numeric_value = NULL; g_free (pe); } /* exit_parser */ /* return parser error code */ ParseError get_parse_error (parser_env_ptr pe) { if (pe == NULL) return PARSER_NO_ERROR; return pe->error_code; } /* get_parse_error */ /* return linked list of named variables which have been defined */ var_store_ptr parser_get_vars (parser_env_ptr pe) { if (pe == NULL) return NULL; return pe->named_vars; } /* get_vars */ /* function to delete variable with specified name from named variables * if it exists. If it exists return TRUE, 1, else return FALSE, 0 */ unsigned delete_var (char *var_name, parser_env_ptr pe) { unsigned ret = FALSE; var_store_ptr nv, tv; if (pe == NULL) return FALSE; for (nv = pe->named_vars, tv = NULL; nv; tv = nv, nv = nv->next_var) { if (strcmp (nv->variable_name, var_name) == 0) { if (tv) tv->next_var = nv->next_var; else pe->named_vars = nv->next_var; g_free (nv->variable_name); nv->variable_name = NULL; pe->free_numeric (nv->value); nv->value = NULL; g_free (nv); ret = TRUE; break; } /* endif */ } /* endfor */ return ret; } /* delete_var */ /* parse string passed using parser environment passed return * evaluated value in numeric structure passed, return NULL if no * parse error. If parse error, return pointer to character at which * error occurred. */ char * parse_string (var_store_ptr value, const char *string, parser_env_ptr pe) { var_store_ptr retv; var_store unnamed_vars[UNNAMED_VARS]; if (!pe || !string) return NULL; pe->unnamed_vars = unnamed_vars; memset (unnamed_vars, 0, UNNAMED_VARS * sizeof (var_store)); pe->parse_str = string; pe->error_code = PARSER_NO_ERROR; g_free (pe->tokens); pe->tokens = g_new0(char, strlen (string) + 1); pe->token_tail = pe->tokens; next_token (pe); if (!pe->error_code) assignment_op (pe); if (!pe->error_code) { /* interpret (num) as -num */ if (strcmp (pe->tokens, "(I)") == 0) { var_store_ptr val; val = pop (pe); pe->negate_numeric (val->value); push (val, pe); } } if (pe->Token == EOS) { if ((pe->stack_cnt) && (retv = pop (pe))) { if (value != NULL) *value = *retv; pe->parse_str = NULL; } else pe->error_code = STACK_UNDERFLOW; } pe->stack_cnt = 0; pe->unnamed_vars = NULL; return (char *) pe->parse_str; } /* expression */ /* pop value off value stack */ static var_store_ptr pop (parser_env_ptr pe) { var_store_ptr val; if (pe->stack_cnt) val = pe->stack[--(pe->stack_cnt)]; else { val = NULL; pe->error_code = STACK_UNDERFLOW; } /* endif */ return val; } /* pop */ /* push value onto value stack */ static var_store_ptr push (var_store_ptr push_value, parser_env_ptr pe) { if (pe->stack_cnt > pe->stack_size) { pe->stack_size += STACK_INIT; pe->stack = g_realloc (pe->stack, pe->stack_size * sizeof (var_store_ptr)); } /* endif */ pe->stack[(pe->stack_cnt)++] = push_value; return push_value; } /* push */ /* get/set variable with specified name - nothing fancy just scan each * variable in linked list checking for a string match return variable * found if match create new variable if none found */ static var_store_ptr get_named_var (parser_env_ptr pe) { var_store_ptr retp = NULL, bv; for (retp = pe->predefined_vars, bv = NULL; retp; retp = retp->next_var) if (strcmp (retp->variable_name, pe->name) == 0) break; if (!retp && pe->named_vars) for (retp = pe->named_vars; retp; bv = retp, retp = retp->next_var) if (strcmp (retp->variable_name, pe->name) == 0) break; if (!retp) { retp = g_new0 (var_store, 1); if (!pe->named_vars) pe->named_vars = retp; else bv->next_var = retp; retp->variable_name = g_strdup (pe->name); retp->type = VST_NUMERIC; retp->value = pe->trans_numeric ("0", pe->radix_point, pe->group_char, NULL); } return retp; } /* get_var */ /* get un-named temporary variable */ static var_store_ptr get_unnamed_var (parser_env_ptr pe) { var_store_ptr retp = NULL; unsigned cntr; for (cntr = 0; cntr < UNNAMED_VARS; cntr++) if (pe->unnamed_vars[cntr].use_flag == UNUSED_VAR) { retp = &(pe->unnamed_vars[cntr]); retp->variable_name = NULL; retp->use_flag = USED_VAR; retp->type = VST_NUMERIC; if (retp->value) { pe->free_numeric (retp->value); retp->value = NULL; } /* endif */ break; } /* endif */ if (retp == NULL) pe->error_code = PARSER_OUT_OF_MEMORY; return retp; } /* get_unnamed_var */ /* mark un-named temporary variable unused */ static void free_var (var_store_ptr value, parser_env_ptr pe) { if (value == NULL) return; /* first check that not a named variable */ if (value->variable_name != NULL) return; value->use_flag = UNUSED_VAR; if (value->value) { pe->free_numeric (value->value); value->value = NULL; } } /* free_var */ static void add_token (parser_env_ptr pe, char token) { pe->Token = token; if ((token != EOS) || (*pe->token_tail != EOS)) { *pe->token_tail = token; pe->token_tail++; } } /* parse next token from string */ static void next_token (parser_env_ptr pe) { char *nstr; const char *str_parse = pe->parse_str; void *number; while (isspace (*str_parse)) str_parse++; pe->asn_op = EOS; /* test for end of string */ if (!*str_parse) { add_token (pe, EOS); } /* test for possible operator */ else if (strchr (allowed_operators, *str_parse)) { add_token (pe, *str_parse++); if (*str_parse == ASN_OP) { /* BUG/FIXME: this seems to allow '(=' and ')=' [?], neither of which * make sense. */ if (pe->Token != ASN_OP) { str_parse++; pe->asn_op = pe->Token; add_token (pe, ASN_OP); } else pe->error_code = UNDEFINED_CHARACTER; } /* endif */ } /* test for string */ else if ( *str_parse == '"' ) { nstr = pe->name; /* skip over the '"'. */ str_parse++; do { *nstr++ = *str_parse++; } while ( *str_parse != '"' ); *nstr = EOS; str_parse++; add_token( pe, STR_TOKEN ); } /* test for name */ else if (isalpha (*str_parse) || (*str_parse == '_')) { int funcFlag = 0; /* Check for variable or function */ /* If variable: add token. */ /* If function: parse args, build struct, add token. */ nstr = pe->name; do { if ( *str_parse == '(' ) { funcFlag = 1; str_parse++; break; } *nstr++ = *str_parse++; } while ((*str_parse == '_') || (*str_parse == '(') || isalpha (*str_parse) || isdigit (*str_parse)); *nstr = EOS; if ( funcFlag ) { add_token(pe, FN_TOKEN); } else { add_token(pe, VAR_TOKEN); } } /* test for numeric token */ else if ((number = pe->trans_numeric (str_parse, pe->radix_point, pe->group_char, &nstr))) { add_token (pe, NUM_TOKEN); pe->numeric_value = number; str_parse = nstr; } /* unrecognized character - error */ else { add_token (pe, *str_parse); pe->error_code = UNDEFINED_CHARACTER; } /* endif */ pe->parse_str = str_parse; } /* next_token */ /* evaluate assignment operators, * = * += * -= * \= * *= */ /* FIXME: add non-numeric checking. */ static void assignment_op (parser_env_ptr pe) { var_store_ptr vl; /* left value */ var_store_ptr vr; /* right value */ char ao; add_sub_op (pe); if (pe->error_code) return; while (pe->Token == ASN_OP) { vl = pop (pe); if (pe->error_code) return; ao = pe->asn_op; if (vl->variable_name) { next_token (pe); if (pe->error_code) { free_var (vl, pe); return; } assignment_op (pe); if (pe->error_code) { free_var (vl, pe); return; } vr = pop (pe); if (pe->error_code) { free_var (vl, pe); return; } vl->assign_flag = ASSIGNED_TO; if (ao) { void *temp; temp = vl->value; vl->value = pe->numeric_ops (ao, vl->value, vr->value); pe->free_numeric (temp); } else if (vl != vr) { if (!vr->variable_name) { pe->free_numeric (vl->value); vl->value = vr->value; vr->value = NULL; } else { pe->numeric_ops (ASN_OP, vl->value, vr->value); } free_var (vr, pe); } /* endif */ push (vl, pe); } else { add_token (pe, EOS); /* error !!!!!!!!!! */ pe->error_code = NOT_A_VARIABLE; free_var (vl, pe); } /* endif */ } /* endwhile */ } /* assignment_op */ /* evaluate addition, subtraction operators */ /* FIXME: add non-numeric checking. */ static void add_sub_op (parser_env_ptr pe) { var_store_ptr vl; /* left value */ var_store_ptr vr; /* right value */ var_store_ptr rslt; /* result */ char op; multiply_divide_op (pe); if (pe->error_code) return; while ((pe->Token == ADD_OP) || (pe->Token == SUB_OP)) { op = pe->Token; vl = pop (pe); if (pe->error_code) return; next_token (pe); if (pe->error_code) { free_var (vl, pe); return; } multiply_divide_op (pe); if (pe->error_code) { free_var (vl, pe); return; } vr = pop (pe); if (pe->error_code) { free_var (vl, pe); return; } rslt = get_unnamed_var (pe); if (pe->error_code) { free_var (vl, pe); free_var (vr, pe); return; } rslt->value = pe->numeric_ops (op, vl->value, vr->value); free_var (vl, pe); free_var (vr, pe); push (rslt, pe); } /* endwhile */ } /* add_sub_op */ /* evaluate multiplication, division operators */ /* FIXME: add non-numeric checking. */ static void multiply_divide_op (parser_env_ptr pe) { var_store_ptr vl; /* left value */ var_store_ptr vr; /* right value */ var_store_ptr rslt; /* result */ char op; primary_exp (pe); if (pe->error_code) return; while ((pe->Token == MUL_OP) || (pe->Token == DIV_OP)) { op = pe->Token; vl = pop (pe); if (pe->error_code) return; next_token (pe); if (pe->error_code) { free_var (vl, pe); return; } primary_exp (pe); if (pe->error_code) { free_var (vl, pe); return; } vr = pop (pe); if (pe->error_code) { free_var (vl, pe); return; } rslt = get_unnamed_var (pe); if (pe->error_code) { free_var (vl, pe); free_var (vr, pe); return; } rslt->value = pe->numeric_ops (op, vl->value, vr->value); free_var (vl, pe); free_var (vr, pe); push (rslt, pe); } /* endwhile */ } /* multiply_divide_op */ /** * Bug#334811, 308554: apply some basic grammar constraints. * @return true if the expression is in error; pe->error_code will already * contain the error. **/ static int check_expression_grammar_error(parser_env_ptr pe) { if (pe->Token == VAR_TOKEN || pe->Token == STR_TOKEN || pe->Token == NUM_TOKEN || pe->Token == FN_TOKEN) { add_token(pe, EOS); pe->error_code = EXPRESSION_ERROR; return TRUE; } return FALSE; } /* evaluate: * unary '+' and '-' * named variables * numerics * grouped expressions, "()" * functions [ <name>( [exp : exp : ... : exp] ) ] * strings */ static void primary_exp (parser_env_ptr pe) { var_store_ptr rslt = NULL; char *ident = NULL; int funcArgCount; char LToken = pe->Token; /* If we are in a state where the non-stacked 'pe->name' is valuable, then * save it before we process the next token. */ switch ( LToken ) { case FN_TOKEN: case STR_TOKEN: ident = g_strdup( pe->name ); break; } next_token (pe); if (pe->error_code) return; switch (LToken) { case '(': assignment_op (pe); if (pe->error_code) return; if (pe->Token == ')') { rslt = pop (pe); if (pe->error_code) return; next_token (pe); if (pe->error_code) return; } else { add_token (pe, EOS); /* error here */ pe->error_code = UNBALANCED_PARENS; } /* endif */ break; case ADD_OP: case SUB_OP: primary_exp (pe); if (pe->error_code) return; rslt = pop (pe); if (pe->error_code) return; if (LToken == SUB_OP) pe->negate_numeric (rslt->value); break; case NUM_TOKEN: rslt = get_unnamed_var (pe); if (pe->error_code) return; if (check_expression_grammar_error(pe)) return; rslt->value = pe->numeric_value; pe->numeric_value = NULL; break; case FN_TOKEN: funcArgCount = 0; if (pe->Token && pe->Token != ')') { do { assignment_op(pe); if ( pe->error_code ) return; funcArgCount++; if (!pe->Token || pe->Token == ')') { break; } next_token(pe); } while (pe->Token != ARG_TOKEN); } if ( pe->Token != ')' ) { add_token( pe, EOS ); pe->error_code = UNBALANCED_PARENS; } { int i; var_store_ptr val; void **argv; argv = g_new0( void*, funcArgCount ); for ( i = 0; i < funcArgCount; i++ ) { /* fill, in back-to-front order, the funcArgCount tokens we just * parsed out of the expression into a argument list to hand back * to the caller's func_op callback. */ val = pop(pe); argv[funcArgCount - i - 1] = val; } rslt = get_unnamed_var(pe); rslt->value = (*pe->func_op)( ident, funcArgCount, argv ); for ( i = 0; i < funcArgCount; i++ ) { free_var( argv[i], pe ); } g_free( argv ); g_free( ident ); if ( rslt->value == NULL ) { pe->error_code = NOT_A_FUNC; add_token( pe, EOS ); return; } } next_token(pe); if (check_expression_grammar_error(pe)) return; break; case VAR_TOKEN: if (check_expression_grammar_error(pe)) return; rslt = get_named_var (pe); break; case STR_TOKEN: if (!(pe->Token == ')' || pe->Token == ARG_TOKEN)) { add_token(pe, EOS); pe->error_code = EXPRESSION_ERROR; return; } rslt = get_unnamed_var( pe ); rslt->type = VST_STRING; rslt->value = ident; break; } /* endswitch */ if (rslt != NULL) push (rslt, pe); } /* primary_exp */