Austin Schuh | bb1338c | 2024-06-15 19:31:16 -0700 | [diff] [blame^] | 1 | /* mpexpr_evaluate -- shared code for simple expression evaluation |
| 2 | |
| 3 | Copyright 2000-2002, 2004 Free Software Foundation, Inc. |
| 4 | |
| 5 | This file is part of the GNU MP Library. |
| 6 | |
| 7 | The GNU MP Library is free software; you can redistribute it and/or modify |
| 8 | it under the terms of either: |
| 9 | |
| 10 | * the GNU Lesser General Public License as published by the Free |
| 11 | Software Foundation; either version 3 of the License, or (at your |
| 12 | option) any later version. |
| 13 | |
| 14 | or |
| 15 | |
| 16 | * the GNU General Public License as published by the Free Software |
| 17 | Foundation; either version 2 of the License, or (at your option) any |
| 18 | later version. |
| 19 | |
| 20 | or both in parallel, as here. |
| 21 | |
| 22 | The GNU MP Library is distributed in the hope that it will be useful, but |
| 23 | WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
| 24 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 25 | for more details. |
| 26 | |
| 27 | You should have received copies of the GNU General Public License and the |
| 28 | GNU Lesser General Public License along with the GNU MP Library. If not, |
| 29 | see https://www.gnu.org/licenses/. */ |
| 30 | |
| 31 | #include <ctype.h> |
| 32 | #include <stdio.h> |
| 33 | #include <string.h> |
| 34 | |
| 35 | #include "gmp.h" |
| 36 | #include "expr-impl.h" |
| 37 | |
| 38 | |
| 39 | /* Change this to "#define TRACE(x) x" to get some traces. The trace |
| 40 | printfs junk up the code a bit, but it's very hard to tell what's going |
| 41 | on without them. Set MPX_TRACE to a suitable output function for the |
| 42 | mpz/mpq/mpf being run (if you have the wrong trace function it'll |
| 43 | probably segv). */ |
| 44 | |
| 45 | #define TRACE(x) |
| 46 | #define MPX_TRACE mpz_trace |
| 47 | |
| 48 | |
| 49 | /* A few helper macros copied from gmp-impl.h */ |
| 50 | #define ALLOCATE_FUNC_TYPE(n,type) \ |
| 51 | ((type *) (*allocate_func) ((n) * sizeof (type))) |
| 52 | #define ALLOCATE_FUNC_LIMBS(n) ALLOCATE_FUNC_TYPE (n, mp_limb_t) |
| 53 | #define REALLOCATE_FUNC_TYPE(p, old_size, new_size, type) \ |
| 54 | ((type *) (*reallocate_func) \ |
| 55 | (p, (old_size) * sizeof (type), (new_size) * sizeof (type))) |
| 56 | #define REALLOCATE_FUNC_LIMBS(p, old_size, new_size) \ |
| 57 | REALLOCATE_FUNC_TYPE(p, old_size, new_size, mp_limb_t) |
| 58 | #define FREE_FUNC_TYPE(p,n,type) (*free_func) (p, (n) * sizeof (type)) |
| 59 | #define FREE_FUNC_LIMBS(p,n) FREE_FUNC_TYPE (p, n, mp_limb_t) |
| 60 | #define ASSERT(x) |
| 61 | |
| 62 | |
| 63 | |
| 64 | /* All the error strings are just for diagnostic traces. Only the error |
| 65 | code is actually returned. */ |
| 66 | #define ERROR(str,code) \ |
| 67 | { \ |
| 68 | TRACE (printf ("%s\n", str)); \ |
| 69 | p->error_code = (code); \ |
| 70 | goto done; \ |
| 71 | } |
| 72 | |
| 73 | |
| 74 | #define REALLOC(ptr, alloc, incr, type) \ |
| 75 | do { \ |
| 76 | int new_alloc = (alloc) + (incr); \ |
| 77 | ptr = REALLOCATE_FUNC_TYPE (ptr, alloc, new_alloc, type); \ |
| 78 | (alloc) = new_alloc; \ |
| 79 | } while (0) |
| 80 | |
| 81 | |
| 82 | /* data stack top element */ |
| 83 | #define SP (p->data_stack + p->data_top) |
| 84 | |
| 85 | /* Make sure there's room for another data element above current top. |
| 86 | reallocate_func is fetched for when this macro is used in lookahead(). */ |
| 87 | #define DATA_SPACE() \ |
| 88 | do { \ |
| 89 | if (p->data_top + 1 >= p->data_alloc) \ |
| 90 | { \ |
| 91 | void *(*reallocate_func) (void *, size_t, size_t); \ |
| 92 | mp_get_memory_functions (NULL, &reallocate_func, NULL); \ |
| 93 | TRACE (printf ("grow stack from %d\n", p->data_alloc)); \ |
| 94 | REALLOC (p->data_stack, p->data_alloc, 20, union mpX_t); \ |
| 95 | } \ |
| 96 | ASSERT (p->data_top + 1 <= p->data_inited); \ |
| 97 | if (p->data_top + 1 == p->data_inited) \ |
| 98 | { \ |
| 99 | TRACE (printf ("initialize %d\n", p->data_top + 1)); \ |
| 100 | (*p->mpX_init) (&p->data_stack[p->data_top + 1], p->prec); \ |
| 101 | p->data_inited++; \ |
| 102 | } \ |
| 103 | } while (0) |
| 104 | |
| 105 | #define DATA_PUSH() \ |
| 106 | do { \ |
| 107 | p->data_top++; \ |
| 108 | ASSERT (p->data_top < p->data_alloc); \ |
| 109 | ASSERT (p->data_top < p->data_inited); \ |
| 110 | } while (0) |
| 111 | |
| 112 | /* the last stack entry is never popped, so top>=0 will be true */ |
| 113 | #define DATA_POP(n) \ |
| 114 | do { \ |
| 115 | p->data_top -= (n); \ |
| 116 | ASSERT (p->data_top >= 0); \ |
| 117 | } while (0) |
| 118 | |
| 119 | |
| 120 | /* lookahead() parses the next token. Return 1 if successful, with some |
| 121 | extra data. Return 0 if fail, with reason in p->error_code. |
| 122 | |
| 123 | "prefix" is MPEXPR_TYPE_PREFIX if an operator with that attribute is |
| 124 | preferred, or 0 if an operator without is preferred. */ |
| 125 | |
| 126 | #define TOKEN_EOF -1 /* no extra data */ |
| 127 | #define TOKEN_VALUE -2 /* pushed onto data stack */ |
| 128 | #define TOKEN_OPERATOR -3 /* stored in p->token_op */ |
| 129 | #define TOKEN_FUNCTION -4 /* stored in p->token_op */ |
| 130 | |
| 131 | #define TOKEN_NAME(n) \ |
| 132 | ((n) == TOKEN_EOF ? "TOKEN_EOF" \ |
| 133 | : (n) == TOKEN_VALUE ? "TOKEN_VALUE" \ |
| 134 | : (n) == TOKEN_OPERATOR ? "TOKEN_OPERATOR" \ |
| 135 | : (n) == TOKEN_VALUE ? "TOKEN_FUNCTION" \ |
| 136 | : "UNKNOWN TOKEN") |
| 137 | |
| 138 | /* Functions default to being parsed as whole words, operators to match just |
| 139 | at the start of the string. The type flags override this. */ |
| 140 | #define WHOLEWORD(op) \ |
| 141 | (op->precedence == 0 \ |
| 142 | ? (! (op->type & MPEXPR_TYPE_OPERATOR)) \ |
| 143 | : (op->type & MPEXPR_TYPE_WHOLEWORD)) |
| 144 | |
| 145 | #define isasciispace(c) (isascii (c) && isspace (c)) |
| 146 | |
| 147 | static int |
| 148 | lookahead (struct mpexpr_parse_t *p, int prefix) |
| 149 | { |
| 150 | const struct mpexpr_operator_t *op, *op_found; |
| 151 | size_t oplen, oplen_found, wlen; |
| 152 | int i; |
| 153 | |
| 154 | /* skip white space */ |
| 155 | while (p->elen > 0 && isasciispace (*p->e)) |
| 156 | p->e++, p->elen--; |
| 157 | |
| 158 | if (p->elen == 0) |
| 159 | { |
| 160 | TRACE (printf ("lookahead EOF\n")); |
| 161 | p->token = TOKEN_EOF; |
| 162 | return 1; |
| 163 | } |
| 164 | |
| 165 | DATA_SPACE (); |
| 166 | |
| 167 | /* Get extent of whole word. */ |
| 168 | for (wlen = 0; wlen < p->elen; wlen++) |
| 169 | if (! isasciicsym (p->e[wlen])) |
| 170 | break; |
| 171 | |
| 172 | TRACE (printf ("lookahead at: \"%.*s\" length %u, word %u\n", |
| 173 | (int) p->elen, p->e, p->elen, wlen)); |
| 174 | |
| 175 | op_found = NULL; |
| 176 | oplen_found = 0; |
| 177 | for (op = p->table; op->name != NULL; op++) |
| 178 | { |
| 179 | if (op->type == MPEXPR_TYPE_NEW_TABLE) |
| 180 | { |
| 181 | printf ("new\n"); |
| 182 | op = (struct mpexpr_operator_t *) op->name - 1; |
| 183 | continue; |
| 184 | } |
| 185 | |
| 186 | oplen = strlen (op->name); |
| 187 | if (! ((WHOLEWORD (op) ? wlen == oplen : p->elen >= oplen) |
| 188 | && memcmp (p->e, op->name, oplen) == 0)) |
| 189 | continue; |
| 190 | |
| 191 | /* Shorter matches don't replace longer previous ones. */ |
| 192 | if (op_found && oplen < oplen_found) |
| 193 | continue; |
| 194 | |
| 195 | /* On a match of equal length to a previous one, the old match isn't |
| 196 | replaced if it has the preferred prefix, and if it doesn't then |
| 197 | it's not replaced if the new one also doesn't. */ |
| 198 | if (op_found && oplen == oplen_found |
| 199 | && ((op_found->type & MPEXPR_TYPE_PREFIX) == prefix |
| 200 | || (op->type & MPEXPR_TYPE_PREFIX) != prefix)) |
| 201 | continue; |
| 202 | |
| 203 | /* This is now either the first match seen, or a longer than previous |
| 204 | match, or an equal to previous one but with a preferred prefix. */ |
| 205 | op_found = op; |
| 206 | oplen_found = oplen; |
| 207 | } |
| 208 | |
| 209 | if (op_found) |
| 210 | { |
| 211 | p->e += oplen_found, p->elen -= oplen_found; |
| 212 | |
| 213 | if (op_found->type == MPEXPR_TYPE_VARIABLE) |
| 214 | { |
| 215 | if (p->elen == 0) |
| 216 | ERROR ("end of string expecting a variable", |
| 217 | MPEXPR_RESULT_PARSE_ERROR); |
| 218 | i = p->e[0] - 'a'; |
| 219 | if (i < 0 || i >= MPEXPR_VARIABLES) |
| 220 | ERROR ("bad variable name", MPEXPR_RESULT_BAD_VARIABLE); |
| 221 | goto variable; |
| 222 | } |
| 223 | |
| 224 | if (op_found->precedence == 0) |
| 225 | { |
| 226 | TRACE (printf ("lookahead function: %s\n", op_found->name)); |
| 227 | p->token = TOKEN_FUNCTION; |
| 228 | p->token_op = op_found; |
| 229 | return 1; |
| 230 | } |
| 231 | else |
| 232 | { |
| 233 | TRACE (printf ("lookahead operator: %s\n", op_found->name)); |
| 234 | p->token = TOKEN_OPERATOR; |
| 235 | p->token_op = op_found; |
| 236 | return 1; |
| 237 | } |
| 238 | } |
| 239 | |
| 240 | oplen = (*p->mpX_number) (SP+1, p->e, p->elen, p->base); |
| 241 | if (oplen != 0) |
| 242 | { |
| 243 | p->e += oplen, p->elen -= oplen; |
| 244 | p->token = TOKEN_VALUE; |
| 245 | DATA_PUSH (); |
| 246 | TRACE (MPX_TRACE ("lookahead number", SP)); |
| 247 | return 1; |
| 248 | } |
| 249 | |
| 250 | /* Maybe an unprefixed one character variable */ |
| 251 | i = p->e[0] - 'a'; |
| 252 | if (wlen == 1 && i >= 0 && i < MPEXPR_VARIABLES) |
| 253 | { |
| 254 | variable: |
| 255 | p->e++, p->elen--; |
| 256 | if (p->var[i] == NULL) |
| 257 | ERROR ("NULL variable", MPEXPR_RESULT_BAD_VARIABLE); |
| 258 | TRACE (printf ("lookahead variable: var[%d] = ", i); |
| 259 | MPX_TRACE ("", p->var[i])); |
| 260 | p->token = TOKEN_VALUE; |
| 261 | DATA_PUSH (); |
| 262 | (*p->mpX_set) (SP, p->var[i]); |
| 263 | return 1; |
| 264 | } |
| 265 | |
| 266 | ERROR ("no token matched", MPEXPR_RESULT_PARSE_ERROR); |
| 267 | |
| 268 | done: |
| 269 | return 0; |
| 270 | } |
| 271 | |
| 272 | |
| 273 | /* control stack current top element */ |
| 274 | #define CP (p->control_stack + p->control_top) |
| 275 | |
| 276 | /* make sure there's room for another control element above current top */ |
| 277 | #define CONTROL_SPACE() \ |
| 278 | do { \ |
| 279 | if (p->control_top + 1 >= p->control_alloc) \ |
| 280 | { \ |
| 281 | TRACE (printf ("grow control stack from %d\n", p->control_alloc)); \ |
| 282 | REALLOC (p->control_stack, p->control_alloc, 20, \ |
| 283 | struct mpexpr_control_t); \ |
| 284 | } \ |
| 285 | } while (0) |
| 286 | |
| 287 | /* Push an operator on the control stack, claiming currently to have the |
| 288 | given number of args ready. Local variable "op" is used in case opptr is |
| 289 | a reference through CP. */ |
| 290 | #define CONTROL_PUSH(opptr,args) \ |
| 291 | do { \ |
| 292 | const struct mpexpr_operator_t *op = opptr; \ |
| 293 | struct mpexpr_control_t *cp; \ |
| 294 | CONTROL_SPACE (); \ |
| 295 | p->control_top++; \ |
| 296 | ASSERT (p->control_top < p->control_alloc); \ |
| 297 | cp = CP; \ |
| 298 | cp->op = op; \ |
| 299 | cp->argcount = (args); \ |
| 300 | TRACE_CONTROL("control stack push:"); \ |
| 301 | } while (0) |
| 302 | |
| 303 | /* The special operator_done is never popped, so top>=0 will hold. */ |
| 304 | #define CONTROL_POP() \ |
| 305 | do { \ |
| 306 | p->control_top--; \ |
| 307 | ASSERT (p->control_top >= 0); \ |
| 308 | TRACE_CONTROL ("control stack pop:"); \ |
| 309 | } while (0) |
| 310 | |
| 311 | #define TRACE_CONTROL(str) \ |
| 312 | TRACE ({ \ |
| 313 | int i; \ |
| 314 | printf ("%s depth %d:", str, p->control_top); \ |
| 315 | for (i = 0; i <= p->control_top; i++) \ |
| 316 | printf (" \"%s\"(%d)", \ |
| 317 | p->control_stack[i].op->name, \ |
| 318 | p->control_stack[i].argcount); \ |
| 319 | printf ("\n"); \ |
| 320 | }); |
| 321 | |
| 322 | |
| 323 | #define LOOKAHEAD(prefix) \ |
| 324 | do { \ |
| 325 | if (! lookahead (p, prefix)) \ |
| 326 | goto done; \ |
| 327 | } while (0) |
| 328 | |
| 329 | #define CHECK_UI(n) \ |
| 330 | do { \ |
| 331 | if (! (*p->mpX_ulong_p) (n)) \ |
| 332 | ERROR ("operand doesn't fit ulong", MPEXPR_RESULT_NOT_UI); \ |
| 333 | } while (0) |
| 334 | |
| 335 | #define CHECK_ARGCOUNT(str,n) \ |
| 336 | do { \ |
| 337 | if (CP->argcount != (n)) \ |
| 338 | { \ |
| 339 | TRACE (printf ("wrong number of arguments for %s, got %d want %d", \ |
| 340 | str, CP->argcount, n)); \ |
| 341 | ERROR ("", MPEXPR_RESULT_PARSE_ERROR); \ |
| 342 | } \ |
| 343 | } while (0) |
| 344 | |
| 345 | |
| 346 | /* There's two basic states here. In both p->token is the next token. |
| 347 | |
| 348 | "another_expr" is when a whole expression should be parsed. This means a |
| 349 | literal or variable value possibly followed by an operator, or a function |
| 350 | or prefix operator followed by a further whole expression. |
| 351 | |
| 352 | "another_operator" is when an expression has been parsed and its value is |
| 353 | on the top of the data stack (SP) and an optional further postfix or |
| 354 | infix operator should be parsed. |
| 355 | |
| 356 | In "another_operator" precedences determine whether to push the operator |
| 357 | onto the control stack, or instead go to "apply_control" to reduce the |
| 358 | operator currently on top of the control stack. |
| 359 | |
| 360 | When an operator has both a prefix and postfix/infix form, a LOOKAHEAD() |
| 361 | for "another_expr" will seek the prefix form, a LOOKAHEAD() for |
| 362 | "another_operator" will seek the postfix/infix form. The grammar is |
| 363 | simple enough that the next state is known before reading the next token. |
| 364 | |
| 365 | Argument count checking guards against functions consuming the wrong |
| 366 | number of operands from the data stack. The same checks are applied to |
| 367 | operators, but will always pass since a UNARY or BINARY will only ever |
| 368 | parse with the correct operands. */ |
| 369 | |
| 370 | int |
| 371 | mpexpr_evaluate (struct mpexpr_parse_t *p) |
| 372 | { |
| 373 | void *(*allocate_func) (size_t); |
| 374 | void *(*reallocate_func) (void *, size_t, size_t); |
| 375 | void (*free_func) (void *, size_t); |
| 376 | |
| 377 | mp_get_memory_functions (&allocate_func, &reallocate_func, &free_func); |
| 378 | |
| 379 | TRACE (printf ("mpexpr_evaluate() base %d \"%.*s\"\n", |
| 380 | p->base, (int) p->elen, p->e)); |
| 381 | |
| 382 | /* "done" is a special sentinel at the bottom of the control stack, |
| 383 | precedence -1 is lower than any normal operator. */ |
| 384 | { |
| 385 | static const struct mpexpr_operator_t operator_done |
| 386 | = { "DONE", NULL, MPEXPR_TYPE_DONE, -1 }; |
| 387 | |
| 388 | p->control_alloc = 20; |
| 389 | p->control_stack = ALLOCATE_FUNC_TYPE (p->control_alloc, |
| 390 | struct mpexpr_control_t); |
| 391 | p->control_top = 0; |
| 392 | CP->op = &operator_done; |
| 393 | CP->argcount = 1; |
| 394 | } |
| 395 | |
| 396 | p->data_inited = 0; |
| 397 | p->data_alloc = 20; |
| 398 | p->data_stack = ALLOCATE_FUNC_TYPE (p->data_alloc, union mpX_t); |
| 399 | p->data_top = -1; |
| 400 | |
| 401 | p->error_code = MPEXPR_RESULT_OK; |
| 402 | |
| 403 | |
| 404 | another_expr_lookahead: |
| 405 | LOOKAHEAD (MPEXPR_TYPE_PREFIX); |
| 406 | TRACE (printf ("another expr\n")); |
| 407 | |
| 408 | /*another_expr:*/ |
| 409 | switch (p->token) { |
| 410 | case TOKEN_VALUE: |
| 411 | goto another_operator_lookahead; |
| 412 | |
| 413 | case TOKEN_OPERATOR: |
| 414 | TRACE (printf ("operator %s\n", p->token_op->name)); |
| 415 | if (! (p->token_op->type & MPEXPR_TYPE_PREFIX)) |
| 416 | ERROR ("expected a prefix operator", MPEXPR_RESULT_PARSE_ERROR); |
| 417 | |
| 418 | CONTROL_PUSH (p->token_op, 1); |
| 419 | goto another_expr_lookahead; |
| 420 | |
| 421 | case TOKEN_FUNCTION: |
| 422 | CONTROL_PUSH (p->token_op, 1); |
| 423 | |
| 424 | if (p->token_op->type & MPEXPR_TYPE_CONSTANT) |
| 425 | goto apply_control_lookahead; |
| 426 | |
| 427 | LOOKAHEAD (MPEXPR_TYPE_PREFIX); |
| 428 | if (! (p->token == TOKEN_OPERATOR |
| 429 | && p->token_op->type == MPEXPR_TYPE_OPENPAREN)) |
| 430 | ERROR ("expected open paren for function", MPEXPR_RESULT_PARSE_ERROR); |
| 431 | |
| 432 | TRACE (printf ("open paren for function \"%s\"\n", CP->op->name)); |
| 433 | |
| 434 | if ((CP->op->type & MPEXPR_TYPE_MASK_ARGCOUNT) == MPEXPR_TYPE_NARY(0)) |
| 435 | { |
| 436 | LOOKAHEAD (0); |
| 437 | if (! (p->token == TOKEN_OPERATOR |
| 438 | && p->token_op->type == MPEXPR_TYPE_CLOSEPAREN)) |
| 439 | ERROR ("expected close paren for 0ary function", |
| 440 | MPEXPR_RESULT_PARSE_ERROR); |
| 441 | goto apply_control_lookahead; |
| 442 | } |
| 443 | |
| 444 | goto another_expr_lookahead; |
| 445 | } |
| 446 | ERROR ("unrecognised start of expression", MPEXPR_RESULT_PARSE_ERROR); |
| 447 | |
| 448 | |
| 449 | another_operator_lookahead: |
| 450 | LOOKAHEAD (0); |
| 451 | another_operator: |
| 452 | TRACE (printf ("another operator maybe: %s\n", TOKEN_NAME(p->token))); |
| 453 | |
| 454 | switch (p->token) { |
| 455 | case TOKEN_EOF: |
| 456 | goto apply_control; |
| 457 | |
| 458 | case TOKEN_OPERATOR: |
| 459 | /* The next operator is compared to the one on top of the control stack. |
| 460 | If the next is lower precedence, or the same precedence and not |
| 461 | right-associative, then reduce using the control stack and look at |
| 462 | the next operator again later. */ |
| 463 | |
| 464 | #define PRECEDENCE_TEST_REDUCE(tprec,cprec,ttype,ctype) \ |
| 465 | ((tprec) < (cprec) \ |
| 466 | || ((tprec) == (cprec) && ! ((ttype) & MPEXPR_TYPE_RIGHTASSOC))) |
| 467 | |
| 468 | if (PRECEDENCE_TEST_REDUCE (p->token_op->precedence, CP->op->precedence, |
| 469 | p->token_op->type, CP->op->type)) |
| 470 | { |
| 471 | TRACE (printf ("defer operator: %s (prec %d vs %d, type 0x%X)\n", |
| 472 | p->token_op->name, |
| 473 | p->token_op->precedence, CP->op->precedence, |
| 474 | p->token_op->type)); |
| 475 | goto apply_control; |
| 476 | } |
| 477 | |
| 478 | /* An argsep is a binary operator, but is never pushed on the control |
| 479 | stack, it just accumulates an extra argument for a function. */ |
| 480 | if (p->token_op->type == MPEXPR_TYPE_ARGSEP) |
| 481 | { |
| 482 | if (CP->op->precedence != 0) |
| 483 | ERROR ("ARGSEP not in a function call", MPEXPR_RESULT_PARSE_ERROR); |
| 484 | |
| 485 | TRACE (printf ("argsep for function \"%s\"(%d)\n", |
| 486 | CP->op->name, CP->argcount)); |
| 487 | |
| 488 | #define IS_PAIRWISE(type) \ |
| 489 | (((type) & (MPEXPR_TYPE_MASK_ARGCOUNT | MPEXPR_TYPE_PAIRWISE)) \ |
| 490 | == (MPEXPR_TYPE_BINARY | MPEXPR_TYPE_PAIRWISE)) |
| 491 | |
| 492 | if (IS_PAIRWISE (CP->op->type) && CP->argcount >= 2) |
| 493 | { |
| 494 | TRACE (printf (" will reduce pairwise now\n")); |
| 495 | CP->argcount--; |
| 496 | CONTROL_PUSH (CP->op, 2); |
| 497 | goto apply_control; |
| 498 | } |
| 499 | |
| 500 | CP->argcount++; |
| 501 | goto another_expr_lookahead; |
| 502 | } |
| 503 | |
| 504 | switch (p->token_op->type & MPEXPR_TYPE_MASK_ARGCOUNT) { |
| 505 | case MPEXPR_TYPE_NARY(1): |
| 506 | /* Postfix unary operators can always be applied immediately. The |
| 507 | easiest way to do this is just push it on the control stack and go |
| 508 | to the normal control stack reduction code. */ |
| 509 | |
| 510 | TRACE (printf ("postfix unary operator: %s\n", p->token_op->name)); |
| 511 | if (p->token_op->type & MPEXPR_TYPE_PREFIX) |
| 512 | ERROR ("prefix unary operator used postfix", |
| 513 | MPEXPR_RESULT_PARSE_ERROR); |
| 514 | CONTROL_PUSH (p->token_op, 1); |
| 515 | goto apply_control_lookahead; |
| 516 | |
| 517 | case MPEXPR_TYPE_NARY(2): |
| 518 | CONTROL_PUSH (p->token_op, 2); |
| 519 | goto another_expr_lookahead; |
| 520 | |
| 521 | case MPEXPR_TYPE_NARY(3): |
| 522 | CONTROL_PUSH (p->token_op, 1); |
| 523 | goto another_expr_lookahead; |
| 524 | } |
| 525 | |
| 526 | TRACE (printf ("unrecognised operator \"%s\" type: 0x%X", |
| 527 | CP->op->name, CP->op->type)); |
| 528 | ERROR ("", MPEXPR_RESULT_PARSE_ERROR); |
| 529 | break; |
| 530 | |
| 531 | default: |
| 532 | TRACE (printf ("expecting an operator, got token %d", p->token)); |
| 533 | ERROR ("", MPEXPR_RESULT_PARSE_ERROR); |
| 534 | } |
| 535 | |
| 536 | |
| 537 | apply_control_lookahead: |
| 538 | LOOKAHEAD (0); |
| 539 | apply_control: |
| 540 | /* Apply the top element CP of the control stack. Data values are SP, |
| 541 | SP-1, etc. Result is left as stack top SP after popping consumed |
| 542 | values. |
| 543 | |
| 544 | The use of sp as a duplicate of SP will help compilers that can't |
| 545 | otherwise recognise the various uses of SP as common subexpressions. */ |
| 546 | |
| 547 | TRACE (printf ("apply control: nested %d, \"%s\" 0x%X, %d args\n", |
| 548 | p->control_top, CP->op->name, CP->op->type, CP->argcount)); |
| 549 | |
| 550 | TRACE (printf ("apply 0x%X-ary\n", |
| 551 | CP->op->type & MPEXPR_TYPE_MASK_ARGCOUNT)); |
| 552 | switch (CP->op->type & MPEXPR_TYPE_MASK_ARGCOUNT) { |
| 553 | case MPEXPR_TYPE_NARY(0): |
| 554 | { |
| 555 | mpX_ptr sp; |
| 556 | DATA_SPACE (); |
| 557 | DATA_PUSH (); |
| 558 | sp = SP; |
| 559 | switch (CP->op->type & MPEXPR_TYPE_MASK_ARGSTYLE) { |
| 560 | case 0: |
| 561 | (* (mpexpr_fun_0ary_t) CP->op->fun) (sp); |
| 562 | break; |
| 563 | case MPEXPR_TYPE_RESULT_INT: |
| 564 | (*p->mpX_set_si) (sp, (long) (* (mpexpr_fun_i_0ary_t) CP->op->fun) ()); |
| 565 | break; |
| 566 | default: |
| 567 | ERROR ("unrecognised 0ary argument calling style", |
| 568 | MPEXPR_RESULT_BAD_TABLE); |
| 569 | } |
| 570 | } |
| 571 | break; |
| 572 | |
| 573 | case MPEXPR_TYPE_NARY(1): |
| 574 | { |
| 575 | mpX_ptr sp = SP; |
| 576 | CHECK_ARGCOUNT ("unary", 1); |
| 577 | TRACE (MPX_TRACE ("before", sp)); |
| 578 | |
| 579 | switch (CP->op->type & MPEXPR_TYPE_MASK_SPECIAL) { |
| 580 | case 0: |
| 581 | /* not a special */ |
| 582 | break; |
| 583 | |
| 584 | case MPEXPR_TYPE_DONE & MPEXPR_TYPE_MASK_SPECIAL: |
| 585 | TRACE (printf ("special done\n")); |
| 586 | goto done; |
| 587 | |
| 588 | case MPEXPR_TYPE_LOGICAL_NOT & MPEXPR_TYPE_MASK_SPECIAL: |
| 589 | TRACE (printf ("special logical not\n")); |
| 590 | (*p->mpX_set_si) |
| 591 | (sp, (long) ((* (mpexpr_fun_i_unary_t) CP->op->fun) (sp) == 0)); |
| 592 | goto apply_control_done; |
| 593 | |
| 594 | case MPEXPR_TYPE_CLOSEPAREN & MPEXPR_TYPE_MASK_SPECIAL: |
| 595 | CONTROL_POP (); |
| 596 | if (CP->op->type == MPEXPR_TYPE_OPENPAREN) |
| 597 | { |
| 598 | TRACE (printf ("close paren matching open paren\n")); |
| 599 | CONTROL_POP (); |
| 600 | goto another_operator; |
| 601 | } |
| 602 | if (CP->op->precedence == 0) |
| 603 | { |
| 604 | TRACE (printf ("close paren for function\n")); |
| 605 | goto apply_control; |
| 606 | } |
| 607 | ERROR ("unexpected close paren", MPEXPR_RESULT_PARSE_ERROR); |
| 608 | |
| 609 | default: |
| 610 | TRACE (printf ("unrecognised special unary operator 0x%X", |
| 611 | CP->op->type & MPEXPR_TYPE_MASK_SPECIAL)); |
| 612 | ERROR ("", MPEXPR_RESULT_BAD_TABLE); |
| 613 | } |
| 614 | |
| 615 | switch (CP->op->type & MPEXPR_TYPE_MASK_ARGSTYLE) { |
| 616 | case 0: |
| 617 | (* (mpexpr_fun_unary_t) CP->op->fun) (sp, sp); |
| 618 | break; |
| 619 | case MPEXPR_TYPE_LAST_UI: |
| 620 | CHECK_UI (sp); |
| 621 | (* (mpexpr_fun_unary_ui_t) CP->op->fun) |
| 622 | (sp, (*p->mpX_get_ui) (sp)); |
| 623 | break; |
| 624 | case MPEXPR_TYPE_RESULT_INT: |
| 625 | (*p->mpX_set_si) |
| 626 | (sp, (long) (* (mpexpr_fun_i_unary_t) CP->op->fun) (sp)); |
| 627 | break; |
| 628 | case MPEXPR_TYPE_RESULT_INT | MPEXPR_TYPE_LAST_UI: |
| 629 | CHECK_UI (sp); |
| 630 | (*p->mpX_set_si) |
| 631 | (sp, |
| 632 | (long) (* (mpexpr_fun_i_unary_ui_t) CP->op->fun) |
| 633 | ((*p->mpX_get_ui) (sp))); |
| 634 | break; |
| 635 | default: |
| 636 | ERROR ("unrecognised unary argument calling style", |
| 637 | MPEXPR_RESULT_BAD_TABLE); |
| 638 | } |
| 639 | } |
| 640 | break; |
| 641 | |
| 642 | case MPEXPR_TYPE_NARY(2): |
| 643 | { |
| 644 | mpX_ptr sp; |
| 645 | |
| 646 | /* pairwise functions are allowed to have just one argument */ |
| 647 | if ((CP->op->type & MPEXPR_TYPE_PAIRWISE) |
| 648 | && CP->op->precedence == 0 |
| 649 | && CP->argcount == 1) |
| 650 | goto apply_control_done; |
| 651 | |
| 652 | CHECK_ARGCOUNT ("binary", 2); |
| 653 | DATA_POP (1); |
| 654 | sp = SP; |
| 655 | TRACE (MPX_TRACE ("lhs", sp); |
| 656 | MPX_TRACE ("rhs", sp+1)); |
| 657 | |
| 658 | if (CP->op->type & MPEXPR_TYPE_MASK_CMP) |
| 659 | { |
| 660 | int type = CP->op->type; |
| 661 | int cmp = (* (mpexpr_fun_i_binary_t) CP->op->fun) |
| 662 | (sp, sp+1); |
| 663 | (*p->mpX_set_si) |
| 664 | (sp, |
| 665 | (long) |
| 666 | (( (cmp < 0) & ((type & MPEXPR_TYPE_MASK_CMP_LT) != 0)) |
| 667 | | ((cmp == 0) & ((type & MPEXPR_TYPE_MASK_CMP_EQ) != 0)) |
| 668 | | ((cmp > 0) & ((type & MPEXPR_TYPE_MASK_CMP_GT) != 0)))); |
| 669 | goto apply_control_done; |
| 670 | } |
| 671 | |
| 672 | switch (CP->op->type & MPEXPR_TYPE_MASK_SPECIAL) { |
| 673 | case 0: |
| 674 | /* not a special */ |
| 675 | break; |
| 676 | |
| 677 | case MPEXPR_TYPE_QUESTION & MPEXPR_TYPE_MASK_SPECIAL: |
| 678 | ERROR ("'?' without ':'", MPEXPR_RESULT_PARSE_ERROR); |
| 679 | |
| 680 | case MPEXPR_TYPE_COLON & MPEXPR_TYPE_MASK_SPECIAL: |
| 681 | TRACE (printf ("special colon\n")); |
| 682 | CONTROL_POP (); |
| 683 | if (CP->op->type != MPEXPR_TYPE_QUESTION) |
| 684 | ERROR ("':' without '?'", MPEXPR_RESULT_PARSE_ERROR); |
| 685 | |
| 686 | CP->argcount--; |
| 687 | DATA_POP (1); |
| 688 | sp--; |
| 689 | TRACE (MPX_TRACE ("query", sp); |
| 690 | MPX_TRACE ("true", sp+1); |
| 691 | MPX_TRACE ("false", sp+2)); |
| 692 | (*p->mpX_set) |
| 693 | (sp, (* (mpexpr_fun_i_unary_t) CP->op->fun) (sp) |
| 694 | ? sp+1 : sp+2); |
| 695 | goto apply_control_done; |
| 696 | |
| 697 | case MPEXPR_TYPE_LOGICAL_AND & MPEXPR_TYPE_MASK_SPECIAL: |
| 698 | TRACE (printf ("special logical and\n")); |
| 699 | (*p->mpX_set_si) |
| 700 | (sp, |
| 701 | (long) |
| 702 | ((* (mpexpr_fun_i_unary_t) CP->op->fun) (sp) |
| 703 | && (* (mpexpr_fun_i_unary_t) CP->op->fun) (sp+1))); |
| 704 | goto apply_control_done; |
| 705 | |
| 706 | case MPEXPR_TYPE_LOGICAL_OR & MPEXPR_TYPE_MASK_SPECIAL: |
| 707 | TRACE (printf ("special logical and\n")); |
| 708 | (*p->mpX_set_si) |
| 709 | (sp, |
| 710 | (long) |
| 711 | ((* (mpexpr_fun_i_unary_t) CP->op->fun) (sp) |
| 712 | || (* (mpexpr_fun_i_unary_t) CP->op->fun) (sp+1))); |
| 713 | goto apply_control_done; |
| 714 | |
| 715 | case MPEXPR_TYPE_MAX & MPEXPR_TYPE_MASK_SPECIAL: |
| 716 | TRACE (printf ("special max\n")); |
| 717 | if ((* (mpexpr_fun_i_binary_t) CP->op->fun) (sp, sp+1) < 0) |
| 718 | (*p->mpX_swap) (sp, sp+1); |
| 719 | goto apply_control_done; |
| 720 | case MPEXPR_TYPE_MIN & MPEXPR_TYPE_MASK_SPECIAL: |
| 721 | TRACE (printf ("special min\n")); |
| 722 | if ((* (mpexpr_fun_i_binary_t) CP->op->fun) (sp, sp+1) > 0) |
| 723 | (*p->mpX_swap) (sp, sp+1); |
| 724 | goto apply_control_done; |
| 725 | |
| 726 | default: |
| 727 | ERROR ("unrecognised special binary operator", |
| 728 | MPEXPR_RESULT_BAD_TABLE); |
| 729 | } |
| 730 | |
| 731 | switch (CP->op->type & MPEXPR_TYPE_MASK_ARGSTYLE) { |
| 732 | case 0: |
| 733 | (* (mpexpr_fun_binary_t) CP->op->fun) (sp, sp, sp+1); |
| 734 | break; |
| 735 | case MPEXPR_TYPE_LAST_UI: |
| 736 | CHECK_UI (sp+1); |
| 737 | (* (mpexpr_fun_binary_ui_t) CP->op->fun) |
| 738 | (sp, sp, (*p->mpX_get_ui) (sp+1)); |
| 739 | break; |
| 740 | case MPEXPR_TYPE_RESULT_INT: |
| 741 | (*p->mpX_set_si) |
| 742 | (sp, |
| 743 | (long) (* (mpexpr_fun_i_binary_t) CP->op->fun) (sp, sp+1)); |
| 744 | break; |
| 745 | case MPEXPR_TYPE_LAST_UI | MPEXPR_TYPE_RESULT_INT: |
| 746 | CHECK_UI (sp+1); |
| 747 | (*p->mpX_set_si) |
| 748 | (sp, |
| 749 | (long) (* (mpexpr_fun_i_binary_ui_t) CP->op->fun) |
| 750 | (sp, (*p->mpX_get_ui) (sp+1))); |
| 751 | break; |
| 752 | default: |
| 753 | ERROR ("unrecognised binary argument calling style", |
| 754 | MPEXPR_RESULT_BAD_TABLE); |
| 755 | } |
| 756 | } |
| 757 | break; |
| 758 | |
| 759 | case MPEXPR_TYPE_NARY(3): |
| 760 | { |
| 761 | mpX_ptr sp; |
| 762 | |
| 763 | CHECK_ARGCOUNT ("ternary", 3); |
| 764 | DATA_POP (2); |
| 765 | sp = SP; |
| 766 | TRACE (MPX_TRACE ("arg1", sp); |
| 767 | MPX_TRACE ("arg2", sp+1); |
| 768 | MPX_TRACE ("arg3", sp+1)); |
| 769 | |
| 770 | switch (CP->op->type & MPEXPR_TYPE_MASK_ARGSTYLE) { |
| 771 | case 0: |
| 772 | (* (mpexpr_fun_ternary_t) CP->op->fun) (sp, sp, sp+1, sp+2); |
| 773 | break; |
| 774 | case MPEXPR_TYPE_LAST_UI: |
| 775 | CHECK_UI (sp+2); |
| 776 | (* (mpexpr_fun_ternary_ui_t) CP->op->fun) |
| 777 | (sp, sp, sp+1, (*p->mpX_get_ui) (sp+2)); |
| 778 | break; |
| 779 | case MPEXPR_TYPE_RESULT_INT: |
| 780 | (*p->mpX_set_si) |
| 781 | (sp, |
| 782 | (long) (* (mpexpr_fun_i_ternary_t) CP->op->fun) |
| 783 | (sp, sp+1, sp+2)); |
| 784 | break; |
| 785 | case MPEXPR_TYPE_LAST_UI | MPEXPR_TYPE_RESULT_INT: |
| 786 | CHECK_UI (sp+2); |
| 787 | (*p->mpX_set_si) |
| 788 | (sp, |
| 789 | (long) (* (mpexpr_fun_i_ternary_ui_t) CP->op->fun) |
| 790 | (sp, sp+1, (*p->mpX_get_ui) (sp+2))); |
| 791 | break; |
| 792 | default: |
| 793 | ERROR ("unrecognised binary argument calling style", |
| 794 | MPEXPR_RESULT_BAD_TABLE); |
| 795 | } |
| 796 | } |
| 797 | break; |
| 798 | |
| 799 | default: |
| 800 | TRACE (printf ("unrecognised operator type: 0x%X\n", CP->op->type)); |
| 801 | ERROR ("", MPEXPR_RESULT_PARSE_ERROR); |
| 802 | } |
| 803 | |
| 804 | apply_control_done: |
| 805 | TRACE (MPX_TRACE ("result", SP)); |
| 806 | CONTROL_POP (); |
| 807 | goto another_operator; |
| 808 | |
| 809 | done: |
| 810 | if (p->error_code == MPEXPR_RESULT_OK) |
| 811 | { |
| 812 | if (p->data_top != 0) |
| 813 | { |
| 814 | TRACE (printf ("data stack want top at 0, got %d\n", p->data_top)); |
| 815 | p->error_code = MPEXPR_RESULT_PARSE_ERROR; |
| 816 | } |
| 817 | else |
| 818 | (*p->mpX_set_or_swap) (p->res, SP); |
| 819 | } |
| 820 | |
| 821 | { |
| 822 | int i; |
| 823 | for (i = 0; i < p->data_inited; i++) |
| 824 | { |
| 825 | TRACE (printf ("clear %d\n", i)); |
| 826 | (*p->mpX_clear) (p->data_stack+i); |
| 827 | } |
| 828 | } |
| 829 | |
| 830 | FREE_FUNC_TYPE (p->data_stack, p->data_alloc, union mpX_t); |
| 831 | FREE_FUNC_TYPE (p->control_stack, p->control_alloc, struct mpexpr_control_t); |
| 832 | |
| 833 | return p->error_code; |
| 834 | } |