1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "glk/adrift/scare.h"
24 #include "glk/adrift/scprotos.h"
25 #include "glk/jumps.h"
26
27 namespace Glk {
28 namespace Adrift {
29
30 /*
31 * Module notes:
32 *
33 * o The tokenizer doesn't differentiate between "&", "&&", and "and", but "&"
34 * is context sensitive -- either a logical and for numerics, or concaten-
35 * ation for strings. As a result, "&&" and "and" also work as string
36 * concatenators when used in string expressions. It may not be to spec,
37 * but we'll call this a "feature".
38 */
39
40 /* Assorted definitions and constants. */
41 enum { MAX_NESTING_DEPTH = 32 };
42 static const sc_char NUL = '\0';
43 static const sc_char PERCENT = '%';
44 static const sc_char SINGLE_QUOTE = '\'';
45 static const sc_char DOUBLE_QUOTE = '"';
46
47
48 /*
49 * Tokens. Single character tokens are represented by their ascii value
50 * (0-255), others by values above 255. -1 represents a null token. Because
51 * '&' and '+' are context sensitive, the pseudo-token TOK_CONCATENATE
52 * serves to indicate string concatenation -- it's never returned by the
53 * tokenizer.
54 */
55 enum {
56 TOK_NONE = -1,
57 TOK_ADD = '+', TOK_SUBTRACT = '-', TOK_MULTIPLY = '*', TOK_DIVIDE = '/',
58 TOK_AND = '&', TOK_OR = '|',
59 TOK_LPAREN = '(', TOK_RPAREN = ')', TOK_COMMA = ',', TOK_POWER = '^',
60 TOK_EQUAL = '=', TOK_GREATER = '>', TOK_LESS = '<',
61
62 TOK_IDENT = 256,
63 TOK_INTEGER, TOK_STRING, TOK_VARIABLE, TOK_UMINUS, TOK_UPLUS,
64 TOK_MOD, TOK_NOT_EQUAL, TOK_GREATER_EQ, TOK_LESS_EQ, TOK_IF,
65 TOK_MIN, TOK_MAX, TOK_EITHER, TOK_RANDOM, TOK_INSTR, TOK_LEN, TOK_VAL,
66 TOK_ABS, TOK_UPPER, TOK_LOWER, TOK_PROPER, TOK_RIGHT, TOK_LEFT, TOK_MID,
67 TOK_STR, TOK_CONCATENATE,
68 TOK_EOS
69 };
70
71 /*
72 * Small tables tying multicharacter tokens strings to tokens. At present,
73 * the string lengths for names are not used.
74 */
75 struct sc_expr_multichar_t {
76 const sc_char *const name;
77 const sc_int length;
78 const sc_int token;
79 };
80
81 static const sc_expr_multichar_t FUNCTION_TOKENS[] = {
82 {"either", 6, TOK_EITHER},
83 {"proper", 6, TOK_PROPER}, {"pcase", 5, TOK_PROPER}, {"instr", 5, TOK_INSTR},
84 {"upper", 5, TOK_UPPER}, {"ucase", 5, TOK_UPPER},
85 {"lower", 5, TOK_LOWER}, {"lcase", 5, TOK_LOWER},
86 {"right", 5, TOK_RIGHT}, {"left", 4, TOK_LEFT},
87 {"rand", 4, TOK_RANDOM}, {"max", 3, TOK_MAX}, {"min", 3, TOK_MIN},
88 {"mod", 3, TOK_MOD}, {"abs", 3, TOK_ABS}, {"len", 3, TOK_LEN},
89 {"val", 3, TOK_VAL}, {"and", 3, TOK_AND}, {"mid", 3, TOK_MID},
90 {"str", 3, TOK_STR}, {"or", 2, TOK_OR}, {"if", 2, TOK_IF},
91 {NULL, 0, TOK_NONE}
92 };
93 static const sc_expr_multichar_t OPERATOR_TOKENS[] = {
94 {"&&", 2, TOK_AND}, {"||", 2, TOK_OR},
95 {"==", 2, TOK_EQUAL}, {"!=", 2, TOK_NOT_EQUAL},
96 {"<>", 2, TOK_NOT_EQUAL}, {">=", 2, TOK_GREATER_EQ}, {"<=", 2, TOK_LESS_EQ},
97 {NULL, 0, TOK_NONE}
98 };
99
100
101 /*
102 * expr_multichar_search()
103 *
104 * Multicharacter token table search, returns the matching token, or
105 * TOK_NONE if no match.
106 */
expr_multichar_search(const sc_char * name,const sc_expr_multichar_t * table)107 static sc_int expr_multichar_search(const sc_char *name, const sc_expr_multichar_t *table) {
108 const sc_expr_multichar_t *entry;
109
110 /* Scan the table for a case-independent full string match. */
111 for (entry = table; entry->name; entry++) {
112 if (sc_strcasecmp(name, entry->name) == 0)
113 break;
114 }
115
116 /* Return the token matched, or TOK_NONE. */
117 return entry->name ? entry->token : (sc_int)TOK_NONE;
118 }
119
120
121 /* Tokenizer variables. */
122 static const sc_char *expr_expression = NULL;
123 static sc_int expr_index = 0;
124 static sc_vartype_t expr_token_value;
125 static sc_char *expr_temporary = NULL;
126 static sc_int expr_current_token = TOK_NONE;
127
128 /*
129 * expr_tokenize_start()
130 * expr_tokenize_end()
131 *
132 * Start and wrap up expression string tokenization.
133 */
expr_tokenize_start(const sc_char * expression)134 static void expr_tokenize_start(const sc_char *expression) {
135 static sc_bool initialized = FALSE;
136
137 /* On first call only, verify the string lengths in the tables. */
138 if (!initialized) {
139 const sc_expr_multichar_t *entry;
140
141 /* Compare table lengths with string lengths. */
142 for (entry = FUNCTION_TOKENS; entry->name; entry++) {
143 if (entry->length != (sc_int) strlen(entry->name)) {
144 sc_fatal("expr_tokenize_start:"
145 " token string length is wrong for \"%s\"\n",
146 entry->name);
147 }
148 }
149
150 for (entry = OPERATOR_TOKENS; entry->name; entry++) {
151 if (entry->length != (sc_int) strlen(entry->name)) {
152 sc_fatal("expr_tokenize_start:"
153 " operator string length is wrong for \"%s\"\n",
154 entry->name);
155 }
156 }
157
158 initialized = TRUE;
159 }
160
161 /* Save expression, and restart index. */
162 expr_expression = expression;
163 expr_index = 0;
164
165 /* Allocate a temporary token value/literals string. */
166 assert(!expr_temporary);
167 expr_temporary = (sc_char *)sc_malloc(strlen(expression) + 1);
168
169 /* Reset last token to none. */
170 expr_current_token = TOK_NONE;
171 }
172
expr_tokenize_end(void)173 static void expr_tokenize_end(void) {
174 /* Deallocate temporary strings, clear expression. */
175 sc_free(expr_temporary);
176 expr_temporary = NULL;
177 expr_expression = NULL;
178 expr_index = 0;
179 expr_current_token = TOK_NONE;
180 }
181
182
183 /*
184 * expr_next_token_unadjusted()
185 * expr_next_token()
186 *
187 * Return the next token from the current expression. The initial token may
188 * be adjusted into a unary +/- depending on the value of the previous token.
189 */
expr_next_token_unadjusted(sc_vartype_t * token_value)190 static sc_int expr_next_token_unadjusted(sc_vartype_t *token_value) {
191 sc_int c;
192 assert(expr_expression);
193
194 /* Skip any and all leading whitespace. */
195 do {
196 c = expr_expression[expr_index++];
197 } while (sc_isspace(c) && c != NUL);
198
199 /* Return EOS if at expression end. */
200 if (c == NUL) {
201 expr_index--;
202 return TOK_EOS;
203 }
204
205 /*
206 * Identify and return numerics. We deal only with unsigned numbers here;
207 * the unary +/- tokens take care of any integer sign issues.
208 */
209 else if (sc_isdigit(c)) {
210 sc_int value;
211
212 sscanf(expr_expression + expr_index - 1, "%ld", &value);
213
214 while (sc_isdigit(c) && c != NUL)
215 c = expr_expression[expr_index++];
216 expr_index--;
217
218 token_value->integer = value;
219 return TOK_INTEGER;
220 }
221
222 /* Identify and return variable references. */
223 else if (c == PERCENT) {
224 sc_int index_;
225
226 /* Copy variable name. */
227 c = expr_expression[expr_index++];
228 for (index_ = 0; c != PERCENT && c != NUL;) {
229 expr_temporary[index_++] = c;
230 c = expr_expression[expr_index++];
231 }
232 expr_temporary[index_++] = NUL;
233
234 if (c == NUL) {
235 sc_error("expr_next_token_unadjusted:"
236 " warning: unterminated variable name\n");
237 expr_index--;
238 }
239
240 /* Return a variable name. */
241 token_value->string = expr_temporary;
242 return TOK_VARIABLE;
243 }
244
245 /* Identify and return string literals. */
246 else if (c == DOUBLE_QUOTE || c == SINGLE_QUOTE) {
247 sc_int index_;
248 sc_char quote;
249
250 /* Copy maximal string literal. */
251 quote = c;
252 c = expr_expression[expr_index++];
253 for (index_ = 0; c != quote && c != NUL;) {
254 expr_temporary[index_++] = c;
255 c = expr_expression[expr_index++];
256 }
257 expr_temporary[index_++] = NUL;
258
259 if (c == NUL) {
260 sc_error("expr_next_token_unadjusted:"
261 " warning: unterminated string literal\n");
262 expr_index--;
263 }
264
265 /* Return string literal. */
266 token_value->string = expr_temporary;
267 return TOK_STRING;
268 }
269
270 /* Identify ids and other multicharacter tokens. */
271 else if (sc_isalpha(c)) {
272 sc_int index_, token;
273
274 /*
275 * Copy maximal alphabetical string. While an ident would normally
276 * be alpha followed by zero or more alnum, for Adrift purposes we
277 * use only alpha -- all idents should really be "functions", and
278 * in particular we want to see "mod7" as "mod" and 7 separately.
279 */
280 for (index_ = 0; sc_isalpha(c) && c != NUL;) {
281 expr_temporary[index_++] = c;
282 c = expr_expression[expr_index++];
283 }
284 expr_index--;
285 expr_temporary[index_++] = NUL;
286
287 /*
288 * Check for a function name, and if known, return that, otherwise
289 * return a bare id.
290 */
291 token = expr_multichar_search(expr_temporary, FUNCTION_TOKENS);
292 if (token == TOK_NONE) {
293 token_value->string = expr_temporary;
294 return TOK_IDENT;
295 } else
296 return token;
297 }
298
299 /*
300 * Last chance check for two-character (multichar) operators, and if none
301 * then return a single-character token.
302 */
303 else {
304 sc_char operator_[3];
305 sc_int token;
306
307 /*
308 * Build a two-character string. If we happen to be at the last
309 * expression character, we'll pick up the expression NUL into
310 * operator_[1], so no need to special case end of expression here.
311 */
312 operator_[0] = c;
313 operator_[1] = expr_expression[expr_index];
314 operator_[2] = NUL;
315
316 /* Search for this two-character operator. */
317 if (operator_[0] != NUL && operator_[1] != NUL) {
318 token = expr_multichar_search(operator_, OPERATOR_TOKENS);
319 if (token != TOK_NONE) {
320 /* Matched, so advance expression index and return this token. */
321 expr_index++;
322 return token;
323 }
324 }
325
326 /*
327 * No match, or at last expression character; return a single character
328 * token.
329 */
330 return c;
331 }
332 }
333
expr_next_token(void)334 static sc_int expr_next_token(void) {
335 sc_int token;
336 sc_vartype_t token_value;
337
338 /*
339 * Get the basic next token. We may adjust it later for unary minus/plus
340 * depending on what it is, and the prior token.
341 */
342 token_value.voidp = NULL;
343 token = expr_next_token_unadjusted(&token_value);
344
345 /* Special handling for unary minus/plus signs. */
346 if (token == TOK_SUBTRACT || token == TOK_ADD) {
347 /*
348 * Unary minus/plus if prior token was an operator or a comparison, left
349 * parenthesis, or comma, or if there was no prior token.
350 */
351 switch (expr_current_token) {
352 case TOK_MOD:
353 case TOK_POWER:
354 case TOK_ADD:
355 case TOK_SUBTRACT:
356 case TOK_MULTIPLY:
357 case TOK_DIVIDE:
358 case TOK_AND:
359 case TOK_OR:
360 case TOK_EQUAL:
361 case TOK_GREATER:
362 case TOK_LESS:
363 case TOK_NOT_EQUAL:
364 case TOK_GREATER_EQ:
365 case TOK_LESS_EQ:
366 case TOK_LPAREN:
367 case TOK_COMMA:
368 case TOK_NONE:
369 token = (token == TOK_SUBTRACT) ? TOK_UMINUS : TOK_UPLUS;
370 break;
371
372 default:
373 break;
374 }
375 }
376
377 /* Set current token to the one just found, and return it. */
378 expr_current_token = token;
379 expr_token_value = token_value;
380 return token;
381 }
382
383
384 /*
385 * expr_current_token_value()
386 *
387 * Return the token value of the current token. Undefined if the current
388 * token is not numeric, an id, or a variable.
389 */
expr_current_token_value(sc_vartype_t * value)390 static void expr_current_token_value(sc_vartype_t *value) {
391 /* Quick check that the value is a valid one. */
392 switch (expr_current_token) {
393 case TOK_INTEGER:
394 case TOK_STRING:
395 case TOK_VARIABLE:
396 case TOK_IDENT:
397 break;
398
399 default:
400 sc_fatal("expr_current_token_value:"
401 " taking undefined token value, %ld\n", expr_current_token);
402 }
403
404 /* Return value. */
405 *value = expr_token_value;
406 }
407
408
409 /*
410 * Evaluation values stack, uses a variable type so it can contain both
411 * integers and strings, and flags strings for possible garbage collection
412 * on parse errors.
413 */
414 struct sc_stack_t {
415 sc_bool is_collectible;
416 sc_vartype_t value;
417 };
418 static sc_stack_t expr_eval_stack[MAX_NESTING_DEPTH];
419 static sc_int expr_eval_stack_index = 0;
420
421 /* Variables set to reference for %...% values. */
422 static sc_var_setref_t expr_varset = NULL;
423
424 /*
425 * expr_eval_start()
426 *
427 * Reset the evaluation stack to an empty state, and register the variables
428 * set to use when referencing %...% variables.
429 */
expr_eval_start(sc_var_setref_t vars)430 static void expr_eval_start(sc_var_setref_t vars) {
431 expr_eval_stack_index = 0;
432 expr_varset = vars;
433 }
434
435
436 /*
437 * expr_eval_garbage_collect()
438 *
439 * In case of parse error, empty out and free all collectible malloced
440 * strings left in the evaluation array.
441 */
expr_eval_garbage_collect(void)442 static void expr_eval_garbage_collect(void) {
443 sc_int index_;
444
445 /*
446 * Find and free all collectible strings still in the stack. We have to
447 * free through mutable string rather than const string.
448 */
449 for (index_ = 0; index_ < expr_eval_stack_index; index_++) {
450 if (expr_eval_stack[index_].is_collectible)
451 sc_free(expr_eval_stack[index_].value.mutable_string);
452 }
453
454 /* Reset the stack index, for clarity and neatness. */
455 expr_eval_stack_index = 0;
456 }
457
458
459 /*
460 * expr_eval_push_integer()
461 * expr_eval_push_string()
462 * expr_eval_push_alloced_string()
463 *
464 * Push a value onto the values stack. Strings are malloc'ed and copied,
465 * and the copy is placed onto the stack, unless _alloced_string() is used;
466 * for this case, the input string is assumed to be already malloc'ed, and
467 * the caller should not subsequently free the string.
468 */
expr_eval_push_integer(sc_int value)469 static void expr_eval_push_integer(sc_int value) {
470 if (expr_eval_stack_index >= MAX_NESTING_DEPTH)
471 sc_fatal("expr_eval_push_integer: stack overflow\n");
472
473 expr_eval_stack[expr_eval_stack_index].is_collectible = FALSE;
474 expr_eval_stack[expr_eval_stack_index++].value.integer = value;
475 }
476
expr_eval_push_string(const sc_char * value)477 static void expr_eval_push_string(const sc_char *value) {
478 sc_char *value_copy;
479
480 if (expr_eval_stack_index >= MAX_NESTING_DEPTH)
481 sc_fatal("expr_eval_push_string: stack overflow\n");
482
483 /* Push a copy of value. */
484 value_copy = (sc_char *)sc_malloc(strlen(value) + 1);
485 strcpy(value_copy, value);
486 expr_eval_stack[expr_eval_stack_index].is_collectible = TRUE;
487 expr_eval_stack[expr_eval_stack_index++].value.mutable_string = value_copy;
488 }
489
expr_eval_push_alloced_string(sc_char * value)490 static void expr_eval_push_alloced_string(sc_char *value) {
491 if (expr_eval_stack_index >= MAX_NESTING_DEPTH)
492 sc_fatal("expr_eval_push_alloced_string: stack overflow\n");
493
494 expr_eval_stack[expr_eval_stack_index].is_collectible = TRUE;
495 expr_eval_stack[expr_eval_stack_index++].value.mutable_string = value;
496 }
497
498
499 /*
500 * expr_eval_pop_integer()
501 * expr_eval_pop_string()
502 *
503 * Pop values off the values stack. Returned strings are malloc'ed copies,
504 * and the caller is responsible for freeing them.
505 */
expr_eval_pop_integer(void)506 static sc_int expr_eval_pop_integer(void) {
507 if (expr_eval_stack_index == 0)
508 sc_fatal("expr_eval_pop_integer: stack underflow\n");
509
510 assert(!expr_eval_stack[expr_eval_stack_index - 1].is_collectible);
511 return expr_eval_stack[--expr_eval_stack_index].value.integer;
512 }
513
expr_eval_pop_string(void)514 static sc_char *expr_eval_pop_string(void) {
515 if (expr_eval_stack_index == 0)
516 sc_fatal("expr_eval_pop_string: stack underflow\n");
517
518 /* Returns mutable string rather than const string. */
519 assert(expr_eval_stack[expr_eval_stack_index - 1].is_collectible);
520 return expr_eval_stack[--expr_eval_stack_index].value.mutable_string;
521 }
522
523
524 /*
525 * expr_eval_result()
526 *
527 * Return the top of the values stack as the expression result.
528 */
expr_eval_result(sc_vartype_t * vt_rvalue)529 static void expr_eval_result(sc_vartype_t *vt_rvalue) {
530 if (expr_eval_stack_index != 1)
531 sc_fatal("expr_eval_result: values stack not completed\n");
532
533 /* Clear down stack and return the top value. */
534 expr_eval_stack_index = 0;
535 *vt_rvalue = expr_eval_stack[0].value;
536 }
537
538
539 /*
540 * expr_eval_abs()
541 *
542 * Return the absolute value of the given sc_int. Replacement for labs(),
543 * avoids tying sc_int to long types too closely.
544 */
expr_eval_abs(sc_int value)545 static sc_int expr_eval_abs(sc_int value) {
546 return value < 0 ? -value : value;
547 }
548
549 /*
550 * expr_eval_action
551 *
552 * Evaluate the effect of a token into the values stack.
553 */
expr_eval_action(CONTEXT,sc_int token)554 static void expr_eval_action(CONTEXT, sc_int token) {
555 sc_vartype_t token_value;
556
557 switch (token) {
558 /* Handle tokens representing stack pushes. */
559 case TOK_INTEGER:
560 expr_current_token_value(&token_value);
561 expr_eval_push_integer(token_value.integer);
562 break;
563
564 case TOK_STRING:
565 expr_current_token_value(&token_value);
566 expr_eval_push_string(token_value.string);
567 break;
568
569 case TOK_VARIABLE: {
570 sc_vartype_t vt_rvalue;
571 sc_int type;
572
573 expr_current_token_value(&token_value);
574 if (!var_get(expr_varset, token_value.string, &type, &vt_rvalue)) {
575 sc_error("expr_eval_action:"
576 " undefined variable, %s\n", token_value.string);
577 LONG_JUMP;
578 }
579 switch (type) {
580 case VAR_INTEGER:
581 expr_eval_push_integer(vt_rvalue.integer);
582 break;
583
584 case VAR_STRING:
585 expr_eval_push_string(vt_rvalue.string);
586 break;
587
588 default:
589 sc_fatal("expr_eval_action: bad variable type\n");
590 }
591 break;
592 }
593
594 /* Handle tokens representing functions returning numeric. */
595 case TOK_IF: {
596 sc_int test, val1, val2;
597
598 /* Pop the test and alternatives, and push back result. */
599 val2 = expr_eval_pop_integer();
600 val1 = expr_eval_pop_integer();
601 test = expr_eval_pop_integer();
602 expr_eval_push_integer(test ? val1 : val2);
603 break;
604 }
605
606 case TOK_MAX:
607 case TOK_MIN: {
608 sc_int argument_count, index_, result;
609
610 /* Get argument count off the top of the stack. */
611 argument_count = expr_eval_pop_integer();
612 assert(argument_count > 0);
613
614 /* Find the max or min of these stacked values. */
615 result = expr_eval_pop_integer();
616 for (index_ = 1; index_ < argument_count; index_++) {
617 sc_int next;
618
619 next = expr_eval_pop_integer();
620 switch (token) {
621 case TOK_MAX:
622 result = (next > result) ? next : result;
623 break;
624
625 case TOK_MIN:
626 result = (next < result) ? next : result;
627 break;
628
629 default:
630 sc_fatal("expr_eval_action: bad token, %ld\n", token);
631 }
632 }
633
634 /* Push back the result. */
635 expr_eval_push_integer(result);
636 break;
637 }
638
639 case TOK_EITHER: {
640 sc_int argument_count, pick, index_;
641 sc_int result = 0;
642
643 /* Get argument count off the top of the stack. */
644 argument_count = expr_eval_pop_integer();
645 assert(argument_count > 0);
646
647 /*
648 * Pick one of the top N items at random, then unstack all N and
649 * push back the value of the one picked.
650 */
651 pick = sc_rand() % argument_count;
652 for (index_ = 0; index_ < argument_count; index_++) {
653 sc_int val;
654
655 val = expr_eval_pop_integer();
656 if (index_ == pick)
657 result = val;
658 }
659
660 /* Push back the result. */
661 expr_eval_push_integer(result);
662 break;
663 }
664
665 case TOK_INSTR: {
666 sc_char *val1, *val2, *search;
667 sc_int result;
668
669 /* Extract the two values to work on. */
670 val2 = expr_eval_pop_string();
671 val1 = expr_eval_pop_string();
672
673 /*
674 * Search for the second in the first. The result is the character
675 * position, starting at 1, or 0 if not found. Then free the popped
676 * strings, and push back the result.
677 */
678 search = (val1[0] != NUL) ? strstr(val1, val2) : NULL;
679 result = (!search) ? 0 : search - val1 + 1;
680 sc_free(val1);
681 sc_free(val2);
682 expr_eval_push_integer(result);
683 break;
684 }
685
686 case TOK_LEN: {
687 sc_char *val;
688 sc_int result;
689
690 /* Pop the top string, and push back its length. */
691 val = expr_eval_pop_string();
692 result = strlen(val);
693 sc_free(val);
694 expr_eval_push_integer(result);
695 break;
696 }
697
698 case TOK_VAL: {
699 sc_char *val;
700 sc_int result = 0;
701
702 /*
703 * Extract the string at stack top, and try to convert, returning
704 * zero if conversion fails. Free the popped string, and push back
705 * the result.
706 */
707 val = expr_eval_pop_string();
708 sscanf(val, "%ld", &result);
709 sc_free(val);
710 expr_eval_push_integer(result);
711 break;
712 }
713
714 /* Handle tokens representing unary numeric operations. */
715 case TOK_UMINUS:
716 expr_eval_push_integer(-(expr_eval_pop_integer()));
717 break;
718
719 case TOK_UPLUS:
720 break;
721
722 case TOK_ABS:
723 expr_eval_push_integer(expr_eval_abs(expr_eval_pop_integer()));
724 break;
725
726 /* Handle tokens representing most binary numeric operations. */
727 case TOK_ADD:
728 case TOK_SUBTRACT:
729 case TOK_MULTIPLY:
730 case TOK_AND:
731 case TOK_OR:
732 case TOK_EQUAL:
733 case TOK_GREATER:
734 case TOK_LESS:
735 case TOK_NOT_EQUAL:
736 case TOK_GREATER_EQ:
737 case TOK_LESS_EQ:
738 case TOK_RANDOM: {
739 sc_int val1, val2, result = 0;
740
741 /* Extract the two values to work on. */
742 val2 = expr_eval_pop_integer();
743 val1 = expr_eval_pop_integer();
744
745 /* Generate the result value. */
746 switch (token) {
747 case TOK_ADD:
748 result = val1 + val2;
749 break;
750 case TOK_SUBTRACT:
751 result = val1 - val2;
752 break;
753 case TOK_MULTIPLY:
754 result = val1 * val2;
755 break;
756 case TOK_AND:
757 result = val1 && val2;
758 break;
759 case TOK_OR:
760 result = val1 || val2;
761 break;
762 case TOK_EQUAL:
763 result = val1 == val2;
764 break;
765 case TOK_GREATER:
766 result = val1 > val2;
767 break;
768 case TOK_LESS:
769 result = val1 < val2;
770 break;
771 case TOK_NOT_EQUAL:
772 result = val1 != val2;
773 break;
774 case TOK_GREATER_EQ:
775 result = val1 >= val2;
776 break;
777 case TOK_LESS_EQ:
778 result = val1 <= val2;
779 break;
780 case TOK_RANDOM:
781 result = sc_randomint(val1, val2);
782 break;
783 default:
784 sc_fatal("expr_eval_action: bad token, %ld\n", token);
785 }
786
787 /* Put result back at top of stack. */
788 expr_eval_push_integer(result);
789 break;
790 }
791
792 /* Handle division and modulus separately; they're "eccentric". */
793 case TOK_DIVIDE:
794 case TOK_MOD: {
795 sc_int val1, val2, x, y, result = 0;
796
797 /* Extract the two values to work on, complain about division by 0. */
798 val2 = expr_eval_pop_integer();
799 val1 = expr_eval_pop_integer();
800 if (val2 == 0) {
801 sc_error("expr_eval_action: attempt to divide by zero\n");
802 expr_eval_push_integer(result);
803 break;
804 }
805
806 /*
807 * ANSI/ISO C only defines integer division for positive values.
808 * Negative values usually work consistently across platforms, but are
809 * not guaranteed. For maximum portability, then, here we'll work
810 * carefully with positive integers only.
811 */
812 x = expr_eval_abs(val1);
813 y = expr_eval_abs(val2);
814
815 /* Generate the result value. */
816 switch (token) {
817 case TOK_DIVIDE:
818 /*
819 * Adrift's division apparently works by dividing using floating
820 * point, then applying (asymmetrical) rounding, so we have to do
821 * the same here.
822 */
823 result = ((val1 < 0) == (val2 < 0))
824 ? ((x / y) + (((x % y) * 2 >= y) ? 1 : 0))
825 : -((x / y) + (((x % y) * 2 > y) ? 1 : 0));
826 break;
827
828 case TOK_MOD:
829 /*
830 * Adrift also breaks numerical consistency by defining mod in a
831 * conventional (non-rounded), way, so that A=(AdivB)*B+AmodB
832 * does not hold.
833 */
834 result = (val1 < 0) ? -(x % y) : (x % y);
835 break;
836
837 default:
838 sc_fatal("expr_eval_action: bad token, %ld\n", token);
839 }
840
841 /* Put result back at top of stack. */
842 expr_eval_push_integer(result);
843 break;
844 }
845
846 /* Handle power individually, to avoid needing a maths library. */
847 case TOK_POWER: {
848 sc_int val1, val2, result;
849
850 /* Extract the two values to work on. */
851 val2 = expr_eval_pop_integer();
852 val1 = expr_eval_pop_integer();
853
854 /* Handle negative and zero power values first, as special cases. */
855 if (val2 == 0)
856 result = 1;
857 else if (val2 < 0) {
858 if (val1 == 0) {
859 sc_error("expr_eval_action: attempt to divide by zero\n");
860 result = 0;
861 } else if (val1 == 1)
862 result = val1;
863 else if (val1 == -1)
864 result = (-val2 & 1) ? val1 : -val1;
865 else
866 result = 0;
867 } else {
868 /* Raise to positive powers using the Russian Peasant algorithm. */
869 while ((val2 & 1) == 0) {
870 val1 = val1 * val1;
871 val2 >>= 1;
872 }
873
874 result = val1;
875 val2 >>= 1;
876 while (val2 > 0) {
877 val1 = val1 * val1;
878 if (val2 & 1)
879 result = result * val1;
880 val2 >>= 1;
881 }
882 }
883
884 /* Put result back at top of stack. */
885 expr_eval_push_integer(result);
886 break;
887 }
888
889 /* Handle tokens representing functions returning string. */
890 case TOK_LEFT:
891 case TOK_RIGHT: {
892 sc_char *text;
893 sc_int length;
894
895 /*
896 * Extract the text and length. If length is longer than text, or
897 * negative, do nothing.
898 */
899 length = expr_eval_pop_integer();
900 text = expr_eval_pop_string();
901 if (length < 0 || length >= (sc_int) strlen(text)) {
902 expr_eval_push_alloced_string(text);
903 break;
904 }
905
906 /*
907 * Take the left or right segment -- for left, the operation is a
908 * simple truncation; for right, it's a memmove.
909 */
910 switch (token) {
911 case TOK_LEFT:
912 text[length] = NUL;
913 break;
914
915 case TOK_RIGHT:
916 memmove(text, text + strlen(text) - length, length + 1);
917 break;
918
919 default:
920 sc_fatal("expr_eval_action: bad token, %ld\n", token);
921 }
922
923 /* Put result back at top of stack. */
924 expr_eval_push_alloced_string(text);
925 break;
926 }
927
928 case TOK_MID: {
929 sc_char *text;
930 sc_int length, start, limit;
931
932 /*
933 * Extract the text, start, and length, re-basing start from 1 to 0,
934 * and calculate the limit on characters available for the move.
935 */
936 length = expr_eval_pop_integer();
937 start = expr_eval_pop_integer() - 1;
938 text = expr_eval_pop_string();
939 limit = strlen(text);
940
941 /*
942 * Clamp ranges that roam outside the available text -- start less
943 * than 0 to 0, and greater than len(text) to len(text), and length
944 * less than 0 to 0, and off string end to string end.
945 */
946 if (start < 0)
947 start = 0;
948 else if (start > limit)
949 start = limit;
950 if (length < 0)
951 length = 0;
952 else if (length > limit - start)
953 length = limit - start;
954
955 /* Move substring, terminate, and put back at top of stack. */
956 memmove(text, text + start, length + 1);
957 text[length] = NUL;
958 expr_eval_push_alloced_string(text);
959 break;
960 }
961
962 case TOK_STR: {
963 sc_int val;
964 sc_char buffer[32];
965
966 /*
967 * Extract the value, convert it, and push back the resulting string.
968 * The leading space on positive values matches the Runner.
969 */
970 val = expr_eval_pop_integer();
971 sprintf(buffer, "% ld", val);
972 expr_eval_push_string(buffer);
973 break;
974 }
975
976
977 /* Handle tokens representing unary string operations. */
978 case TOK_UPPER:
979 case TOK_LOWER:
980 case TOK_PROPER: {
981 sc_char *text;
982 sc_int index_;
983
984 /* Extract the value to work on. */
985 text = expr_eval_pop_string();
986
987 /* Convert the entire string in place -- it's malloc'ed. */
988 for (index_ = 0; text[index_] != NUL; index_++) {
989 switch (token) {
990 case TOK_UPPER:
991 text[index_] = sc_toupper(text[index_]);
992 break;
993
994 case TOK_LOWER:
995 text[index_] = sc_tolower(text[index_]);
996 break;
997
998 case TOK_PROPER:
999 if (index_ == 0 || sc_isspace(text[index_ - 1]))
1000 text[index_] = sc_toupper(text[index_]);
1001 else
1002 text[index_] = sc_tolower(text[index_]);
1003 break;
1004
1005 default:
1006 sc_fatal("expr_eval_action: bad token, %ld\n", token);
1007 }
1008 }
1009
1010 /* Put result back at top of stack. */
1011 expr_eval_push_alloced_string(text);
1012 break;
1013 }
1014
1015 /* Handle token representing binary string operation. */
1016 case TOK_CONCATENATE: {
1017 sc_char *text1, *text2;
1018
1019 /* Extract the two texts to work on. */
1020 text2 = expr_eval_pop_string();
1021 text1 = expr_eval_pop_string();
1022
1023 /*
1024 * Resize text1 to be long enough for both, and concatenate, then
1025 * free text2, and push back the concatenation.
1026 */
1027 text1 = (sc_char *)sc_realloc(text1, strlen(text1) + strlen(text2) + 1);
1028 strcat(text1, text2);
1029 sc_free(text2);
1030 expr_eval_push_alloced_string(text1);
1031 break;
1032 }
1033
1034 default:
1035 sc_fatal("expr_eval_action: bad token, %ld\n", token);
1036 }
1037 }
1038
1039
1040 /* Predictive parser lookahead token. */
1041 static sc_int expr_parse_lookahead = TOK_NONE;
1042
1043 /* Forward declaration of factor parsers and string expression parser. */
1044 static void expr_parse_numeric_factor(CONTEXT);
1045 static void expr_parse_string_factor(CONTEXT);
1046 static void expr_parse_string_expr(CONTEXT);
1047
1048 /*
1049 * expr_parse_match
1050 *
1051 * Match a token to the lookahead, then advance lookahead.
1052 */
expr_parse_match(CONTEXT,sc_int token)1053 static void expr_parse_match(CONTEXT, sc_int token) {
1054 if (expr_parse_lookahead == token)
1055 expr_parse_lookahead = expr_next_token();
1056 else {
1057 /* Syntax error. */
1058 sc_error("expr_parse_match: syntax error,"
1059 " expected %ld, got %ld\n", expr_parse_lookahead, token);
1060 LONG_JUMP;
1061 }
1062 }
1063
1064
1065 /*
1066 * Numeric operator precedence table. Operators are in order of precedence,
1067 * with the highest being a factor. Each precedence entry permits several
1068 * listed tokens. The end of the table (highest precedence) is marked by
1069 * a list with no operators (although in practice we need to put a TOK_NONE
1070 * in here since some C compilers won't accept { } as an empty initializer).
1071 */
1072 struct sc_precedence_entry_t {
1073 const sc_int token_count;
1074 const sc_int tokens[6];
1075 };
1076 #if 0
1077 /*
1078 * Conventional (BASIC, C) precedence table for the parser. Exponentiation
1079 * has the highest precedence, then multiplicative operations, additive,
1080 * comparisons, and boolean combiners.
1081 */
1082 static const sc_precedence_entry_t PRECEDENCE_TABLE[] = {
1083 {1, {TOK_OR}},
1084 {1, {TOK_AND}},
1085 {2, {TOK_EQUAL, TOK_NOT_EQUAL}},
1086 {4, {TOK_GREATER, TOK_LESS, TOK_GREATER_EQ, TOK_LESS_EQ}},
1087 {2, {TOK_ADD, TOK_SUBTRACT}},
1088 {3, {TOK_MULTIPLY, TOK_DIVIDE, TOK_MOD}},
1089 {1, {TOK_POWER}},
1090 {0, {TOK_NONE}}
1091 };
1092 #else
1093 /*
1094 * Adrift-like precedence table for the parser. Exponentiation and modulus
1095 * operations seem to be implemented at the same level as addition and
1096 * subtraction, and boolean 'and' and 'or' have equal precedence.
1097 */
1098 static const sc_precedence_entry_t PRECEDENCE_TABLE[] = {
1099 {2, {TOK_OR, TOK_AND}},
1100 {
1101 6, {
1102 TOK_EQUAL, TOK_NOT_EQUAL,
1103 TOK_GREATER, TOK_LESS, TOK_GREATER_EQ, TOK_LESS_EQ
1104 }
1105 },
1106 {4, {TOK_ADD, TOK_SUBTRACT, TOK_POWER, TOK_MOD}},
1107 {2, {TOK_MULTIPLY, TOK_DIVIDE}},
1108 {0, {TOK_NONE}}
1109 };
1110 #endif
1111
1112
1113 /*
1114 * expr_parse_contains_token()
1115 *
1116 * Helper for expr_parse_numeric_element(). Search the token list for the
1117 * entry passed in, and return TRUE if it contains the given token.
1118 */
expr_parse_contains_token(const sc_precedence_entry_t * entry,sc_int token)1119 static int expr_parse_contains_token(const sc_precedence_entry_t *entry, sc_int token) {
1120 sc_bool is_matched;
1121 sc_int index_;
1122
1123 /* Search the entry's token list for the token passed in. */
1124 is_matched = FALSE;
1125 for (index_ = 0; index_ < entry->token_count; index_++) {
1126 if (entry->tokens[index_] == token) {
1127 is_matched = TRUE;
1128 break;
1129 }
1130 }
1131
1132 return is_matched;
1133 }
1134
1135
1136 /*
1137 * expr_parse_numeric_element()
1138 *
1139 * Parse numeric expression elements. This function uses the precedence table
1140 * to match tokens, then decide whether, and how, to recurse into itself, or
1141 * whether to parse a highest-precedence factor.
1142 */
expr_parse_numeric_element(CONTEXT,sc_int precedence)1143 static void expr_parse_numeric_element(CONTEXT, sc_int precedence) {
1144 const sc_precedence_entry_t *entry;
1145
1146 /* See if the level passed in has listed tokens. */
1147 entry = PRECEDENCE_TABLE + precedence;
1148 if (entry->token_count == 0) {
1149 /* Precedence levels that hit the table end are factors. */
1150 CALL0(expr_parse_numeric_factor);
1151 return;
1152 }
1153
1154 /*
1155 * Parse initial higher-precedence factor, then others that associate
1156 * with the given level.
1157 */
1158 CALL1(expr_parse_numeric_element, precedence + 1);
1159 while (expr_parse_contains_token(entry, expr_parse_lookahead)) {
1160 sc_int token;
1161
1162 /* Note token and match, parse next level, then action this token. */
1163 token = expr_parse_lookahead;
1164 CALL1(expr_parse_match, token);
1165 CALL1(expr_parse_numeric_element, precedence + 1);
1166 CALL1(expr_eval_action, token);
1167 }
1168 }
1169
1170
1171 /*
1172 * expr_parse_numeric_expr
1173 *
1174 * Parse a complete numeric (sub-)expression.
1175 */
expr_parse_numeric_expr(CONTEXT)1176 static void expr_parse_numeric_expr(CONTEXT) {
1177 /* Call the parser of the lowest precedence operators. */
1178 CALL1(expr_parse_numeric_element, 0);
1179 }
1180
1181
1182 /*
1183 * expr_parse_numeric_factor()
1184 *
1185 * Parse a numeric expression factor.
1186 */
expr_parse_numeric_factor(CONTEXT)1187 static void expr_parse_numeric_factor(CONTEXT) {
1188 /* Handle factors based on lookahead token. */
1189 switch (expr_parse_lookahead) {
1190 /* Handle straightforward factors first. */
1191 case TOK_LPAREN:
1192 CALL1(expr_parse_match, TOK_LPAREN);
1193 CALL0(expr_parse_numeric_expr);
1194 CALL1(expr_parse_match, TOK_RPAREN);
1195 break;
1196
1197 case TOK_UMINUS:
1198 CALL1(expr_parse_match, TOK_UMINUS);
1199 CALL0(expr_parse_numeric_factor);
1200 CALL1(expr_eval_action, TOK_UMINUS);
1201 break;
1202
1203 case TOK_UPLUS:
1204 CALL1(expr_parse_match, TOK_UPLUS);
1205 CALL0(expr_parse_numeric_factor);
1206 break;
1207
1208 case TOK_INTEGER:
1209 CALL1(expr_eval_action, TOK_INTEGER);
1210 CALL1(expr_parse_match, TOK_INTEGER);
1211 break;
1212
1213 case TOK_VARIABLE: {
1214 sc_vartype_t token_value, vt_rvalue;
1215 sc_int type;
1216
1217 expr_current_token_value(&token_value);
1218 if (!var_get(expr_varset, token_value.string, &type, &vt_rvalue)) {
1219 sc_error("expr_parse_numeric_factor:"
1220 " undefined variable, %s\n", token_value.string);
1221 LONG_JUMP;
1222 }
1223 if (type != VAR_INTEGER) {
1224 sc_error("expr_parse_numeric_factor:"
1225 " string variable in numeric context, %s\n",
1226 token_value.string);
1227 LONG_JUMP;
1228 }
1229 CALL1(expr_eval_action, TOK_VARIABLE);
1230 CALL1(expr_parse_match, TOK_VARIABLE);
1231 break;
1232 }
1233
1234 /* Handle functions as factors. */
1235 case TOK_ABS:
1236 /* Parse as "abs (val)". */
1237 CALL1(expr_parse_match, TOK_ABS);
1238 CALL1(expr_parse_match, TOK_LPAREN);
1239 CALL0(expr_parse_numeric_expr);
1240 CALL1(expr_parse_match, TOK_RPAREN);
1241 CALL1(expr_eval_action, TOK_ABS);
1242 break;
1243
1244 case TOK_IF:
1245 /* Parse as "if (boolean, val1, val2)". */
1246 CALL1(expr_parse_match, TOK_IF);
1247 CALL1(expr_parse_match, TOK_LPAREN);
1248 CALL0(expr_parse_numeric_expr);
1249 CALL1(expr_parse_match, TOK_COMMA);
1250 CALL0(expr_parse_numeric_expr);
1251 CALL1(expr_parse_match, TOK_COMMA);
1252 CALL0(expr_parse_numeric_expr);
1253 CALL1(expr_parse_match, TOK_RPAREN);
1254 CALL1(expr_eval_action, TOK_IF);
1255 break;
1256
1257 case TOK_RANDOM:
1258 /* Parse as "random (low, high)". */
1259 CALL1(expr_parse_match, TOK_RANDOM);
1260 CALL1(expr_parse_match, TOK_LPAREN);
1261 CALL0(expr_parse_numeric_expr);
1262 CALL1(expr_parse_match, TOK_COMMA);
1263 CALL0(expr_parse_numeric_expr);
1264 CALL1(expr_parse_match, TOK_RPAREN);
1265 CALL1(expr_eval_action, TOK_RANDOM);
1266 break;
1267
1268 case TOK_MAX:
1269 case TOK_MIN:
1270 case TOK_EITHER:
1271 /* Parse as "<func> (val1[,val2[,val3...]]])". */
1272 {
1273 sc_int token, argument_count;
1274
1275 /* Match up the function name and opening parenthesis. */
1276 token = expr_parse_lookahead;
1277 CALL1(expr_parse_match, token);
1278 CALL1(expr_parse_match, TOK_LPAREN);
1279
1280 /* Count variable number of arguments as they are stacked. */
1281 CALL0(expr_parse_numeric_expr);
1282 argument_count = 1;
1283 while (expr_parse_lookahead == TOK_COMMA) {
1284 CALL1(expr_parse_match, TOK_COMMA);
1285 CALL0(expr_parse_numeric_expr);
1286 argument_count++;
1287 }
1288 CALL1(expr_parse_match, TOK_RPAREN);
1289
1290 /* Push additional value -- the count of arguments. */
1291 expr_eval_push_integer(argument_count);
1292 CALL1(expr_eval_action, token);
1293 break;
1294 }
1295
1296 case TOK_INSTR:
1297 /* Parse as "instr (val1, val2)". */
1298 CALL1(expr_parse_match, TOK_INSTR);
1299 CALL1(expr_parse_match, TOK_LPAREN);
1300 CALL0(expr_parse_string_expr);
1301 CALL1(expr_parse_match, TOK_COMMA);
1302 CALL0(expr_parse_string_expr);
1303 CALL1(expr_parse_match, TOK_RPAREN);
1304 CALL1(expr_eval_action, TOK_INSTR);
1305 break;
1306
1307 case TOK_LEN:
1308 /* Parse as "len (val)". */
1309 CALL1(expr_parse_match, TOK_LEN);
1310 CALL1(expr_parse_match, TOK_LPAREN);
1311 CALL0(expr_parse_string_expr);
1312 CALL1(expr_parse_match, TOK_RPAREN);
1313 CALL1(expr_eval_action, TOK_LEN);
1314 break;
1315
1316 case TOK_VAL:
1317 /* Parse as "val (val)". */
1318 CALL1(expr_parse_match, TOK_VAL);
1319 CALL1(expr_parse_match, TOK_LPAREN);
1320 CALL0(expr_parse_string_expr);
1321 CALL1(expr_parse_match, TOK_RPAREN);
1322 CALL1(expr_eval_action, TOK_VAL);
1323 break;
1324
1325 case TOK_IDENT:
1326 /* Unrecognized function-type token. */
1327 sc_error("expr_parse_numeric_factor: syntax error, unknown ident\n");
1328 LONG_JUMP;
1329
1330 default:
1331 /* Syntax error. */
1332 sc_error("expr_parse_numeric_factor:"
1333 " syntax error, unexpected token, %ld\n", expr_parse_lookahead);
1334 LONG_JUMP;
1335 }
1336 }
1337
1338
1339 /*
1340 * expr_parse_string_expr()
1341 *
1342 * Parse a complete string (sub-)expression.
1343 */
expr_parse_string_expr(CONTEXT)1344 static void expr_parse_string_expr(CONTEXT) {
1345 /*
1346 * Parse a string factor, then all repeated concatenations. Because the '+'
1347 * and '&' are context sensitive, we have to invent/translate them into the
1348 * otherwise unused TOK_CONCATENATE for evaluation.
1349 */
1350 CALL0(expr_parse_string_factor);
1351 while (expr_parse_lookahead == TOK_AND || expr_parse_lookahead == TOK_ADD) {
1352 CALL1(expr_parse_match, expr_parse_lookahead);
1353 CALL0(expr_parse_string_factor);
1354 CALL1(expr_eval_action, TOK_CONCATENATE);
1355 }
1356 }
1357
1358
1359 /*
1360 * expr_parse_string_factor()
1361 *
1362 * Parse a string expression factor.
1363 */
expr_parse_string_factor(CONTEXT)1364 static void expr_parse_string_factor(CONTEXT) {
1365 /* Handle factors based on lookahead token. */
1366 switch (expr_parse_lookahead) {
1367 /* Handle straightforward factors first. */
1368 case TOK_LPAREN:
1369 CALL1(expr_parse_match, TOK_LPAREN);
1370 CALL0(expr_parse_string_expr);
1371 CALL1(expr_parse_match, TOK_RPAREN);
1372 break;
1373
1374 case TOK_STRING:
1375 CALL1(expr_eval_action, TOK_STRING);
1376 CALL1(expr_parse_match, TOK_STRING);
1377 break;
1378
1379 case TOK_VARIABLE: {
1380 sc_vartype_t token_value, vt_rvalue;
1381 sc_int type;
1382
1383 expr_current_token_value(&token_value);
1384 if (!var_get(expr_varset, token_value.string, &type, &vt_rvalue)) {
1385 sc_error("expr_parse_string_factor:"
1386 " undefined variable, %s\n", token_value.string);
1387 LONG_JUMP;
1388 }
1389 if (type != VAR_STRING) {
1390 sc_error("expr_parse_string_factor:"
1391 " numeric variable in string context, %s\n",
1392 token_value.string);
1393 LONG_JUMP;
1394 }
1395 CALL1(expr_eval_action, TOK_VARIABLE);
1396 CALL1(expr_parse_match, TOK_VARIABLE);
1397 break;
1398 }
1399
1400 /* Handle functions as factors. */
1401 case TOK_UPPER:
1402 case TOK_LOWER:
1403 case TOK_PROPER:
1404 /* Parse as "<func> (text)". */
1405 {
1406 sc_int token;
1407
1408 token = expr_parse_lookahead;
1409 CALL1(expr_parse_match, token);
1410 CALL1(expr_parse_match, TOK_LPAREN);
1411 CALL0(expr_parse_string_expr);
1412 CALL1(expr_parse_match, TOK_RPAREN);
1413 CALL1(expr_eval_action, token);
1414 break;
1415 }
1416
1417 case TOK_LEFT:
1418 case TOK_RIGHT:
1419 /* Parse as "<func> (text,length)". */
1420 {
1421 sc_int token;
1422
1423 token = expr_parse_lookahead;
1424 CALL1(expr_parse_match, token);
1425 CALL1(expr_parse_match, TOK_LPAREN);
1426 CALL0(expr_parse_string_expr);
1427 CALL1(expr_parse_match, TOK_COMMA);
1428 CALL0(expr_parse_numeric_expr);
1429 CALL1(expr_parse_match, TOK_RPAREN);
1430 CALL1(expr_eval_action, token);
1431 break;
1432 }
1433
1434 case TOK_MID:
1435 /* Parse as "mid (text,start,length)". */
1436 CALL1(expr_parse_match, TOK_MID);
1437 CALL1(expr_parse_match, TOK_LPAREN);
1438 CALL0(expr_parse_string_expr);
1439 CALL1(expr_parse_match, TOK_COMMA);
1440 CALL0(expr_parse_numeric_expr);
1441 CALL1(expr_parse_match, TOK_COMMA);
1442 CALL0(expr_parse_numeric_expr);
1443 CALL1(expr_parse_match, TOK_RPAREN);
1444 CALL1(expr_eval_action, TOK_MID);
1445 break;
1446
1447 case TOK_STR:
1448 /* Parse as "str (val)". */
1449 CALL1(expr_parse_match, TOK_STR);
1450 CALL1(expr_parse_match, TOK_LPAREN);
1451 CALL0(expr_parse_numeric_expr);
1452 CALL1(expr_parse_match, TOK_RPAREN);
1453 CALL1(expr_eval_action, TOK_STR);
1454 break;
1455
1456 case TOK_IDENT:
1457 /* Unrecognized function-type token. */
1458 sc_error("expr_parse_string_factor: syntax error, unknown ident\n");
1459 LONG_JUMP;
1460
1461 default:
1462 /* Syntax error. */
1463 sc_error("expr_parse_string_factor:"
1464 " syntax error, unexpected token, %ld\n", expr_parse_lookahead);
1465 LONG_JUMP;
1466 }
1467 }
1468
1469
1470 /*
1471 * expr_evaluate_expression()
1472 *
1473 * Parse a string expression into a runtime values stack. Return the
1474 * value of the expression.
1475 */
expr_evaluate_expression(const sc_char * expression,sc_var_setref_t vars,sc_int assign_type,sc_vartype_t * vt_rvalue)1476 static sc_bool expr_evaluate_expression(const sc_char *expression, sc_var_setref_t vars,
1477 sc_int assign_type, sc_vartype_t *vt_rvalue) {
1478 assert(assign_type == VAR_INTEGER || assign_type == VAR_STRING);
1479 Context context;
1480
1481 /* Reset values stack and start tokenizer. */
1482 expr_eval_start(vars);
1483 expr_tokenize_start(expression);
1484
1485 // Try parsing an expression, and ensure it ends at string end. */
1486 expr_parse_lookahead = expr_next_token();
1487 if (assign_type == VAR_STRING)
1488 expr_parse_string_expr(context);
1489 else
1490 expr_parse_numeric_expr(context);
1491 if (!context._break)
1492 expr_parse_match(context, TOK_EOS);
1493
1494 if (context._break) {
1495 /* Parse error -- clean up tokenizer, collect garbage, and fail. */
1496 expr_tokenize_end();
1497 expr_eval_garbage_collect();
1498 return FALSE;
1499 }
1500
1501 /* Clean up tokenizer and return successfully with result. */
1502 expr_tokenize_end();
1503 expr_eval_result(vt_rvalue);
1504 return TRUE;
1505 }
1506
1507
1508 /*
1509 * expr_eval_numeric_expression()
1510 * expr_eval_string_expression()
1511 *
1512 * Public interfaces to expression evaluation. Evaluate an expression, and
1513 * assign the result to either a numeric or a string. For string expressions,
1514 * the return value is malloc'ed, and the caller is responsible for freeing
1515 * it.
1516 */
expr_eval_numeric_expression(const sc_char * expression,sc_var_setref_t vars,sc_int * rvalue)1517 sc_bool expr_eval_numeric_expression(const sc_char *expression, sc_var_setref_t vars, sc_int *rvalue) {
1518 sc_vartype_t vt_rvalue;
1519 sc_bool status;
1520 assert(expression && vars && rvalue);
1521
1522 /* Evaluate numeric expression, and return value if valid. */
1523 status = expr_evaluate_expression(expression, vars, VAR_INTEGER, &vt_rvalue);
1524 if (status)
1525 *rvalue = vt_rvalue.integer;
1526 return status;
1527 }
1528
expr_eval_string_expression(const sc_char * expression,sc_var_setref_t vars,sc_char ** rvalue)1529 sc_bool expr_eval_string_expression(const sc_char *expression, sc_var_setref_t vars, sc_char **rvalue) {
1530 sc_vartype_t vt_rvalue;
1531 sc_bool status;
1532 assert(expression && vars && rvalue);
1533
1534 /* Evaluate string expression, and return value if valid. */
1535 status = expr_evaluate_expression(expression, vars, VAR_STRING, &vt_rvalue);
1536 if (status)
1537 *rvalue = vt_rvalue.mutable_string;
1538 return status;
1539 }
1540
1541 } // End of namespace Adrift
1542 } // End of namespace Glk
1543