diff options
Diffstat (limited to 'engines/glk/adrift/scexpr.cpp')
-rw-r--r-- | engines/glk/adrift/scexpr.cpp | 2557 |
1 files changed, 1226 insertions, 1331 deletions
diff --git a/engines/glk/adrift/scexpr.cpp b/engines/glk/adrift/scexpr.cpp index ed3386020f..a316b7cdec 100644 --- a/engines/glk/adrift/scexpr.cpp +++ b/engines/glk/adrift/scexpr.cpp @@ -51,50 +51,49 @@ static const sc_char DOUBLE_QUOTE = '"'; * serves to indicate string concatenation -- it's never returned by the * tokenizer. */ -enum -{ TOK_NONE = -1, - TOK_ADD = '+', TOK_SUBTRACT = '-', TOK_MULTIPLY = '*', TOK_DIVIDE = '/', - TOK_AND = '&', TOK_OR = '|', - TOK_LPAREN = '(', TOK_RPAREN = ')', TOK_COMMA = ',', TOK_POWER = '^', - TOK_EQUAL = '=', TOK_GREATER = '>', TOK_LESS = '<', - - TOK_IDENT = 256, - TOK_INTEGER, TOK_STRING, TOK_VARIABLE, TOK_UMINUS, TOK_UPLUS, - TOK_MOD, TOK_NOT_EQUAL, TOK_GREATER_EQ, TOK_LESS_EQ, TOK_IF, - TOK_MIN, TOK_MAX, TOK_EITHER, TOK_RANDOM, TOK_INSTR, TOK_LEN, TOK_VAL, - TOK_ABS, TOK_UPPER, TOK_LOWER, TOK_PROPER, TOK_RIGHT, TOK_LEFT, TOK_MID, - TOK_STR, TOK_CONCATENATE, - TOK_EOS +enum { + TOK_NONE = -1, + TOK_ADD = '+', TOK_SUBTRACT = '-', TOK_MULTIPLY = '*', TOK_DIVIDE = '/', + TOK_AND = '&', TOK_OR = '|', + TOK_LPAREN = '(', TOK_RPAREN = ')', TOK_COMMA = ',', TOK_POWER = '^', + TOK_EQUAL = '=', TOK_GREATER = '>', TOK_LESS = '<', + + TOK_IDENT = 256, + TOK_INTEGER, TOK_STRING, TOK_VARIABLE, TOK_UMINUS, TOK_UPLUS, + TOK_MOD, TOK_NOT_EQUAL, TOK_GREATER_EQ, TOK_LESS_EQ, TOK_IF, + TOK_MIN, TOK_MAX, TOK_EITHER, TOK_RANDOM, TOK_INSTR, TOK_LEN, TOK_VAL, + TOK_ABS, TOK_UPPER, TOK_LOWER, TOK_PROPER, TOK_RIGHT, TOK_LEFT, TOK_MID, + TOK_STR, TOK_CONCATENATE, + TOK_EOS }; /* * Small tables tying multicharacter tokens strings to tokens. At present, * the string lengths for names are not used. */ -typedef struct -{ - const sc_char *const name; - const sc_int length; - const sc_int token; +typedef struct { + const sc_char *const name; + const sc_int length; + const sc_int token; } sc_expr_multichar_t; static const sc_expr_multichar_t FUNCTION_TOKENS[] = { - {"either", 6, TOK_EITHER}, - {"proper", 6, TOK_PROPER}, {"pcase", 5, TOK_PROPER}, {"instr", 5, TOK_INSTR}, - {"upper", 5, TOK_UPPER}, {"ucase", 5, TOK_UPPER}, - {"lower", 5, TOK_LOWER}, {"lcase", 5, TOK_LOWER}, - {"right", 5, TOK_RIGHT}, {"left", 4, TOK_LEFT}, - {"rand", 4, TOK_RANDOM}, {"max", 3, TOK_MAX}, {"min", 3, TOK_MIN}, - {"mod", 3, TOK_MOD}, {"abs", 3, TOK_ABS}, {"len", 3, TOK_LEN}, - {"val", 3, TOK_VAL}, {"and", 3, TOK_AND}, {"mid", 3, TOK_MID}, - {"str", 3, TOK_STR}, {"or", 2, TOK_OR}, {"if", 2, TOK_IF}, - {NULL, 0, TOK_NONE} + {"either", 6, TOK_EITHER}, + {"proper", 6, TOK_PROPER}, {"pcase", 5, TOK_PROPER}, {"instr", 5, TOK_INSTR}, + {"upper", 5, TOK_UPPER}, {"ucase", 5, TOK_UPPER}, + {"lower", 5, TOK_LOWER}, {"lcase", 5, TOK_LOWER}, + {"right", 5, TOK_RIGHT}, {"left", 4, TOK_LEFT}, + {"rand", 4, TOK_RANDOM}, {"max", 3, TOK_MAX}, {"min", 3, TOK_MIN}, + {"mod", 3, TOK_MOD}, {"abs", 3, TOK_ABS}, {"len", 3, TOK_LEN}, + {"val", 3, TOK_VAL}, {"and", 3, TOK_AND}, {"mid", 3, TOK_MID}, + {"str", 3, TOK_STR}, {"or", 2, TOK_OR}, {"if", 2, TOK_IF}, + {NULL, 0, TOK_NONE} }; static const sc_expr_multichar_t OPERATOR_TOKENS[] = { - {"&&", 2, TOK_AND}, {"||", 2, TOK_OR}, - {"==", 2, TOK_EQUAL}, {"!=", 2, TOK_NOT_EQUAL}, - {"<>", 2, TOK_NOT_EQUAL}, {">=", 2, TOK_GREATER_EQ}, {"<=", 2, TOK_LESS_EQ}, - {NULL, 0, TOK_NONE} + {"&&", 2, TOK_AND}, {"||", 2, TOK_OR}, + {"==", 2, TOK_EQUAL}, {"!=", 2, TOK_NOT_EQUAL}, + {"<>", 2, TOK_NOT_EQUAL}, {">=", 2, TOK_GREATER_EQ}, {"<=", 2, TOK_LESS_EQ}, + {NULL, 0, TOK_NONE} }; @@ -105,19 +104,17 @@ static const sc_expr_multichar_t OPERATOR_TOKENS[] = { * TOK_NONE if no match. */ static sc_int -expr_multichar_search (const sc_char *name, const sc_expr_multichar_t *table) -{ - const sc_expr_multichar_t *entry; - - /* Scan the table for a case-independent full string match. */ - for (entry = table; entry->name; entry++) - { - if (sc_strcasecmp (name, entry->name) == 0) - break; - } - - /* Return the token matched, or TOK_NONE. */ - return entry->name ? entry->token : TOK_NONE; +expr_multichar_search(const sc_char *name, const sc_expr_multichar_t *table) { + const sc_expr_multichar_t *entry; + + /* Scan the table for a case-independent full string match. */ + for (entry = table; entry->name; entry++) { + if (sc_strcasecmp(name, entry->name) == 0) + break; + } + + /* Return the token matched, or TOK_NONE. */ + return entry->name ? entry->token : TOK_NONE; } @@ -135,60 +132,53 @@ static sc_int expr_current_token = TOK_NONE; * Start and wrap up expression string tokenization. */ static void -expr_tokenize_start (const sc_char *expression) -{ - static sc_bool initialized = FALSE; - - /* On first call only, verify the string lengths in the tables. */ - if (!initialized) - { - const sc_expr_multichar_t *entry; - - /* Compare table lengths with string lengths. */ - for (entry = FUNCTION_TOKENS; entry->name; entry++) - { - if (entry->length != (sc_int) strlen (entry->name)) - { - sc_fatal ("expr_tokenize_start:" - " token string length is wrong for \"%s\"\n", - entry->name); - } - } - - for (entry = OPERATOR_TOKENS; entry->name; entry++) - { - if (entry->length != (sc_int) strlen (entry->name)) - { - sc_fatal ("expr_tokenize_start:" - " operator string length is wrong for \"%s\"\n", - entry->name); - } - } - - initialized = TRUE; - } - - /* Save expression, and restart index. */ - expr_expression = expression; - expr_index = 0; - - /* Allocate a temporary token value/literals string. */ - assert (!expr_temporary); - expr_temporary = (sc_char *)sc_malloc (strlen (expression) + 1); - - /* Reset last token to none. */ - expr_current_token = TOK_NONE; +expr_tokenize_start(const sc_char *expression) { + static sc_bool initialized = FALSE; + + /* On first call only, verify the string lengths in the tables. */ + if (!initialized) { + const sc_expr_multichar_t *entry; + + /* Compare table lengths with string lengths. */ + for (entry = FUNCTION_TOKENS; entry->name; entry++) { + if (entry->length != (sc_int) strlen(entry->name)) { + sc_fatal("expr_tokenize_start:" + " token string length is wrong for \"%s\"\n", + entry->name); + } + } + + for (entry = OPERATOR_TOKENS; entry->name; entry++) { + if (entry->length != (sc_int) strlen(entry->name)) { + sc_fatal("expr_tokenize_start:" + " operator string length is wrong for \"%s\"\n", + entry->name); + } + } + + initialized = TRUE; + } + + /* Save expression, and restart index. */ + expr_expression = expression; + expr_index = 0; + + /* Allocate a temporary token value/literals string. */ + assert(!expr_temporary); + expr_temporary = (sc_char *)sc_malloc(strlen(expression) + 1); + + /* Reset last token to none. */ + expr_current_token = TOK_NONE; } static void -expr_tokenize_end (void) -{ - /* Deallocate temporary strings, clear expression. */ - sc_free (expr_temporary); - expr_temporary = NULL; - expr_expression = NULL; - expr_index = 0; - expr_current_token = TOK_NONE; +expr_tokenize_end(void) { + /* Deallocate temporary strings, clear expression. */ + sc_free(expr_temporary); + expr_temporary = NULL; + expr_expression = NULL; + expr_index = 0; + expr_current_token = TOK_NONE; } @@ -200,219 +190,198 @@ expr_tokenize_end (void) * be adjusted into a unary +/- depending on the value of the previous token. */ static sc_int -expr_next_token_unadjusted (sc_vartype_t *token_value) -{ - sc_int c; - assert (expr_expression); - - /* Skip any and all leading whitespace. */ - do - { - c = expr_expression[expr_index++]; - } - while (sc_isspace (c) && c != NUL); - - /* Return EOS if at expression end. */ - if (c == NUL) - { - expr_index--; - return TOK_EOS; - } - - /* - * Identify and return numerics. We deal only with unsigned numbers here; - * the unary +/- tokens take care of any integer sign issues. - */ - else if (sc_isdigit (c)) - { - sc_int value; - - sscanf (expr_expression + expr_index - 1, "%ld", &value); - - while (sc_isdigit (c) && c != NUL) - c = expr_expression[expr_index++]; - expr_index--; - - token_value->integer = value; - return TOK_INTEGER; - } - - /* Identify and return variable references. */ - else if (c == PERCENT) - { - sc_int index_; - - /* Copy variable name. */ - c = expr_expression[expr_index++]; - for (index_ = 0; c != PERCENT && c != NUL;) - { - expr_temporary[index_++] = c; - c = expr_expression[expr_index++]; - } - expr_temporary[index_++] = NUL; - - if (c == NUL) - { - sc_error ("expr_next_token_unadjusted:" - " warning: unterminated variable name\n"); - expr_index--; - } - - /* Return a variable name. */ - token_value->string = expr_temporary; - return TOK_VARIABLE; - } - - /* Identify and return string literals. */ - else if (c == DOUBLE_QUOTE || c == SINGLE_QUOTE) - { - sc_int index_; - sc_char quote; - - /* Copy maximal string literal. */ - quote = c; - c = expr_expression[expr_index++]; - for (index_ = 0; c != quote && c != NUL;) - { - expr_temporary[index_++] = c; - c = expr_expression[expr_index++]; - } - expr_temporary[index_++] = NUL; - - if (c == NUL) - { - sc_error ("expr_next_token_unadjusted:" - " warning: unterminated string literal\n"); - expr_index--; - } - - /* Return string literal. */ - token_value->string = expr_temporary; - return TOK_STRING; - } - - /* Identify ids and other multicharacter tokens. */ - else if (sc_isalpha (c)) - { - sc_int index_, token; - - /* - * Copy maximal alphabetical string. While an ident would normally - * be alpha followed by zero or more alnum, for Adrift purposes we - * use only alpha -- all idents should really be "functions", and - * in particular we want to see "mod7" as "mod" and 7 separately. - */ - for (index_ = 0; sc_isalpha (c) && c != NUL;) - { - expr_temporary[index_++] = c; - c = expr_expression[expr_index++]; - } - expr_index--; - expr_temporary[index_++] = NUL; - - /* - * Check for a function name, and if known, return that, otherwise - * return a bare id. - */ - token = expr_multichar_search (expr_temporary, FUNCTION_TOKENS); - if (token == TOK_NONE) - { - token_value->string = expr_temporary; - return TOK_IDENT; - } - else - return token; - } - - /* - * Last chance check for two-character (multichar) operators, and if none - * then return a single-character token. - */ - else - { - sc_char operator_[3]; - sc_int token; - - /* - * Build a two-character string. If we happen to be at the last - * expression character, we'll pick up the expression NUL into - * operator_[1], so no need to special case end of expression here. - */ - operator_[0] = c; - operator_[1] = expr_expression[expr_index]; - operator_[2] = NUL; - - /* Search for this two-character operator. */ - if (operator_[0] != NUL && operator_[1] != NUL) - { - token = expr_multichar_search (operator_, OPERATOR_TOKENS); - if (token != TOK_NONE) - { - /* Matched, so advance expression index and return this token. */ - expr_index++; - return token; - } - } - - /* - * No match, or at last expression character; return a single character - * token. - */ - return c; - } +expr_next_token_unadjusted(sc_vartype_t *token_value) { + sc_int c; + assert(expr_expression); + + /* Skip any and all leading whitespace. */ + do { + c = expr_expression[expr_index++]; + } while (sc_isspace(c) && c != NUL); + + /* Return EOS if at expression end. */ + if (c == NUL) { + expr_index--; + return TOK_EOS; + } + + /* + * Identify and return numerics. We deal only with unsigned numbers here; + * the unary +/- tokens take care of any integer sign issues. + */ + else if (sc_isdigit(c)) { + sc_int value; + + sscanf(expr_expression + expr_index - 1, "%ld", &value); + + while (sc_isdigit(c) && c != NUL) + c = expr_expression[expr_index++]; + expr_index--; + + token_value->integer = value; + return TOK_INTEGER; + } + + /* Identify and return variable references. */ + else if (c == PERCENT) { + sc_int index_; + + /* Copy variable name. */ + c = expr_expression[expr_index++]; + for (index_ = 0; c != PERCENT && c != NUL;) { + expr_temporary[index_++] = c; + c = expr_expression[expr_index++]; + } + expr_temporary[index_++] = NUL; + + if (c == NUL) { + sc_error("expr_next_token_unadjusted:" + " warning: unterminated variable name\n"); + expr_index--; + } + + /* Return a variable name. */ + token_value->string = expr_temporary; + return TOK_VARIABLE; + } + + /* Identify and return string literals. */ + else if (c == DOUBLE_QUOTE || c == SINGLE_QUOTE) { + sc_int index_; + sc_char quote; + + /* Copy maximal string literal. */ + quote = c; + c = expr_expression[expr_index++]; + for (index_ = 0; c != quote && c != NUL;) { + expr_temporary[index_++] = c; + c = expr_expression[expr_index++]; + } + expr_temporary[index_++] = NUL; + + if (c == NUL) { + sc_error("expr_next_token_unadjusted:" + " warning: unterminated string literal\n"); + expr_index--; + } + + /* Return string literal. */ + token_value->string = expr_temporary; + return TOK_STRING; + } + + /* Identify ids and other multicharacter tokens. */ + else if (sc_isalpha(c)) { + sc_int index_, token; + + /* + * Copy maximal alphabetical string. While an ident would normally + * be alpha followed by zero or more alnum, for Adrift purposes we + * use only alpha -- all idents should really be "functions", and + * in particular we want to see "mod7" as "mod" and 7 separately. + */ + for (index_ = 0; sc_isalpha(c) && c != NUL;) { + expr_temporary[index_++] = c; + c = expr_expression[expr_index++]; + } + expr_index--; + expr_temporary[index_++] = NUL; + + /* + * Check for a function name, and if known, return that, otherwise + * return a bare id. + */ + token = expr_multichar_search(expr_temporary, FUNCTION_TOKENS); + if (token == TOK_NONE) { + token_value->string = expr_temporary; + return TOK_IDENT; + } else + return token; + } + + /* + * Last chance check for two-character (multichar) operators, and if none + * then return a single-character token. + */ + else { + sc_char operator_[3]; + sc_int token; + + /* + * Build a two-character string. If we happen to be at the last + * expression character, we'll pick up the expression NUL into + * operator_[1], so no need to special case end of expression here. + */ + operator_[0] = c; + operator_[1] = expr_expression[expr_index]; + operator_[2] = NUL; + + /* Search for this two-character operator. */ + if (operator_[0] != NUL && operator_[1] != NUL) { + token = expr_multichar_search(operator_, OPERATOR_TOKENS); + if (token != TOK_NONE) { + /* Matched, so advance expression index and return this token. */ + expr_index++; + return token; + } + } + + /* + * No match, or at last expression character; return a single character + * token. + */ + return c; + } } static sc_int -expr_next_token (void) -{ - sc_int token; - sc_vartype_t token_value; - - /* - * Get the basic next token. We may adjust it later for unary minus/plus - * depending on what it is, and the prior token. - */ - token_value.voidp = NULL; - token = expr_next_token_unadjusted (&token_value); - - /* Special handling for unary minus/plus signs. */ - if (token == TOK_SUBTRACT || token == TOK_ADD) - { - /* - * Unary minus/plus if prior token was an operator or a comparison, left - * parenthesis, or comma, or if there was no prior token. - */ - switch (expr_current_token) - { - case TOK_MOD: - case TOK_POWER: - case TOK_ADD: - case TOK_SUBTRACT: - case TOK_MULTIPLY: - case TOK_DIVIDE: - case TOK_AND: - case TOK_OR: - case TOK_EQUAL: - case TOK_GREATER: - case TOK_LESS: - case TOK_NOT_EQUAL: - case TOK_GREATER_EQ: - case TOK_LESS_EQ: - case TOK_LPAREN: - case TOK_COMMA: - case TOK_NONE: - token = (token == TOK_SUBTRACT) ? TOK_UMINUS : TOK_UPLUS; - break; - - default: - break; - } - } - - /* Set current token to the one just found, and return it. */ - expr_current_token = token; - expr_token_value = token_value; - return token; +expr_next_token(void) { + sc_int token; + sc_vartype_t token_value; + + /* + * Get the basic next token. We may adjust it later for unary minus/plus + * depending on what it is, and the prior token. + */ + token_value.voidp = NULL; + token = expr_next_token_unadjusted(&token_value); + + /* Special handling for unary minus/plus signs. */ + if (token == TOK_SUBTRACT || token == TOK_ADD) { + /* + * Unary minus/plus if prior token was an operator or a comparison, left + * parenthesis, or comma, or if there was no prior token. + */ + switch (expr_current_token) { + case TOK_MOD: + case TOK_POWER: + case TOK_ADD: + case TOK_SUBTRACT: + case TOK_MULTIPLY: + case TOK_DIVIDE: + case TOK_AND: + case TOK_OR: + case TOK_EQUAL: + case TOK_GREATER: + case TOK_LESS: + case TOK_NOT_EQUAL: + case TOK_GREATER_EQ: + case TOK_LESS_EQ: + case TOK_LPAREN: + case TOK_COMMA: + case TOK_NONE: + token = (token == TOK_SUBTRACT) ? TOK_UMINUS : TOK_UPLUS; + break; + + default: + break; + } + } + + /* Set current token to the one just found, and return it. */ + expr_current_token = token; + expr_token_value = token_value; + return token; } @@ -423,24 +392,22 @@ expr_next_token (void) * token is not numeric, an id, or a variable. */ static void -expr_current_token_value (sc_vartype_t *value) -{ - /* Quick check that the value is a valid one. */ - switch (expr_current_token) - { - case TOK_INTEGER: - case TOK_STRING: - case TOK_VARIABLE: - case TOK_IDENT: - break; - - default: - sc_fatal ("expr_current_token_value:" - " taking undefined token value, %ld\n", expr_current_token); - } - - /* Return value. */ - *value = expr_token_value; +expr_current_token_value(sc_vartype_t *value) { + /* Quick check that the value is a valid one. */ + switch (expr_current_token) { + case TOK_INTEGER: + case TOK_STRING: + case TOK_VARIABLE: + case TOK_IDENT: + break; + + default: + sc_fatal("expr_current_token_value:" + " taking undefined token value, %ld\n", expr_current_token); + } + + /* Return value. */ + *value = expr_token_value; } @@ -449,10 +416,9 @@ expr_current_token_value (sc_vartype_t *value) * integers and strings, and flags strings for possible garbage collection * on parse errors. */ -typedef struct -{ - sc_bool is_collectible; - sc_vartype_t value; +typedef struct { + sc_bool is_collectible; + sc_vartype_t value; } sc_stack_t; static sc_stack_t expr_eval_stack[MAX_NESTING_DEPTH]; static sc_int expr_eval_stack_index = 0; @@ -467,10 +433,9 @@ static sc_var_setref_t expr_varset = NULL; * set to use when referencing %...% variables. */ static void -expr_eval_start (sc_var_setref_t vars) -{ - expr_eval_stack_index = 0; - expr_varset = vars; +expr_eval_start(sc_var_setref_t vars) { + expr_eval_stack_index = 0; + expr_varset = vars; } @@ -481,22 +446,20 @@ expr_eval_start (sc_var_setref_t vars) * strings left in the evaluation array. */ static void -expr_eval_garbage_collect (void) -{ - sc_int index_; - - /* - * Find and free all collectible strings still in the stack. We have to - * free through mutable string rather than const string. - */ - for (index_ = 0; index_ < expr_eval_stack_index; index_++) - { - if (expr_eval_stack[index_].is_collectible) - sc_free (expr_eval_stack[index_].value.mutable_string); - } - - /* Reset the stack index, for clarity and neatness. */ - expr_eval_stack_index = 0; +expr_eval_garbage_collect(void) { + sc_int index_; + + /* + * Find and free all collectible strings still in the stack. We have to + * free through mutable string rather than const string. + */ + for (index_ = 0; index_ < expr_eval_stack_index; index_++) { + if (expr_eval_stack[index_].is_collectible) + sc_free(expr_eval_stack[index_].value.mutable_string); + } + + /* Reset the stack index, for clarity and neatness. */ + expr_eval_stack_index = 0; } @@ -511,38 +474,35 @@ expr_eval_garbage_collect (void) * the caller should not subsequently free the string. */ static void -expr_eval_push_integer (sc_int value) -{ - if (expr_eval_stack_index >= MAX_NESTING_DEPTH) - sc_fatal ("expr_eval_push_integer: stack overflow\n"); +expr_eval_push_integer(sc_int value) { + if (expr_eval_stack_index >= MAX_NESTING_DEPTH) + sc_fatal("expr_eval_push_integer: stack overflow\n"); - expr_eval_stack[expr_eval_stack_index].is_collectible = FALSE; - expr_eval_stack[expr_eval_stack_index++].value.integer = value; + expr_eval_stack[expr_eval_stack_index].is_collectible = FALSE; + expr_eval_stack[expr_eval_stack_index++].value.integer = value; } static void -expr_eval_push_string (const sc_char *value) -{ - sc_char *value_copy; - - if (expr_eval_stack_index >= MAX_NESTING_DEPTH) - sc_fatal ("expr_eval_push_string: stack overflow\n"); - - /* Push a copy of value. */ - value_copy = (sc_char *)sc_malloc (strlen (value) + 1); - strcpy (value_copy, value); - expr_eval_stack[expr_eval_stack_index].is_collectible = TRUE; - expr_eval_stack[expr_eval_stack_index++].value.mutable_string = value_copy; +expr_eval_push_string(const sc_char *value) { + sc_char *value_copy; + + if (expr_eval_stack_index >= MAX_NESTING_DEPTH) + sc_fatal("expr_eval_push_string: stack overflow\n"); + + /* Push a copy of value. */ + value_copy = (sc_char *)sc_malloc(strlen(value) + 1); + strcpy(value_copy, value); + expr_eval_stack[expr_eval_stack_index].is_collectible = TRUE; + expr_eval_stack[expr_eval_stack_index++].value.mutable_string = value_copy; } static void -expr_eval_push_alloced_string (sc_char *value) -{ - if (expr_eval_stack_index >= MAX_NESTING_DEPTH) - sc_fatal ("expr_eval_push_alloced_string: stack overflow\n"); +expr_eval_push_alloced_string(sc_char *value) { + if (expr_eval_stack_index >= MAX_NESTING_DEPTH) + sc_fatal("expr_eval_push_alloced_string: stack overflow\n"); - expr_eval_stack[expr_eval_stack_index].is_collectible = TRUE; - expr_eval_stack[expr_eval_stack_index++].value.mutable_string = value; + expr_eval_stack[expr_eval_stack_index].is_collectible = TRUE; + expr_eval_stack[expr_eval_stack_index++].value.mutable_string = value; } @@ -554,24 +514,22 @@ expr_eval_push_alloced_string (sc_char *value) * and the caller is responsible for freeing them. */ static sc_int -expr_eval_pop_integer (void) -{ - if (expr_eval_stack_index == 0) - sc_fatal ("expr_eval_pop_integer: stack underflow\n"); +expr_eval_pop_integer(void) { + if (expr_eval_stack_index == 0) + sc_fatal("expr_eval_pop_integer: stack underflow\n"); - assert (!expr_eval_stack[expr_eval_stack_index - 1].is_collectible); - return expr_eval_stack[--expr_eval_stack_index].value.integer; + assert(!expr_eval_stack[expr_eval_stack_index - 1].is_collectible); + return expr_eval_stack[--expr_eval_stack_index].value.integer; } static sc_char * -expr_eval_pop_string (void) -{ - if (expr_eval_stack_index == 0) - sc_fatal ("expr_eval_pop_string: stack underflow\n"); - - /* Returns mutable string rather than const string. */ - assert (expr_eval_stack[expr_eval_stack_index - 1].is_collectible); - return expr_eval_stack[--expr_eval_stack_index].value.mutable_string; +expr_eval_pop_string(void) { + if (expr_eval_stack_index == 0) + sc_fatal("expr_eval_pop_string: stack underflow\n"); + + /* Returns mutable string rather than const string. */ + assert(expr_eval_stack[expr_eval_stack_index - 1].is_collectible); + return expr_eval_stack[--expr_eval_stack_index].value.mutable_string; } @@ -581,14 +539,13 @@ expr_eval_pop_string (void) * Return the top of the values stack as the expression result. */ static void -expr_eval_result (sc_vartype_t *vt_rvalue) -{ - if (expr_eval_stack_index != 1) - sc_fatal ("expr_eval_result: values stack not completed\n"); - - /* Clear down stack and return the top value. */ - expr_eval_stack_index = 0; - *vt_rvalue = expr_eval_stack[0].value; +expr_eval_result(sc_vartype_t *vt_rvalue) { + if (expr_eval_stack_index != 1) + sc_fatal("expr_eval_result: values stack not completed\n"); + + /* Clear down stack and return the top value. */ + expr_eval_stack_index = 0; + *vt_rvalue = expr_eval_stack[0].value; } @@ -599,9 +556,8 @@ expr_eval_result (sc_vartype_t *vt_rvalue) * avoids tying sc_int to long types too closely. */ static sc_int -expr_eval_abs (sc_int value) -{ - return value < 0 ? -value : value; +expr_eval_abs(sc_int value) { + return value < 0 ? -value : value; } @@ -614,525 +570,489 @@ static jmp_buf expr_parse_error; * Evaluate the effect of a token into the values stack. */ static void -expr_eval_action (sc_int token) -{ - sc_vartype_t token_value; - - switch (token) - { - /* Handle tokens representing stack pushes. */ - case TOK_INTEGER: - expr_current_token_value (&token_value); - expr_eval_push_integer (token_value.integer); - break; - - case TOK_STRING: - expr_current_token_value (&token_value); - expr_eval_push_string (token_value.string); - break; - - case TOK_VARIABLE: - { - sc_vartype_t vt_rvalue; - sc_int type; - - expr_current_token_value (&token_value); - if (!var_get (expr_varset, token_value.string, &type, &vt_rvalue)) - { - sc_error ("expr_eval_action:" - " undefined variable, %s\n", token_value.string); - longjmp (expr_parse_error, 1); - } - switch (type) - { - case VAR_INTEGER: - expr_eval_push_integer (vt_rvalue.integer); - break; - - case VAR_STRING: - expr_eval_push_string (vt_rvalue.string); - break; - - default: - sc_fatal ("expr_eval_action: bad variable type\n"); - } - break; - } - - /* Handle tokens representing functions returning numeric. */ - case TOK_IF: - { - sc_int test, val1, val2; - - /* Pop the test and alternatives, and push back result. */ - val2 = expr_eval_pop_integer (); - val1 = expr_eval_pop_integer (); - test = expr_eval_pop_integer (); - expr_eval_push_integer (test ? val1 : val2); - break; - } - - case TOK_MAX: - case TOK_MIN: - { - sc_int argument_count, index_, result; - - /* Get argument count off the top of the stack. */ - argument_count = expr_eval_pop_integer (); - assert (argument_count > 0); - - /* Find the max or min of these stacked values. */ - result = expr_eval_pop_integer (); - for (index_ = 1; index_ < argument_count; index_++) - { - sc_int next; - - next = expr_eval_pop_integer (); - switch (token) - { - case TOK_MAX: - result = (next > result) ? next : result; - break; - - case TOK_MIN: - result = (next < result) ? next : result; - break; - - default: - sc_fatal ("expr_eval_action: bad token, %ld\n", token); - } - } - - /* Push back the result. */ - expr_eval_push_integer (result); - break; - } - - case TOK_EITHER: - { - sc_int argument_count, pick, index_; - sc_int result = 0; - - /* Get argument count off the top of the stack. */ - argument_count = expr_eval_pop_integer (); - assert (argument_count > 0); - - /* - * Pick one of the top N items at random, then unstack all N and - * push back the value of the one picked. - */ - pick = sc_rand () % argument_count; - for (index_ = 0; index_ < argument_count; index_++) - { - sc_int val; - - val = expr_eval_pop_integer (); - if (index_ == pick) - result = val; - } - - /* Push back the result. */ - expr_eval_push_integer (result); - break; - } - - case TOK_INSTR: - { - sc_char *val1, *val2, *search; - sc_int result; - - /* Extract the two values to work on. */ - val2 = expr_eval_pop_string (); - val1 = expr_eval_pop_string (); - - /* - * Search for the second in the first. The result is the character - * position, starting at 1, or 0 if not found. Then free the popped - * strings, and push back the result. - */ - search = (val1[0] != NUL) ? strstr (val1, val2) : NULL; - result = (!search) ? 0 : search - val1 + 1; - sc_free (val1); - sc_free (val2); - expr_eval_push_integer (result); - break; - } - - case TOK_LEN: - { - sc_char *val; - sc_int result; - - /* Pop the top string, and push back its length. */ - val = expr_eval_pop_string (); - result = strlen (val); - sc_free (val); - expr_eval_push_integer (result); - break; - } - - case TOK_VAL: - { - sc_char *val; - sc_int result = 0; - - /* - * Extract the string at stack top, and try to convert, returning - * zero if conversion fails. Free the popped string, and push back - * the result. - */ - val = expr_eval_pop_string (); - sscanf (val, "%ld", &result); - sc_free (val); - expr_eval_push_integer (result); - break; - } - - /* Handle tokens representing unary numeric operations. */ - case TOK_UMINUS: - expr_eval_push_integer (-(expr_eval_pop_integer ())); - break; - - case TOK_UPLUS: - break; - - case TOK_ABS: - expr_eval_push_integer (expr_eval_abs (expr_eval_pop_integer ())); - break; - - /* Handle tokens representing most binary numeric operations. */ - case TOK_ADD: - case TOK_SUBTRACT: - case TOK_MULTIPLY: - case TOK_AND: - case TOK_OR: - case TOK_EQUAL: - case TOK_GREATER: - case TOK_LESS: - case TOK_NOT_EQUAL: - case TOK_GREATER_EQ: - case TOK_LESS_EQ: - case TOK_RANDOM: - { - sc_int val1, val2, result = 0; - - /* Extract the two values to work on. */ - val2 = expr_eval_pop_integer (); - val1 = expr_eval_pop_integer (); - - /* Generate the result value. */ - switch (token) - { - case TOK_ADD: - result = val1 + val2; - break; - case TOK_SUBTRACT: - result = val1 - val2; - break; - case TOK_MULTIPLY: - result = val1 * val2; - break; - case TOK_AND: - result = val1 && val2; - break; - case TOK_OR: - result = val1 || val2; - break; - case TOK_EQUAL: - result = val1 == val2; - break; - case TOK_GREATER: - result = val1 > val2; - break; - case TOK_LESS: - result = val1 < val2; - break; - case TOK_NOT_EQUAL: - result = val1 != val2; - break; - case TOK_GREATER_EQ: - result = val1 >= val2; - break; - case TOK_LESS_EQ: - result = val1 <= val2; - break; - case TOK_RANDOM: - result = sc_randomint (val1, val2); - break; - default: - sc_fatal ("expr_eval_action: bad token, %ld\n", token); - } - - /* Put result back at top of stack. */ - expr_eval_push_integer (result); - break; - } - - /* Handle division and modulus separately; they're "eccentric". */ - case TOK_DIVIDE: - case TOK_MOD: - { - sc_int val1, val2, x, y, result = 0; - - /* Extract the two values to work on, complain about division by 0. */ - val2 = expr_eval_pop_integer (); - val1 = expr_eval_pop_integer (); - if (val2 == 0) - { - sc_error ("expr_eval_action: attempt to divide by zero\n"); - expr_eval_push_integer (result); - break; - } - - /* - * ANSI/ISO C only defines integer division for positive values. - * Negative values usually work consistently across platforms, but are - * not guaranteed. For maximum portability, then, here we'll work - * carefully with positive integers only. - */ - x = expr_eval_abs (val1); - y = expr_eval_abs (val2); - - /* Generate the result value. */ - switch (token) - { - case TOK_DIVIDE: - /* - * Adrift's division apparently works by dividing using floating - * point, then applying (asymmetrical) rounding, so we have to do - * the same here. - */ - result = ((val1 < 0) == (val2 < 0)) - ? ((x / y) + (((x % y) * 2 >= y) ? 1 : 0)) - : -((x / y) + (((x % y) * 2 > y) ? 1 : 0)); - break; - - case TOK_MOD: - /* - * Adrift also breaks numerical consistency by defining mod in a - * conventional (non-rounded), way, so that A=(AdivB)*B+AmodB - * does not hold. - */ - result = (val1 < 0) ? -(x % y) : (x % y); - break; - - default: - sc_fatal ("expr_eval_action: bad token, %ld\n", token); - } - - /* Put result back at top of stack. */ - expr_eval_push_integer (result); - break; - } - - /* Handle power individually, to avoid needing a maths library. */ - case TOK_POWER: - { - sc_int val1, val2, result; - - /* Extract the two values to work on. */ - val2 = expr_eval_pop_integer (); - val1 = expr_eval_pop_integer (); - - /* Handle negative and zero power values first, as special cases. */ - if (val2 == 0) - result = 1; - else if (val2 < 0) - { - if (val1 == 0) - { - sc_error ("expr_eval_action: attempt to divide by zero\n"); - result = 0; - } - else if (val1 == 1) - result = val1; - else if (val1 == -1) - result = (-val2 & 1) ? val1 : -val1; - else - result = 0; - } - else - { - /* Raise to positive powers using the Russian Peasant algorithm. */ - while ((val2 & 1) == 0) - { - val1 = val1 * val1; - val2 >>= 1; - } - - result = val1; - val2 >>= 1; - while (val2 > 0) - { - val1 = val1 * val1; - if (val2 & 1) - result = result * val1; - val2 >>= 1; - } - } - - /* Put result back at top of stack. */ - expr_eval_push_integer (result); - break; - } - - /* Handle tokens representing functions returning string. */ - case TOK_LEFT: - case TOK_RIGHT: - { - sc_char *text; - sc_int length; - - /* - * Extract the text and length. If length is longer than text, or - * negative, do nothing. - */ - length = expr_eval_pop_integer (); - text = expr_eval_pop_string (); - if (length < 0 || length >= (sc_int) strlen (text)) - { - expr_eval_push_alloced_string (text); - break; - } - - /* - * Take the left or right segment -- for left, the operation is a - * simple truncation; for right, it's a memmove. - */ - switch (token) - { - case TOK_LEFT: - text[length] = NUL; - break; - - case TOK_RIGHT: - memmove (text, text + strlen (text) - length, length + 1); - break; - - default: - sc_fatal ("expr_eval_action: bad token, %ld\n", token); - } - - /* Put result back at top of stack. */ - expr_eval_push_alloced_string (text); - break; - } - - case TOK_MID: - { - sc_char *text; - sc_int length, start, limit; - - /* - * Extract the text, start, and length, re-basing start from 1 to 0, - * and calculate the limit on characters available for the move. - */ - length = expr_eval_pop_integer (); - start = expr_eval_pop_integer () - 1; - text = expr_eval_pop_string (); - limit = strlen (text); - - /* - * Clamp ranges that roam outside the available text -- start less - * than 0 to 0, and greater than len(text) to len(text), and length - * less than 0 to 0, and off string end to string end. - */ - if (start < 0) - start = 0; - else if (start > limit) - start = limit; - if (length < 0) - length = 0; - else if (length > limit - start) - length = limit - start; - - /* Move substring, terminate, and put back at top of stack. */ - memmove (text, text + start, length + 1); - text[length] = NUL; - expr_eval_push_alloced_string (text); - break; - } - - case TOK_STR: - { - sc_int val; - sc_char buffer[32]; - - /* - * Extract the value, convert it, and push back the resulting string. - * The leading space on positive values matches the Runner. - */ - val = expr_eval_pop_integer (); - sprintf (buffer, "% ld", val); - expr_eval_push_string (buffer); - break; - } - - - /* Handle tokens representing unary string operations. */ - case TOK_UPPER: - case TOK_LOWER: - case TOK_PROPER: - { - sc_char *text; - sc_int index_; - - /* Extract the value to work on. */ - text = expr_eval_pop_string (); - - /* Convert the entire string in place -- it's malloc'ed. */ - for (index_ = 0; text[index_] != NUL; index_++) - { - switch (token) - { - case TOK_UPPER: - text[index_] = sc_toupper (text[index_]); - break; - - case TOK_LOWER: - text[index_] = sc_tolower (text[index_]); - break; - - case TOK_PROPER: - if (index_ == 0 || sc_isspace (text[index_ - 1])) - text[index_] = sc_toupper (text[index_]); - else - text[index_] = sc_tolower (text[index_]); - break; - - default: - sc_fatal ("expr_eval_action: bad token, %ld\n", token); - } - } - - /* Put result back at top of stack. */ - expr_eval_push_alloced_string (text); - break; - } - - /* Handle token representing binary string operation. */ - case TOK_CONCATENATE: - { - sc_char *text1, *text2; - - /* Extract the two texts to work on. */ - text2 = expr_eval_pop_string (); - text1 = expr_eval_pop_string (); - - /* - * Resize text1 to be long enough for both, and concatenate, then - * free text2, and push back the concatenation. - */ - text1 = (sc_char *)sc_realloc (text1, strlen (text1) + strlen (text2) + 1); - strcat (text1, text2); - sc_free (text2); - expr_eval_push_alloced_string (text1); - break; - } - - default: - sc_fatal ("expr_eval_action: bad token, %ld\n", token); - } +expr_eval_action(sc_int token) { + sc_vartype_t token_value; + + switch (token) { + /* Handle tokens representing stack pushes. */ + case TOK_INTEGER: + expr_current_token_value(&token_value); + expr_eval_push_integer(token_value.integer); + break; + + case TOK_STRING: + expr_current_token_value(&token_value); + expr_eval_push_string(token_value.string); + break; + + case TOK_VARIABLE: { + sc_vartype_t vt_rvalue; + sc_int type; + + expr_current_token_value(&token_value); + if (!var_get(expr_varset, token_value.string, &type, &vt_rvalue)) { + sc_error("expr_eval_action:" + " undefined variable, %s\n", token_value.string); + longjmp(expr_parse_error, 1); + } + switch (type) { + case VAR_INTEGER: + expr_eval_push_integer(vt_rvalue.integer); + break; + + case VAR_STRING: + expr_eval_push_string(vt_rvalue.string); + break; + + default: + sc_fatal("expr_eval_action: bad variable type\n"); + } + break; + } + + /* Handle tokens representing functions returning numeric. */ + case TOK_IF: { + sc_int test, val1, val2; + + /* Pop the test and alternatives, and push back result. */ + val2 = expr_eval_pop_integer(); + val1 = expr_eval_pop_integer(); + test = expr_eval_pop_integer(); + expr_eval_push_integer(test ? val1 : val2); + break; + } + + case TOK_MAX: + case TOK_MIN: { + sc_int argument_count, index_, result; + + /* Get argument count off the top of the stack. */ + argument_count = expr_eval_pop_integer(); + assert(argument_count > 0); + + /* Find the max or min of these stacked values. */ + result = expr_eval_pop_integer(); + for (index_ = 1; index_ < argument_count; index_++) { + sc_int next; + + next = expr_eval_pop_integer(); + switch (token) { + case TOK_MAX: + result = (next > result) ? next : result; + break; + + case TOK_MIN: + result = (next < result) ? next : result; + break; + + default: + sc_fatal("expr_eval_action: bad token, %ld\n", token); + } + } + + /* Push back the result. */ + expr_eval_push_integer(result); + break; + } + + case TOK_EITHER: { + sc_int argument_count, pick, index_; + sc_int result = 0; + + /* Get argument count off the top of the stack. */ + argument_count = expr_eval_pop_integer(); + assert(argument_count > 0); + + /* + * Pick one of the top N items at random, then unstack all N and + * push back the value of the one picked. + */ + pick = sc_rand() % argument_count; + for (index_ = 0; index_ < argument_count; index_++) { + sc_int val; + + val = expr_eval_pop_integer(); + if (index_ == pick) + result = val; + } + + /* Push back the result. */ + expr_eval_push_integer(result); + break; + } + + case TOK_INSTR: { + sc_char *val1, *val2, *search; + sc_int result; + + /* Extract the two values to work on. */ + val2 = expr_eval_pop_string(); + val1 = expr_eval_pop_string(); + + /* + * Search for the second in the first. The result is the character + * position, starting at 1, or 0 if not found. Then free the popped + * strings, and push back the result. + */ + search = (val1[0] != NUL) ? strstr(val1, val2) : NULL; + result = (!search) ? 0 : search - val1 + 1; + sc_free(val1); + sc_free(val2); + expr_eval_push_integer(result); + break; + } + + case TOK_LEN: { + sc_char *val; + sc_int result; + + /* Pop the top string, and push back its length. */ + val = expr_eval_pop_string(); + result = strlen(val); + sc_free(val); + expr_eval_push_integer(result); + break; + } + + case TOK_VAL: { + sc_char *val; + sc_int result = 0; + + /* + * Extract the string at stack top, and try to convert, returning + * zero if conversion fails. Free the popped string, and push back + * the result. + */ + val = expr_eval_pop_string(); + sscanf(val, "%ld", &result); + sc_free(val); + expr_eval_push_integer(result); + break; + } + + /* Handle tokens representing unary numeric operations. */ + case TOK_UMINUS: + expr_eval_push_integer(-(expr_eval_pop_integer())); + break; + + case TOK_UPLUS: + break; + + case TOK_ABS: + expr_eval_push_integer(expr_eval_abs(expr_eval_pop_integer())); + break; + + /* Handle tokens representing most binary numeric operations. */ + case TOK_ADD: + case TOK_SUBTRACT: + case TOK_MULTIPLY: + case TOK_AND: + case TOK_OR: + case TOK_EQUAL: + case TOK_GREATER: + case TOK_LESS: + case TOK_NOT_EQUAL: + case TOK_GREATER_EQ: + case TOK_LESS_EQ: + case TOK_RANDOM: { + sc_int val1, val2, result = 0; + + /* Extract the two values to work on. */ + val2 = expr_eval_pop_integer(); + val1 = expr_eval_pop_integer(); + + /* Generate the result value. */ + switch (token) { + case TOK_ADD: + result = val1 + val2; + break; + case TOK_SUBTRACT: + result = val1 - val2; + break; + case TOK_MULTIPLY: + result = val1 * val2; + break; + case TOK_AND: + result = val1 && val2; + break; + case TOK_OR: + result = val1 || val2; + break; + case TOK_EQUAL: + result = val1 == val2; + break; + case TOK_GREATER: + result = val1 > val2; + break; + case TOK_LESS: + result = val1 < val2; + break; + case TOK_NOT_EQUAL: + result = val1 != val2; + break; + case TOK_GREATER_EQ: + result = val1 >= val2; + break; + case TOK_LESS_EQ: + result = val1 <= val2; + break; + case TOK_RANDOM: + result = sc_randomint(val1, val2); + break; + default: + sc_fatal("expr_eval_action: bad token, %ld\n", token); + } + + /* Put result back at top of stack. */ + expr_eval_push_integer(result); + break; + } + + /* Handle division and modulus separately; they're "eccentric". */ + case TOK_DIVIDE: + case TOK_MOD: { + sc_int val1, val2, x, y, result = 0; + + /* Extract the two values to work on, complain about division by 0. */ + val2 = expr_eval_pop_integer(); + val1 = expr_eval_pop_integer(); + if (val2 == 0) { + sc_error("expr_eval_action: attempt to divide by zero\n"); + expr_eval_push_integer(result); + break; + } + + /* + * ANSI/ISO C only defines integer division for positive values. + * Negative values usually work consistently across platforms, but are + * not guaranteed. For maximum portability, then, here we'll work + * carefully with positive integers only. + */ + x = expr_eval_abs(val1); + y = expr_eval_abs(val2); + + /* Generate the result value. */ + switch (token) { + case TOK_DIVIDE: + /* + * Adrift's division apparently works by dividing using floating + * point, then applying (asymmetrical) rounding, so we have to do + * the same here. + */ + result = ((val1 < 0) == (val2 < 0)) + ? ((x / y) + (((x % y) * 2 >= y) ? 1 : 0)) + : -((x / y) + (((x % y) * 2 > y) ? 1 : 0)); + break; + + case TOK_MOD: + /* + * Adrift also breaks numerical consistency by defining mod in a + * conventional (non-rounded), way, so that A=(AdivB)*B+AmodB + * does not hold. + */ + result = (val1 < 0) ? -(x % y) : (x % y); + break; + + default: + sc_fatal("expr_eval_action: bad token, %ld\n", token); + } + + /* Put result back at top of stack. */ + expr_eval_push_integer(result); + break; + } + + /* Handle power individually, to avoid needing a maths library. */ + case TOK_POWER: { + sc_int val1, val2, result; + + /* Extract the two values to work on. */ + val2 = expr_eval_pop_integer(); + val1 = expr_eval_pop_integer(); + + /* Handle negative and zero power values first, as special cases. */ + if (val2 == 0) + result = 1; + else if (val2 < 0) { + if (val1 == 0) { + sc_error("expr_eval_action: attempt to divide by zero\n"); + result = 0; + } else if (val1 == 1) + result = val1; + else if (val1 == -1) + result = (-val2 & 1) ? val1 : -val1; + else + result = 0; + } else { + /* Raise to positive powers using the Russian Peasant algorithm. */ + while ((val2 & 1) == 0) { + val1 = val1 * val1; + val2 >>= 1; + } + + result = val1; + val2 >>= 1; + while (val2 > 0) { + val1 = val1 * val1; + if (val2 & 1) + result = result * val1; + val2 >>= 1; + } + } + + /* Put result back at top of stack. */ + expr_eval_push_integer(result); + break; + } + + /* Handle tokens representing functions returning string. */ + case TOK_LEFT: + case TOK_RIGHT: { + sc_char *text; + sc_int length; + + /* + * Extract the text and length. If length is longer than text, or + * negative, do nothing. + */ + length = expr_eval_pop_integer(); + text = expr_eval_pop_string(); + if (length < 0 || length >= (sc_int) strlen(text)) { + expr_eval_push_alloced_string(text); + break; + } + + /* + * Take the left or right segment -- for left, the operation is a + * simple truncation; for right, it's a memmove. + */ + switch (token) { + case TOK_LEFT: + text[length] = NUL; + break; + + case TOK_RIGHT: + memmove(text, text + strlen(text) - length, length + 1); + break; + + default: + sc_fatal("expr_eval_action: bad token, %ld\n", token); + } + + /* Put result back at top of stack. */ + expr_eval_push_alloced_string(text); + break; + } + + case TOK_MID: { + sc_char *text; + sc_int length, start, limit; + + /* + * Extract the text, start, and length, re-basing start from 1 to 0, + * and calculate the limit on characters available for the move. + */ + length = expr_eval_pop_integer(); + start = expr_eval_pop_integer() - 1; + text = expr_eval_pop_string(); + limit = strlen(text); + + /* + * Clamp ranges that roam outside the available text -- start less + * than 0 to 0, and greater than len(text) to len(text), and length + * less than 0 to 0, and off string end to string end. + */ + if (start < 0) + start = 0; + else if (start > limit) + start = limit; + if (length < 0) + length = 0; + else if (length > limit - start) + length = limit - start; + + /* Move substring, terminate, and put back at top of stack. */ + memmove(text, text + start, length + 1); + text[length] = NUL; + expr_eval_push_alloced_string(text); + break; + } + + case TOK_STR: { + sc_int val; + sc_char buffer[32]; + + /* + * Extract the value, convert it, and push back the resulting string. + * The leading space on positive values matches the Runner. + */ + val = expr_eval_pop_integer(); + sprintf(buffer, "% ld", val); + expr_eval_push_string(buffer); + break; + } + + + /* Handle tokens representing unary string operations. */ + case TOK_UPPER: + case TOK_LOWER: + case TOK_PROPER: { + sc_char *text; + sc_int index_; + + /* Extract the value to work on. */ + text = expr_eval_pop_string(); + + /* Convert the entire string in place -- it's malloc'ed. */ + for (index_ = 0; text[index_] != NUL; index_++) { + switch (token) { + case TOK_UPPER: + text[index_] = sc_toupper(text[index_]); + break; + + case TOK_LOWER: + text[index_] = sc_tolower(text[index_]); + break; + + case TOK_PROPER: + if (index_ == 0 || sc_isspace(text[index_ - 1])) + text[index_] = sc_toupper(text[index_]); + else + text[index_] = sc_tolower(text[index_]); + break; + + default: + sc_fatal("expr_eval_action: bad token, %ld\n", token); + } + } + + /* Put result back at top of stack. */ + expr_eval_push_alloced_string(text); + break; + } + + /* Handle token representing binary string operation. */ + case TOK_CONCATENATE: { + sc_char *text1, *text2; + + /* Extract the two texts to work on. */ + text2 = expr_eval_pop_string(); + text1 = expr_eval_pop_string(); + + /* + * Resize text1 to be long enough for both, and concatenate, then + * free text2, and push back the concatenation. + */ + text1 = (sc_char *)sc_realloc(text1, strlen(text1) + strlen(text2) + 1); + strcat(text1, text2); + sc_free(text2); + expr_eval_push_alloced_string(text1); + break; + } + + default: + sc_fatal("expr_eval_action: bad token, %ld\n", token); + } } @@ -1140,9 +1060,9 @@ expr_eval_action (sc_int token) static sc_int expr_parse_lookahead = TOK_NONE; /* Forward declaration of factor parsers and string expression parser. */ -static void expr_parse_numeric_factor (void); -static void expr_parse_string_factor (void); -static void expr_parse_string_expr (void); +static void expr_parse_numeric_factor(void); +static void expr_parse_string_factor(void); +static void expr_parse_string_expr(void); /* * expr_parse_match() @@ -1150,17 +1070,15 @@ static void expr_parse_string_expr (void); * Match a token to the lookahead, then advance lookahead. */ static void -expr_parse_match (sc_int token) -{ - if (expr_parse_lookahead == token) - expr_parse_lookahead = expr_next_token (); - else - { - /* Syntax error. */ - sc_error ("expr_parse_match: syntax error," - " expected %ld, got %ld\n", expr_parse_lookahead, token); - longjmp (expr_parse_error, 1); - } +expr_parse_match(sc_int token) { + if (expr_parse_lookahead == token) + expr_parse_lookahead = expr_next_token(); + else { + /* Syntax error. */ + sc_error("expr_parse_match: syntax error," + " expected %ld, got %ld\n", expr_parse_lookahead, token); + longjmp(expr_parse_error, 1); + } } @@ -1171,10 +1089,9 @@ expr_parse_match (sc_int token) * a list with no operators (although in practice we need to put a TOK_NONE * in here since some C compilers won't accept { } as an empty initializer). */ -typedef struct -{ - const sc_int token_count; - const sc_int tokens[6]; +typedef struct { + const sc_int token_count; + const sc_int tokens[6]; } sc_precedence_entry_t; #if 0 /* @@ -1183,14 +1100,14 @@ typedef struct * comparisons, and boolean combiners. */ static const sc_precedence_entry_t PRECEDENCE_TABLE[] = { - {1, {TOK_OR}}, - {1, {TOK_AND}}, - {2, {TOK_EQUAL, TOK_NOT_EQUAL}}, - {4, {TOK_GREATER, TOK_LESS, TOK_GREATER_EQ, TOK_LESS_EQ}}, - {2, {TOK_ADD, TOK_SUBTRACT}}, - {3, {TOK_MULTIPLY, TOK_DIVIDE, TOK_MOD}}, - {1, {TOK_POWER}}, - {0, {TOK_NONE}} + {1, {TOK_OR}}, + {1, {TOK_AND}}, + {2, {TOK_EQUAL, TOK_NOT_EQUAL}}, + {4, {TOK_GREATER, TOK_LESS, TOK_GREATER_EQ, TOK_LESS_EQ}}, + {2, {TOK_ADD, TOK_SUBTRACT}}, + {3, {TOK_MULTIPLY, TOK_DIVIDE, TOK_MOD}}, + {1, {TOK_POWER}}, + {0, {TOK_NONE}} }; #else /* @@ -1199,12 +1116,16 @@ static const sc_precedence_entry_t PRECEDENCE_TABLE[] = { * subtraction, and boolean 'and' and 'or' have equal precedence. */ static const sc_precedence_entry_t PRECEDENCE_TABLE[] = { - {2, {TOK_OR, TOK_AND}}, - {6, {TOK_EQUAL, TOK_NOT_EQUAL, - TOK_GREATER, TOK_LESS, TOK_GREATER_EQ, TOK_LESS_EQ}}, - {4, {TOK_ADD, TOK_SUBTRACT, TOK_POWER, TOK_MOD}}, - {2, {TOK_MULTIPLY, TOK_DIVIDE}}, - {0, {TOK_NONE}} + {2, {TOK_OR, TOK_AND}}, + { + 6, { + TOK_EQUAL, TOK_NOT_EQUAL, + TOK_GREATER, TOK_LESS, TOK_GREATER_EQ, TOK_LESS_EQ + } + }, + {4, {TOK_ADD, TOK_SUBTRACT, TOK_POWER, TOK_MOD}}, + {2, {TOK_MULTIPLY, TOK_DIVIDE}}, + {0, {TOK_NONE}} }; #endif @@ -1216,23 +1137,20 @@ static const sc_precedence_entry_t PRECEDENCE_TABLE[] = { * entry passed in, and return TRUE if it contains the given token. */ static int -expr_parse_contains_token (const sc_precedence_entry_t *entry, sc_int token) -{ - sc_bool is_matched; - sc_int index_; - - /* Search the entry's token list for the token passed in. */ - is_matched = FALSE; - for (index_ = 0; index_ < entry->token_count; index_++) - { - if (entry->tokens[index_] == token) - { - is_matched = TRUE; - break; - } - } - - return is_matched; +expr_parse_contains_token(const sc_precedence_entry_t *entry, sc_int token) { + sc_bool is_matched; + sc_int index_; + + /* Search the entry's token list for the token passed in. */ + is_matched = FALSE; + for (index_ = 0; index_ < entry->token_count; index_++) { + if (entry->tokens[index_] == token) { + is_matched = TRUE; + break; + } + } + + return is_matched; } @@ -1244,34 +1162,31 @@ expr_parse_contains_token (const sc_precedence_entry_t *entry, sc_int token) * whether to parse a highest-precedence factor. */ static void -expr_parse_numeric_element (sc_int precedence) -{ - const sc_precedence_entry_t *entry; - - /* See if the level passed in has listed tokens. */ - entry = PRECEDENCE_TABLE + precedence; - if (entry->token_count == 0) - { - /* Precedence levels that hit the table end are factors. */ - expr_parse_numeric_factor (); - return; - } - - /* - * Parse initial higher-precedence factor, then others that associate - * with the given level. - */ - expr_parse_numeric_element (precedence + 1); - while (expr_parse_contains_token (entry, expr_parse_lookahead)) - { - sc_int token; - - /* Note token and match, parse next level, then action this token. */ - token = expr_parse_lookahead; - expr_parse_match (token); - expr_parse_numeric_element (precedence + 1); - expr_eval_action (token); - } +expr_parse_numeric_element(sc_int precedence) { + const sc_precedence_entry_t *entry; + + /* See if the level passed in has listed tokens. */ + entry = PRECEDENCE_TABLE + precedence; + if (entry->token_count == 0) { + /* Precedence levels that hit the table end are factors. */ + expr_parse_numeric_factor(); + return; + } + + /* + * Parse initial higher-precedence factor, then others that associate + * with the given level. + */ + expr_parse_numeric_element(precedence + 1); + while (expr_parse_contains_token(entry, expr_parse_lookahead)) { + sc_int token; + + /* Note token and match, parse next level, then action this token. */ + token = expr_parse_lookahead; + expr_parse_match(token); + expr_parse_numeric_element(precedence + 1); + expr_eval_action(token); + } } @@ -1281,10 +1196,9 @@ expr_parse_numeric_element (sc_int precedence) * Parse a complete numeric (sub-)expression. */ static void -expr_parse_numeric_expr (void) -{ - /* Call the parser of the lowest precedence operators. */ - expr_parse_numeric_element (0); +expr_parse_numeric_expr(void) { + /* Call the parser of the lowest precedence operators. */ + expr_parse_numeric_element(0); } @@ -1294,161 +1208,155 @@ expr_parse_numeric_expr (void) * Parse a numeric expression factor. */ static void -expr_parse_numeric_factor (void) -{ - /* Handle factors based on lookahead token. */ - switch (expr_parse_lookahead) - { - /* Handle straightforward factors first. */ - case TOK_LPAREN: - expr_parse_match (TOK_LPAREN); - expr_parse_numeric_expr (); - expr_parse_match (TOK_RPAREN); - break; - - case TOK_UMINUS: - expr_parse_match (TOK_UMINUS); - expr_parse_numeric_factor (); - expr_eval_action (TOK_UMINUS); - break; - - case TOK_UPLUS: - expr_parse_match (TOK_UPLUS); - expr_parse_numeric_factor (); - break; - - case TOK_INTEGER: - expr_eval_action (TOK_INTEGER); - expr_parse_match (TOK_INTEGER); - break; - - case TOK_VARIABLE: - { - sc_vartype_t token_value, vt_rvalue; - sc_int type; - - expr_current_token_value (&token_value); - if (!var_get (expr_varset, token_value.string, &type, &vt_rvalue)) - { - sc_error ("expr_parse_numeric_factor:" - " undefined variable, %s\n", token_value.string); - longjmp (expr_parse_error, 1); - } - if (type != VAR_INTEGER) - { - sc_error ("expr_parse_numeric_factor:" - " string variable in numeric context, %s\n", - token_value.string); - longjmp (expr_parse_error, 1); - } - expr_eval_action (TOK_VARIABLE); - expr_parse_match (TOK_VARIABLE); - break; - } - - /* Handle functions as factors. */ - case TOK_ABS: - /* Parse as "abs (val)". */ - expr_parse_match (TOK_ABS); - expr_parse_match (TOK_LPAREN); - expr_parse_numeric_expr (); - expr_parse_match (TOK_RPAREN); - expr_eval_action (TOK_ABS); - break; - - case TOK_IF: - /* Parse as "if (boolean, val1, val2)". */ - expr_parse_match (TOK_IF); - expr_parse_match (TOK_LPAREN); - expr_parse_numeric_expr (); - expr_parse_match (TOK_COMMA); - expr_parse_numeric_expr (); - expr_parse_match (TOK_COMMA); - expr_parse_numeric_expr (); - expr_parse_match (TOK_RPAREN); - expr_eval_action (TOK_IF); - break; - - case TOK_RANDOM: - /* Parse as "random (low, high)". */ - expr_parse_match (TOK_RANDOM); - expr_parse_match (TOK_LPAREN); - expr_parse_numeric_expr (); - expr_parse_match (TOK_COMMA); - expr_parse_numeric_expr (); - expr_parse_match (TOK_RPAREN); - expr_eval_action (TOK_RANDOM); - break; - - case TOK_MAX: - case TOK_MIN: - case TOK_EITHER: - /* Parse as "<func> (val1[,val2[,val3...]]])". */ - { - sc_int token, argument_count; - - /* Match up the function name and opening parenthesis. */ - token = expr_parse_lookahead; - expr_parse_match (token); - expr_parse_match (TOK_LPAREN); - - /* Count variable number of arguments as they are stacked. */ - expr_parse_numeric_expr (); - argument_count = 1; - while (expr_parse_lookahead == TOK_COMMA) - { - expr_parse_match (TOK_COMMA); - expr_parse_numeric_expr (); - argument_count++; - } - expr_parse_match (TOK_RPAREN); - - /* Push additional value -- the count of arguments. */ - expr_eval_push_integer (argument_count); - expr_eval_action (token); - break; - } - - case TOK_INSTR: - /* Parse as "instr (val1, val2)". */ - expr_parse_match (TOK_INSTR); - expr_parse_match (TOK_LPAREN); - expr_parse_string_expr (); - expr_parse_match (TOK_COMMA); - expr_parse_string_expr (); - expr_parse_match (TOK_RPAREN); - expr_eval_action (TOK_INSTR); - break; - - case TOK_LEN: - /* Parse as "len (val)". */ - expr_parse_match (TOK_LEN); - expr_parse_match (TOK_LPAREN); - expr_parse_string_expr (); - expr_parse_match (TOK_RPAREN); - expr_eval_action (TOK_LEN); - break; - - case TOK_VAL: - /* Parse as "val (val)". */ - expr_parse_match (TOK_VAL); - expr_parse_match (TOK_LPAREN); - expr_parse_string_expr (); - expr_parse_match (TOK_RPAREN); - expr_eval_action (TOK_VAL); - break; - - case TOK_IDENT: - /* Unrecognized function-type token. */ - sc_error ("expr_parse_numeric_factor: syntax error, unknown ident\n"); - longjmp (expr_parse_error, 1); - - default: - /* Syntax error. */ - sc_error ("expr_parse_numeric_factor:" - " syntax error, unexpected token, %ld\n", expr_parse_lookahead); - longjmp (expr_parse_error, 1); - } +expr_parse_numeric_factor(void) { + /* Handle factors based on lookahead token. */ + switch (expr_parse_lookahead) { + /* Handle straightforward factors first. */ + case TOK_LPAREN: + expr_parse_match(TOK_LPAREN); + expr_parse_numeric_expr(); + expr_parse_match(TOK_RPAREN); + break; + + case TOK_UMINUS: + expr_parse_match(TOK_UMINUS); + expr_parse_numeric_factor(); + expr_eval_action(TOK_UMINUS); + break; + + case TOK_UPLUS: + expr_parse_match(TOK_UPLUS); + expr_parse_numeric_factor(); + break; + + case TOK_INTEGER: + expr_eval_action(TOK_INTEGER); + expr_parse_match(TOK_INTEGER); + break; + + case TOK_VARIABLE: { + sc_vartype_t token_value, vt_rvalue; + sc_int type; + + expr_current_token_value(&token_value); + if (!var_get(expr_varset, token_value.string, &type, &vt_rvalue)) { + sc_error("expr_parse_numeric_factor:" + " undefined variable, %s\n", token_value.string); + longjmp(expr_parse_error, 1); + } + if (type != VAR_INTEGER) { + sc_error("expr_parse_numeric_factor:" + " string variable in numeric context, %s\n", + token_value.string); + longjmp(expr_parse_error, 1); + } + expr_eval_action(TOK_VARIABLE); + expr_parse_match(TOK_VARIABLE); + break; + } + + /* Handle functions as factors. */ + case TOK_ABS: + /* Parse as "abs (val)". */ + expr_parse_match(TOK_ABS); + expr_parse_match(TOK_LPAREN); + expr_parse_numeric_expr(); + expr_parse_match(TOK_RPAREN); + expr_eval_action(TOK_ABS); + break; + + case TOK_IF: + /* Parse as "if (boolean, val1, val2)". */ + expr_parse_match(TOK_IF); + expr_parse_match(TOK_LPAREN); + expr_parse_numeric_expr(); + expr_parse_match(TOK_COMMA); + expr_parse_numeric_expr(); + expr_parse_match(TOK_COMMA); + expr_parse_numeric_expr(); + expr_parse_match(TOK_RPAREN); + expr_eval_action(TOK_IF); + break; + + case TOK_RANDOM: + /* Parse as "random (low, high)". */ + expr_parse_match(TOK_RANDOM); + expr_parse_match(TOK_LPAREN); + expr_parse_numeric_expr(); + expr_parse_match(TOK_COMMA); + expr_parse_numeric_expr(); + expr_parse_match(TOK_RPAREN); + expr_eval_action(TOK_RANDOM); + break; + + case TOK_MAX: + case TOK_MIN: + case TOK_EITHER: + /* Parse as "<func> (val1[,val2[,val3...]]])". */ + { + sc_int token, argument_count; + + /* Match up the function name and opening parenthesis. */ + token = expr_parse_lookahead; + expr_parse_match(token); + expr_parse_match(TOK_LPAREN); + + /* Count variable number of arguments as they are stacked. */ + expr_parse_numeric_expr(); + argument_count = 1; + while (expr_parse_lookahead == TOK_COMMA) { + expr_parse_match(TOK_COMMA); + expr_parse_numeric_expr(); + argument_count++; + } + expr_parse_match(TOK_RPAREN); + + /* Push additional value -- the count of arguments. */ + expr_eval_push_integer(argument_count); + expr_eval_action(token); + break; + } + + case TOK_INSTR: + /* Parse as "instr (val1, val2)". */ + expr_parse_match(TOK_INSTR); + expr_parse_match(TOK_LPAREN); + expr_parse_string_expr(); + expr_parse_match(TOK_COMMA); + expr_parse_string_expr(); + expr_parse_match(TOK_RPAREN); + expr_eval_action(TOK_INSTR); + break; + + case TOK_LEN: + /* Parse as "len (val)". */ + expr_parse_match(TOK_LEN); + expr_parse_match(TOK_LPAREN); + expr_parse_string_expr(); + expr_parse_match(TOK_RPAREN); + expr_eval_action(TOK_LEN); + break; + + case TOK_VAL: + /* Parse as "val (val)". */ + expr_parse_match(TOK_VAL); + expr_parse_match(TOK_LPAREN); + expr_parse_string_expr(); + expr_parse_match(TOK_RPAREN); + expr_eval_action(TOK_VAL); + break; + + case TOK_IDENT: + /* Unrecognized function-type token. */ + sc_error("expr_parse_numeric_factor: syntax error, unknown ident\n"); + longjmp(expr_parse_error, 1); + + default: + /* Syntax error. */ + sc_error("expr_parse_numeric_factor:" + " syntax error, unexpected token, %ld\n", expr_parse_lookahead); + longjmp(expr_parse_error, 1); + } } @@ -1458,20 +1366,18 @@ expr_parse_numeric_factor (void) * Parse a complete string (sub-)expression. */ static void -expr_parse_string_expr (void) -{ - /* - * Parse a string factor, then all repeated concatenations. Because the '+' - * and '&' are context sensitive, we have to invent/translate them into the - * otherwise unused TOK_CONCATENATE for evaluation. - */ - expr_parse_string_factor (); - while (expr_parse_lookahead == TOK_AND || expr_parse_lookahead == TOK_ADD) - { - expr_parse_match (expr_parse_lookahead); - expr_parse_string_factor (); - expr_eval_action (TOK_CONCATENATE); - } +expr_parse_string_expr(void) { + /* + * Parse a string factor, then all repeated concatenations. Because the '+' + * and '&' are context sensitive, we have to invent/translate them into the + * otherwise unused TOK_CONCATENATE for evaluation. + */ + expr_parse_string_factor(); + while (expr_parse_lookahead == TOK_AND || expr_parse_lookahead == TOK_ADD) { + expr_parse_match(expr_parse_lookahead); + expr_parse_string_factor(); + expr_eval_action(TOK_CONCATENATE); + } } @@ -1481,114 +1387,109 @@ expr_parse_string_expr (void) * Parse a string expression factor. */ static void -expr_parse_string_factor (void) -{ - /* Handle factors based on lookahead token. */ - switch (expr_parse_lookahead) - { - /* Handle straightforward factors first. */ - case TOK_LPAREN: - expr_parse_match (TOK_LPAREN); - expr_parse_string_expr (); - expr_parse_match (TOK_RPAREN); - break; - - case TOK_STRING: - expr_eval_action (TOK_STRING); - expr_parse_match (TOK_STRING); - break; - - case TOK_VARIABLE: - { - sc_vartype_t token_value, vt_rvalue; - sc_int type; - - expr_current_token_value (&token_value); - if (!var_get (expr_varset, token_value.string, &type, &vt_rvalue)) - { - sc_error ("expr_parse_string_factor:" - " undefined variable, %s\n", token_value.string); - longjmp (expr_parse_error, 1); - } - if (type != VAR_STRING) - { - sc_error ("expr_parse_string_factor:" - " numeric variable in string context, %s\n", - token_value.string); - longjmp (expr_parse_error, 1); - } - expr_eval_action (TOK_VARIABLE); - expr_parse_match (TOK_VARIABLE); - break; - } - - /* Handle functions as factors. */ - case TOK_UPPER: - case TOK_LOWER: - case TOK_PROPER: - /* Parse as "<func> (text)". */ - { - sc_int token; - - token = expr_parse_lookahead; - expr_parse_match (token); - expr_parse_match (TOK_LPAREN); - expr_parse_string_expr (); - expr_parse_match (TOK_RPAREN); - expr_eval_action (token); - break; - } - - case TOK_LEFT: - case TOK_RIGHT: - /* Parse as "<func> (text,length)". */ - { - sc_int token; - - token = expr_parse_lookahead; - expr_parse_match (token); - expr_parse_match (TOK_LPAREN); - expr_parse_string_expr (); - expr_parse_match (TOK_COMMA); - expr_parse_numeric_expr (); - expr_parse_match (TOK_RPAREN); - expr_eval_action (token); - break; - } - - case TOK_MID: - /* Parse as "mid (text,start,length)". */ - expr_parse_match (TOK_MID); - expr_parse_match (TOK_LPAREN); - expr_parse_string_expr (); - expr_parse_match (TOK_COMMA); - expr_parse_numeric_expr (); - expr_parse_match (TOK_COMMA); - expr_parse_numeric_expr (); - expr_parse_match (TOK_RPAREN); - expr_eval_action (TOK_MID); - break; - - case TOK_STR: - /* Parse as "str (val)". */ - expr_parse_match (TOK_STR); - expr_parse_match (TOK_LPAREN); - expr_parse_numeric_expr (); - expr_parse_match (TOK_RPAREN); - expr_eval_action (TOK_STR); - break; - - case TOK_IDENT: - /* Unrecognized function-type token. */ - sc_error ("expr_parse_string_factor: syntax error, unknown ident\n"); - longjmp (expr_parse_error, 1); - - default: - /* Syntax error. */ - sc_error ("expr_parse_string_factor:" - " syntax error, unexpected token, %ld\n", expr_parse_lookahead); - longjmp (expr_parse_error, 1); - } +expr_parse_string_factor(void) { + /* Handle factors based on lookahead token. */ + switch (expr_parse_lookahead) { + /* Handle straightforward factors first. */ + case TOK_LPAREN: + expr_parse_match(TOK_LPAREN); + expr_parse_string_expr(); + expr_parse_match(TOK_RPAREN); + break; + + case TOK_STRING: + expr_eval_action(TOK_STRING); + expr_parse_match(TOK_STRING); + break; + + case TOK_VARIABLE: { + sc_vartype_t token_value, vt_rvalue; + sc_int type; + + expr_current_token_value(&token_value); + if (!var_get(expr_varset, token_value.string, &type, &vt_rvalue)) { + sc_error("expr_parse_string_factor:" + " undefined variable, %s\n", token_value.string); + longjmp(expr_parse_error, 1); + } + if (type != VAR_STRING) { + sc_error("expr_parse_string_factor:" + " numeric variable in string context, %s\n", + token_value.string); + longjmp(expr_parse_error, 1); + } + expr_eval_action(TOK_VARIABLE); + expr_parse_match(TOK_VARIABLE); + break; + } + + /* Handle functions as factors. */ + case TOK_UPPER: + case TOK_LOWER: + case TOK_PROPER: + /* Parse as "<func> (text)". */ + { + sc_int token; + + token = expr_parse_lookahead; + expr_parse_match(token); + expr_parse_match(TOK_LPAREN); + expr_parse_string_expr(); + expr_parse_match(TOK_RPAREN); + expr_eval_action(token); + break; + } + + case TOK_LEFT: + case TOK_RIGHT: + /* Parse as "<func> (text,length)". */ + { + sc_int token; + + token = expr_parse_lookahead; + expr_parse_match(token); + expr_parse_match(TOK_LPAREN); + expr_parse_string_expr(); + expr_parse_match(TOK_COMMA); + expr_parse_numeric_expr(); + expr_parse_match(TOK_RPAREN); + expr_eval_action(token); + break; + } + + case TOK_MID: + /* Parse as "mid (text,start,length)". */ + expr_parse_match(TOK_MID); + expr_parse_match(TOK_LPAREN); + expr_parse_string_expr(); + expr_parse_match(TOK_COMMA); + expr_parse_numeric_expr(); + expr_parse_match(TOK_COMMA); + expr_parse_numeric_expr(); + expr_parse_match(TOK_RPAREN); + expr_eval_action(TOK_MID); + break; + + case TOK_STR: + /* Parse as "str (val)". */ + expr_parse_match(TOK_STR); + expr_parse_match(TOK_LPAREN); + expr_parse_numeric_expr(); + expr_parse_match(TOK_RPAREN); + expr_eval_action(TOK_STR); + break; + + case TOK_IDENT: + /* Unrecognized function-type token. */ + sc_error("expr_parse_string_factor: syntax error, unknown ident\n"); + longjmp(expr_parse_error, 1); + + default: + /* Syntax error. */ + sc_error("expr_parse_string_factor:" + " syntax error, unexpected token, %ld\n", expr_parse_lookahead); + longjmp(expr_parse_error, 1); + } } @@ -1599,38 +1500,34 @@ expr_parse_string_factor (void) * value of the expression. */ static sc_bool -expr_evaluate_expression (const sc_char *expression, sc_var_setref_t vars, - sc_int assign_type, sc_vartype_t *vt_rvalue) -{ - assert (assign_type == VAR_INTEGER || assign_type == VAR_STRING); - - /* Reset values stack and start tokenizer. */ - expr_eval_start (vars); - expr_tokenize_start (expression); - - /* Try parsing an expression, and catch errors. */ - if (setjmp (expr_parse_error) == 0) - { - /* Parse an expression, and ensure it ends at string end. */ - expr_parse_lookahead = expr_next_token (); - if (assign_type == VAR_STRING) - expr_parse_string_expr (); - else - expr_parse_numeric_expr (); - expr_parse_match (TOK_EOS); - } - else - { - /* Parse error -- clean up tokenizer, collect garbage, and fail. */ - expr_tokenize_end (); - expr_eval_garbage_collect (); - return FALSE; - } - - /* Clean up tokenizer and return successfully with result. */ - expr_tokenize_end (); - expr_eval_result (vt_rvalue); - return TRUE; +expr_evaluate_expression(const sc_char *expression, sc_var_setref_t vars, + sc_int assign_type, sc_vartype_t *vt_rvalue) { + assert(assign_type == VAR_INTEGER || assign_type == VAR_STRING); + + /* Reset values stack and start tokenizer. */ + expr_eval_start(vars); + expr_tokenize_start(expression); + + /* Try parsing an expression, and catch errors. */ + if (setjmp(expr_parse_error) == 0) { + /* Parse an expression, and ensure it ends at string end. */ + expr_parse_lookahead = expr_next_token(); + if (assign_type == VAR_STRING) + expr_parse_string_expr(); + else + expr_parse_numeric_expr(); + expr_parse_match(TOK_EOS); + } else { + /* Parse error -- clean up tokenizer, collect garbage, and fail. */ + expr_tokenize_end(); + expr_eval_garbage_collect(); + return FALSE; + } + + /* Clean up tokenizer and return successfully with result. */ + expr_tokenize_end(); + expr_eval_result(vt_rvalue); + return TRUE; } @@ -1644,33 +1541,31 @@ expr_evaluate_expression (const sc_char *expression, sc_var_setref_t vars, * it. */ sc_bool -expr_eval_numeric_expression (const sc_char *expression, - sc_var_setref_t vars, sc_int *rvalue) -{ - sc_vartype_t vt_rvalue; - sc_bool status; - assert (expression && vars && rvalue); - - /* Evaluate numeric expression, and return value if valid. */ - status = expr_evaluate_expression (expression, vars, VAR_INTEGER, &vt_rvalue); - if (status) - *rvalue = vt_rvalue.integer; - return status; +expr_eval_numeric_expression(const sc_char *expression, + sc_var_setref_t vars, sc_int *rvalue) { + sc_vartype_t vt_rvalue; + sc_bool status; + assert(expression && vars && rvalue); + + /* Evaluate numeric expression, and return value if valid. */ + status = expr_evaluate_expression(expression, vars, VAR_INTEGER, &vt_rvalue); + if (status) + *rvalue = vt_rvalue.integer; + return status; } sc_bool -expr_eval_string_expression (const sc_char *expression, - sc_var_setref_t vars, sc_char **rvalue) -{ - sc_vartype_t vt_rvalue; - sc_bool status; - assert (expression && vars && rvalue); - - /* Evaluate string expression, and return value if valid. */ - status = expr_evaluate_expression (expression, vars, VAR_STRING, &vt_rvalue); - if (status) - *rvalue = vt_rvalue.mutable_string; - return status; +expr_eval_string_expression(const sc_char *expression, + sc_var_setref_t vars, sc_char **rvalue) { + sc_vartype_t vt_rvalue; + sc_bool status; + assert(expression && vars && rvalue); + + /* Evaluate string expression, and return value if valid. */ + status = expr_evaluate_expression(expression, vars, VAR_STRING, &vt_rvalue); + if (status) + *rvalue = vt_rvalue.mutable_string; + return status; } } // End of namespace Adrift |