1/* mpexpr_evaluate -- shared code for simple expression evaluation
2
3Copyright 2000-2002, 2004 Free Software Foundation, Inc.
4
5This file is part of the GNU MP Library.
6
7The GNU MP Library is free software; you can redistribute it and/or modify
8it 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
14or
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
20or both in parallel, as here.
21
22The GNU MP Library is distributed in the hope that it will be useful, but
23WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
25for more details.
26
27You should have received copies of the GNU General Public License and the
28GNU Lesser General Public License along with the GNU MP Library.  If not,
29see 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
147static int
148lookahead (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
370int
371mpexpr_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}
835