]> oss.titaniummirror.com Git - msp430-gcc.git/blobdiff - gmp/demos/expr/expr.c
Imported gcc-4.4.3
[msp430-gcc.git] / gmp / demos / expr / expr.c
diff --git a/gmp/demos/expr/expr.c b/gmp/demos/expr/expr.c
new file mode 100644 (file)
index 0000000..f78c321
--- /dev/null
@@ -0,0 +1,823 @@
+/* mpexpr_evaluate -- shared code for simple expression evaluation
+
+Copyright 2000, 2001, 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of the GNU MP Library.
+
+The GNU MP Library is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 3 of the License, or (at your
+option) any later version.
+
+The GNU MP Library is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with the GNU MP Library.  If not, see http://www.gnu.org/licenses/.  */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "gmp.h"
+#include "expr-impl.h"
+
+
+/* Change this to "#define TRACE(x) x" to get some traces.  The trace
+   printfs junk up the code a bit, but it's very hard to tell what's going
+   on without them.  Set MPX_TRACE to a suitable output function for the
+   mpz/mpq/mpf being run (if you have the wrong trace function it'll
+   probably segv).  */
+
+#define TRACE(x)
+#define MPX_TRACE  mpz_trace
+
+
+/* A few helper macros copied from gmp-impl.h */
+#define ALLOCATE_FUNC_TYPE(n,type) \
+  ((type *) (*allocate_func) ((n) * sizeof (type)))
+#define ALLOCATE_FUNC_LIMBS(n)   ALLOCATE_FUNC_TYPE (n, mp_limb_t)
+#define REALLOCATE_FUNC_TYPE(p, old_size, new_size, type) \
+  ((type *) (*reallocate_func)                            \
+   (p, (old_size) * sizeof (type), (new_size) * sizeof (type)))
+#define REALLOCATE_FUNC_LIMBS(p, old_size, new_size) \
+  REALLOCATE_FUNC_TYPE(p, old_size, new_size, mp_limb_t)
+#define FREE_FUNC_TYPE(p,n,type) (*free_func) (p, (n) * sizeof (type))
+#define FREE_FUNC_LIMBS(p,n)     FREE_FUNC_TYPE (p, n, mp_limb_t)
+#define ASSERT(x)
+
+
+
+/* All the error strings are just for diagnostic traces.  Only the error
+   code is actually returned.  */
+#define ERROR(str,code)                 \
+  {                                     \
+    TRACE (printf ("%s\n", str));       \
+    p->error_code = (code);             \
+    goto done;                          \
+  }
+
+
+#define REALLOC(ptr, alloc, incr, type)                         \
+  do {                                                          \
+    int  new_alloc = (alloc) + (incr);                          \
+    ptr = REALLOCATE_FUNC_TYPE (ptr, alloc, new_alloc, type);   \
+    (alloc) = new_alloc;                                        \
+  } while (0)
+
+
+/* data stack top element */
+#define SP   (p->data_stack + p->data_top)
+
+/* Make sure there's room for another data element above current top.
+   reallocate_func is fetched for when this macro is used in lookahead(). */
+#define DATA_SPACE()                                                    \
+  do {                                                                  \
+    if (p->data_top + 1 >= p->data_alloc)                               \
+      {                                                                 \
+       void *(*reallocate_func) (void *, size_t, size_t);              \
+       mp_get_memory_functions (NULL, &reallocate_func, NULL);         \
+       TRACE (printf ("grow stack from %d\n", p->data_alloc));         \
+       REALLOC (p->data_stack, p->data_alloc, 20, union mpX_t);        \
+      }                                                                 \
+    ASSERT (p->data_top + 1 <= p->data_inited);                         \
+    if (p->data_top + 1 == p->data_inited)                              \
+      {                                                                 \
+       TRACE (printf ("initialize %d\n", p->data_top + 1));            \
+       (*p->mpX_init) (&p->data_stack[p->data_top + 1], p->prec);      \
+       p->data_inited++;                                               \
+      }                                                                 \
+  } while (0)
+
+#define DATA_PUSH()                             \
+  do {                                          \
+    p->data_top++;                              \
+    ASSERT (p->data_top < p->data_alloc);       \
+    ASSERT (p->data_top < p->data_inited);      \
+  } while (0)
+
+/* the last stack entry is never popped, so top>=0 will be true */
+#define DATA_POP(n)             \
+  do {                          \
+    p->data_top -= (n);         \
+    ASSERT (p->data_top >= 0);  \
+  } while (0)
+
+
+/* lookahead() parses the next token.  Return 1 if successful, with some
+   extra data.  Return 0 if fail, with reason in p->error_code.
+
+   "prefix" is MPEXPR_TYPE_PREFIX if an operator with that attribute is
+   preferred, or 0 if an operator without is preferred. */
+
+#define TOKEN_EOF         -1   /* no extra data */
+#define TOKEN_VALUE       -2   /* pushed onto data stack */
+#define TOKEN_OPERATOR    -3   /* stored in p->token_op */
+#define TOKEN_FUNCTION    -4   /* stored in p->token_op */
+
+#define TOKEN_NAME(n)                           \
+  ((n) == TOKEN_EOF ? "TOKEN_EOF"               \
+   : (n) == TOKEN_VALUE ? "TOKEN_VALUE"         \
+   : (n) == TOKEN_OPERATOR ? "TOKEN_OPERATOR"   \
+   : (n) == TOKEN_VALUE ? "TOKEN_FUNCTION"      \
+   : "UNKNOWN TOKEN")
+
+/* Functions default to being parsed as whole words, operators to match just
+   at the start of the string.  The type flags override this. */
+#define WHOLEWORD(op)                           \
+  (op->precedence == 0                          \
+   ? (! (op->type & MPEXPR_TYPE_OPERATOR))      \
+   :   (op->type & MPEXPR_TYPE_WHOLEWORD))
+
+#define isasciispace(c)   (isascii (c) && isspace (c))
+
+static int
+lookahead (struct mpexpr_parse_t *p, int prefix)
+{
+  __gmp_const struct mpexpr_operator_t  *op, *op_found;
+  size_t  oplen, oplen_found, wlen;
+  int     i;
+
+  /* skip white space */
+  while (p->elen > 0 && isasciispace (*p->e))
+    p->e++, p->elen--;
+
+  if (p->elen == 0)
+    {
+      TRACE (printf ("lookahead EOF\n"));
+      p->token = TOKEN_EOF;
+      return 1;
+    }
+
+  DATA_SPACE ();
+
+  /* Get extent of whole word. */
+  for (wlen = 0; wlen < p->elen; wlen++)
+    if (! isasciicsym (p->e[wlen]))
+      break;
+
+  TRACE (printf ("lookahead at: \"%.*s\" length %u, word %u\n",
+                (int) p->elen, p->e, p->elen, wlen));
+
+  op_found = NULL;
+  oplen_found = 0;
+  for (op = p->table; op->name != NULL; op++)
+    {
+      if (op->type == MPEXPR_TYPE_NEW_TABLE)
+       {
+         printf ("new\n");
+         op = (struct mpexpr_operator_t *) op->name - 1;
+         continue;
+       }
+
+      oplen = strlen (op->name);
+      if (! ((WHOLEWORD (op) ? wlen == oplen : p->elen >= oplen)
+            && memcmp (p->e, op->name, oplen) == 0))
+       continue;
+
+      /* Shorter matches don't replace longer previous ones. */
+      if (op_found && oplen < oplen_found)
+       continue;
+
+      /* On a match of equal length to a previous one, the old match isn't
+        replaced if it has the preferred prefix, and if it doesn't then
+        it's not replaced if the new one also doesn't.  */
+      if (op_found && oplen == oplen_found
+         && ((op_found->type & MPEXPR_TYPE_PREFIX) == prefix
+             || (op->type & MPEXPR_TYPE_PREFIX) != prefix))
+       continue;
+
+      /* This is now either the first match seen, or a longer than previous
+        match, or an equal to previous one but with a preferred prefix. */
+      op_found = op;
+      oplen_found = oplen;
+    }
+
+  if (op_found)
+    {
+      p->e += oplen_found, p->elen -= oplen_found;
+
+      if (op_found->type == MPEXPR_TYPE_VARIABLE)
+       {
+         if (p->elen == 0)
+           ERROR ("end of string expecting a variable",
+                  MPEXPR_RESULT_PARSE_ERROR);
+         i = p->e[0] - 'a';
+         if (i < 0 || i >= MPEXPR_VARIABLES)
+           ERROR ("bad variable name", MPEXPR_RESULT_BAD_VARIABLE);
+         goto variable;
+       }
+
+      if (op_found->precedence == 0)
+       {
+         TRACE (printf ("lookahead function: %s\n", op_found->name));
+         p->token = TOKEN_FUNCTION;
+         p->token_op = op_found;
+         return 1;
+       }
+      else
+       {
+         TRACE (printf ("lookahead operator: %s\n", op_found->name));
+         p->token = TOKEN_OPERATOR;
+         p->token_op = op_found;
+         return 1;
+       }
+    }
+
+  oplen = (*p->mpX_number) (SP+1, p->e, p->elen, p->base);
+  if (oplen != 0)
+    {
+      p->e += oplen, p->elen -= oplen;
+      p->token = TOKEN_VALUE;
+      DATA_PUSH ();
+      TRACE (MPX_TRACE ("lookahead number", SP));
+      return 1;
+    }
+
+  /* Maybe an unprefixed one character variable */
+  i = p->e[0] - 'a';
+  if (wlen == 1 && i >= 0 && i < MPEXPR_VARIABLES)
+    {
+    variable:
+      p->e++, p->elen--;
+      if (p->var[i] == NULL)
+       ERROR ("NULL variable", MPEXPR_RESULT_BAD_VARIABLE);
+      TRACE (printf ("lookahead variable: var[%d] = ", i);
+            MPX_TRACE ("", p->var[i]));
+      p->token = TOKEN_VALUE;
+      DATA_PUSH ();
+      (*p->mpX_set) (SP, p->var[i]);
+      return 1;
+    }
+
+  ERROR ("no token matched", MPEXPR_RESULT_PARSE_ERROR);
+
+ done:
+  return 0;
+}
+
+
+/* control stack current top element */
+#define CP   (p->control_stack + p->control_top)
+
+/* make sure there's room for another control element above current top */
+#define CONTROL_SPACE()                                                    \
+  do {                                                                     \
+    if (p->control_top + 1 >= p->control_alloc)                            \
+      {                                                                    \
+       TRACE (printf ("grow control stack from %d\n", p->control_alloc)); \
+       REALLOC (p->control_stack, p->control_alloc, 20,                   \
+                struct mpexpr_control_t);                                 \
+      }                                                                    \
+  } while (0)
+
+/* Push an operator on the control stack, claiming currently to have the
+   given number of args ready.  Local variable "op" is used in case opptr is
+   a reference through CP.  */
+#define CONTROL_PUSH(opptr,args)                        \
+  do {                                                  \
+    __gmp_const struct mpexpr_operator_t *op = opptr;   \
+    struct mpexpr_control_t *cp;                        \
+    CONTROL_SPACE ();                                   \
+    p->control_top++;                                   \
+    ASSERT (p->control_top < p->control_alloc);         \
+    cp = CP;                                            \
+    cp->op = op;                                        \
+    cp->argcount = (args);                              \
+    TRACE_CONTROL("control stack push:");               \
+  } while (0)
+
+/* The special operator_done is never popped, so top>=0 will hold. */
+#define CONTROL_POP()                           \
+  do {                                          \
+    p->control_top--;                           \
+    ASSERT (p->control_top >= 0);               \
+    TRACE_CONTROL ("control stack pop:");       \
+  } while (0)
+
+#define TRACE_CONTROL(str)                              \
+  TRACE ({                                              \
+    int  i;                                             \
+    printf ("%s depth %d:", str, p->control_top);       \
+    for (i = 0; i <= p->control_top; i++)               \
+      printf (" \"%s\"(%d)",                            \
+             p->control_stack[i].op->name,             \
+             p->control_stack[i].argcount);            \
+    printf ("\n");                                      \
+  });
+
+
+#define LOOKAHEAD(prefix)               \
+  do {                                  \
+    if (! lookahead (p, prefix))        \
+      goto done;                        \
+  } while (0)
+
+#define CHECK_UI(n)                                                     \
+  do {                                                                  \
+    if (! (*p->mpX_ulong_p) (n))                                        \
+      ERROR ("operand doesn't fit ulong", MPEXPR_RESULT_NOT_UI);        \
+  } while (0)
+
+#define CHECK_ARGCOUNT(str,n)                                              \
+  do {                                                                     \
+    if (CP->argcount != (n))                                               \
+      {                                                                    \
+       TRACE (printf ("wrong number of arguments for %s, got %d want %d", \
+                      str, CP->argcount, n));                             \
+       ERROR ("", MPEXPR_RESULT_PARSE_ERROR);                             \
+      }                                                                    \
+  } while (0)
+
+
+/* There's two basic states here.  In both p->token is the next token.
+
+   "another_expr" is when a whole expression should be parsed.  This means a
+   literal or variable value possibly followed by an operator, or a function
+   or prefix operator followed by a further whole expression.
+
+   "another_operator" is when an expression has been parsed and its value is
+   on the top of the data stack (SP) and an optional further postfix or
+   infix operator should be parsed.
+
+   In "another_operator" precedences determine whether to push the operator
+   onto the control stack, or instead go to "apply_control" to reduce the
+   operator currently on top of the control stack.
+
+   When an operator has both a prefix and postfix/infix form, a LOOKAHEAD()
+   for "another_expr" will seek the prefix form, a LOOKAHEAD() for
+   "another_operator" will seek the postfix/infix form.  The grammar is
+   simple enough that the next state is known before reading the next token.
+
+   Argument count checking guards against functions consuming the wrong
+   number of operands from the data stack.  The same checks are applied to
+   operators, but will always pass since a UNARY or BINARY will only ever
+   parse with the correct operands.  */
+
+int
+mpexpr_evaluate (struct mpexpr_parse_t *p)
+{
+  void *(*allocate_func) (size_t);
+  void *(*reallocate_func) (void *, size_t, size_t);
+  void (*free_func) (void *, size_t);
+
+  mp_get_memory_functions (&allocate_func, &reallocate_func, &free_func);
+
+  TRACE (printf ("mpexpr_evaluate() base %d \"%.*s\"\n",
+                p->base, (int) p->elen, p->e));
+
+  /* "done" is a special sentinel at the bottom of the control stack,
+     precedence -1 is lower than any normal operator.  */
+  {
+    static __gmp_const struct mpexpr_operator_t  operator_done
+      = { "DONE", NULL, MPEXPR_TYPE_DONE, -1 };
+
+    p->control_alloc = 20;
+    p->control_stack = ALLOCATE_FUNC_TYPE (p->control_alloc,
+                                          struct mpexpr_control_t);
+    p->control_top = 0;
+    CP->op = &operator_done;
+    CP->argcount = 1;
+  }
+
+  p->data_inited = 0;
+  p->data_alloc = 20;
+  p->data_stack = ALLOCATE_FUNC_TYPE (p->data_alloc, union mpX_t);
+  p->data_top = -1;
+
+  p->error_code = MPEXPR_RESULT_OK;
+
+
+ another_expr_lookahead:
+  LOOKAHEAD (MPEXPR_TYPE_PREFIX);
+  TRACE (printf ("another expr\n"));
+
+  /*another_expr:*/
+  switch (p->token) {
+  case TOKEN_VALUE:
+    goto another_operator_lookahead;
+
+  case TOKEN_OPERATOR:
+    TRACE (printf ("operator %s\n", p->token_op->name));
+    if (! (p->token_op->type & MPEXPR_TYPE_PREFIX))
+      ERROR ("expected a prefix operator", MPEXPR_RESULT_PARSE_ERROR);
+
+    CONTROL_PUSH (p->token_op, 1);
+    goto another_expr_lookahead;
+
+  case TOKEN_FUNCTION:
+    CONTROL_PUSH (p->token_op, 1);
+
+    if (p->token_op->type & MPEXPR_TYPE_CONSTANT)
+      goto apply_control_lookahead;
+
+    LOOKAHEAD (MPEXPR_TYPE_PREFIX);
+    if (! (p->token == TOKEN_OPERATOR
+          && p->token_op->type == MPEXPR_TYPE_OPENPAREN))
+      ERROR ("expected open paren for function", MPEXPR_RESULT_PARSE_ERROR);
+
+    TRACE (printf ("open paren for function \"%s\"\n", CP->op->name));
+
+    if ((CP->op->type & MPEXPR_TYPE_MASK_ARGCOUNT) == MPEXPR_TYPE_NARY(0))
+      {
+       LOOKAHEAD (0);
+       if (! (p->token == TOKEN_OPERATOR
+              && p->token_op->type == MPEXPR_TYPE_CLOSEPAREN))
+         ERROR ("expected close paren for 0ary function",
+                MPEXPR_RESULT_PARSE_ERROR);
+       goto apply_control_lookahead;
+      }
+
+    goto another_expr_lookahead;
+  }
+  ERROR ("unrecognised start of expression", MPEXPR_RESULT_PARSE_ERROR);
+
+
+ another_operator_lookahead:
+  LOOKAHEAD (0);
+ another_operator:
+  TRACE (printf ("another operator maybe: %s\n", TOKEN_NAME(p->token)));
+
+  switch (p->token) {
+  case TOKEN_EOF:
+    goto apply_control;
+
+  case TOKEN_OPERATOR:
+    /* The next operator is compared to the one on top of the control stack.
+       If the next is lower precedence, or the same precedence and not
+       right-associative, then reduce using the control stack and look at
+       the next operator again later.  */
+
+#define PRECEDENCE_TEST_REDUCE(tprec,cprec,ttype,ctype)                 \
+    ((tprec) < (cprec)                                                  \
+     || ((tprec) == (cprec) && ! ((ttype) & MPEXPR_TYPE_RIGHTASSOC)))
+
+    if (PRECEDENCE_TEST_REDUCE (p->token_op->precedence, CP->op->precedence,
+                               p->token_op->type,       CP->op->type))
+      {
+       TRACE (printf ("defer operator: %s (prec %d vs %d, type 0x%X)\n",
+                      p->token_op->name,
+                      p->token_op->precedence, CP->op->precedence,
+                      p->token_op->type));
+       goto apply_control;
+      }
+
+    /* An argsep is a binary operator, but is never pushed on the control
+       stack, it just accumulates an extra argument for a function. */
+    if (p->token_op->type == MPEXPR_TYPE_ARGSEP)
+      {
+       if (CP->op->precedence != 0)
+         ERROR ("ARGSEP not in a function call", MPEXPR_RESULT_PARSE_ERROR);
+
+       TRACE (printf ("argsep for function \"%s\"(%d)\n",
+                      CP->op->name, CP->argcount));
+
+#define IS_PAIRWISE(type)                                               \
+       (((type) & (MPEXPR_TYPE_MASK_ARGCOUNT | MPEXPR_TYPE_PAIRWISE))  \
+        == (MPEXPR_TYPE_BINARY | MPEXPR_TYPE_PAIRWISE))
+
+       if (IS_PAIRWISE (CP->op->type) && CP->argcount >= 2)
+         {
+           TRACE (printf ("    will reduce pairwise now\n"));
+           CP->argcount--;
+           CONTROL_PUSH (CP->op, 2);
+           goto apply_control;
+         }
+
+       CP->argcount++;
+       goto another_expr_lookahead;
+      }
+
+    switch (p->token_op->type & MPEXPR_TYPE_MASK_ARGCOUNT) {
+    case MPEXPR_TYPE_NARY(1):
+      /* Postfix unary operators can always be applied immediately.  The
+        easiest way to do this is just push it on the control stack and go
+        to the normal control stack reduction code. */
+
+      TRACE (printf ("postfix unary operator: %s\n", p->token_op->name));
+      if (p->token_op->type & MPEXPR_TYPE_PREFIX)
+       ERROR ("prefix unary operator used postfix",
+              MPEXPR_RESULT_PARSE_ERROR);
+      CONTROL_PUSH (p->token_op, 1);
+      goto apply_control_lookahead;
+
+    case MPEXPR_TYPE_NARY(2):
+      CONTROL_PUSH (p->token_op, 2);
+      goto another_expr_lookahead;
+
+    case MPEXPR_TYPE_NARY(3):
+      CONTROL_PUSH (p->token_op, 1);
+      goto another_expr_lookahead;
+    }
+
+    TRACE (printf ("unrecognised operator \"%s\" type: 0x%X",
+                  CP->op->name, CP->op->type));
+    ERROR ("", MPEXPR_RESULT_PARSE_ERROR);
+    break;
+
+  default:
+    TRACE (printf ("expecting an operator, got token %d", p->token));
+    ERROR ("", MPEXPR_RESULT_PARSE_ERROR);
+  }
+
+
+ apply_control_lookahead:
+  LOOKAHEAD (0);
+ apply_control:
+  /* Apply the top element CP of the control stack.  Data values are SP,
+     SP-1, etc.  Result is left as stack top SP after popping consumed
+     values.
+
+     The use of sp as a duplicate of SP will help compilers that can't
+     otherwise recognise the various uses of SP as common subexpressions.  */
+
+  TRACE (printf ("apply control: nested %d, \"%s\" 0x%X, %d args\n",
+                p->control_top, CP->op->name, CP->op->type, CP->argcount));
+
+  TRACE (printf ("apply 0x%X-ary\n",
+                CP->op->type & MPEXPR_TYPE_MASK_ARGCOUNT));
+  switch (CP->op->type & MPEXPR_TYPE_MASK_ARGCOUNT) {
+  case MPEXPR_TYPE_NARY(0):
+    {
+      mpX_ptr  sp;
+      DATA_SPACE ();
+      DATA_PUSH ();
+      sp = SP;
+      switch (CP->op->type & MPEXPR_TYPE_MASK_ARGSTYLE) {
+      case 0:
+       (* (mpexpr_fun_0ary_t) CP->op->fun) (sp);
+       break;
+      case MPEXPR_TYPE_RESULT_INT:
+       (*p->mpX_set_si) (sp, (long) (* (mpexpr_fun_i_0ary_t) CP->op->fun) ());
+       break;
+      default:
+       ERROR ("unrecognised 0ary argument calling style",
+              MPEXPR_RESULT_BAD_TABLE);
+      }
+    }
+    break;
+
+  case MPEXPR_TYPE_NARY(1):
+    {
+      mpX_ptr  sp = SP;
+      CHECK_ARGCOUNT ("unary", 1);
+      TRACE (MPX_TRACE ("before", sp));
+
+      switch (CP->op->type & MPEXPR_TYPE_MASK_SPECIAL) {
+      case 0:
+       /* not a special */
+       break;
+
+      case MPEXPR_TYPE_DONE & MPEXPR_TYPE_MASK_SPECIAL:
+       TRACE (printf ("special done\n"));
+       goto done;
+
+      case MPEXPR_TYPE_LOGICAL_NOT & MPEXPR_TYPE_MASK_SPECIAL:
+       TRACE (printf ("special logical not\n"));
+       (*p->mpX_set_si)
+         (sp, (long) ((* (mpexpr_fun_i_unary_t) CP->op->fun) (sp) == 0));
+       goto apply_control_done;
+
+      case MPEXPR_TYPE_CLOSEPAREN & MPEXPR_TYPE_MASK_SPECIAL:
+       CONTROL_POP ();
+       if (CP->op->type == MPEXPR_TYPE_OPENPAREN)
+         {
+           TRACE (printf ("close paren matching open paren\n"));
+           CONTROL_POP ();
+           goto another_operator;
+         }
+       if (CP->op->precedence == 0)
+         {
+           TRACE (printf ("close paren for function\n"));
+           goto apply_control;
+         }
+       ERROR ("unexpected close paren", MPEXPR_RESULT_PARSE_ERROR);
+
+      default:
+       TRACE (printf ("unrecognised special unary operator 0x%X",
+                      CP->op->type & MPEXPR_TYPE_MASK_SPECIAL));
+       ERROR ("", MPEXPR_RESULT_BAD_TABLE);
+      }
+
+      switch (CP->op->type & MPEXPR_TYPE_MASK_ARGSTYLE) {
+      case 0:
+       (* (mpexpr_fun_unary_t) CP->op->fun) (sp, sp);
+       break;
+      case MPEXPR_TYPE_LAST_UI:
+       CHECK_UI (sp);
+       (* (mpexpr_fun_unary_ui_t) CP->op->fun)
+         (sp, (*p->mpX_get_ui) (sp));
+       break;
+      case MPEXPR_TYPE_RESULT_INT:
+       (*p->mpX_set_si)
+         (sp, (long) (* (mpexpr_fun_i_unary_t) CP->op->fun) (sp));
+       break;
+      case MPEXPR_TYPE_RESULT_INT | MPEXPR_TYPE_LAST_UI:
+       CHECK_UI (sp);
+       (*p->mpX_set_si)
+         (sp,
+          (long) (* (mpexpr_fun_i_unary_ui_t) CP->op->fun)
+          ((*p->mpX_get_ui) (sp)));
+       break;
+      default:
+       ERROR ("unrecognised unary argument calling style",
+              MPEXPR_RESULT_BAD_TABLE);
+      }
+    }
+    break;
+
+  case MPEXPR_TYPE_NARY(2):
+    {
+      mpX_ptr  sp;
+
+      /* pairwise functions are allowed to have just one argument */
+      if ((CP->op->type & MPEXPR_TYPE_PAIRWISE)
+         && CP->op->precedence == 0
+         && CP->argcount == 1)
+       goto apply_control_done;
+
+      CHECK_ARGCOUNT ("binary", 2);
+      DATA_POP (1);
+      sp = SP;
+      TRACE (MPX_TRACE ("lhs", sp);
+            MPX_TRACE ("rhs", sp+1));
+
+      if (CP->op->type & MPEXPR_TYPE_MASK_CMP)
+       {
+         int  type = CP->op->type;
+         int  cmp = (* (mpexpr_fun_i_binary_t) CP->op->fun)
+           (sp, sp+1);
+         (*p->mpX_set_si)
+           (sp,
+            (long)
+            ((  (cmp  < 0) & ((type & MPEXPR_TYPE_MASK_CMP_LT) != 0))
+             | ((cmp == 0) & ((type & MPEXPR_TYPE_MASK_CMP_EQ) != 0))
+             | ((cmp  > 0) & ((type & MPEXPR_TYPE_MASK_CMP_GT) != 0))));
+         goto apply_control_done;
+       }
+
+      switch (CP->op->type & MPEXPR_TYPE_MASK_SPECIAL) {
+      case 0:
+       /* not a special */
+       break;
+
+      case MPEXPR_TYPE_QUESTION & MPEXPR_TYPE_MASK_SPECIAL:
+       ERROR ("'?' without ':'", MPEXPR_RESULT_PARSE_ERROR);
+
+      case MPEXPR_TYPE_COLON & MPEXPR_TYPE_MASK_SPECIAL:
+       TRACE (printf ("special colon\n"));
+       CONTROL_POP ();
+       if (CP->op->type != MPEXPR_TYPE_QUESTION)
+         ERROR ("':' without '?'", MPEXPR_RESULT_PARSE_ERROR);
+
+       CP->argcount--;
+       DATA_POP (1);
+       sp--;
+       TRACE (MPX_TRACE ("query", sp);
+              MPX_TRACE ("true",  sp+1);
+              MPX_TRACE ("false", sp+2));
+       (*p->mpX_set)
+         (sp, (* (mpexpr_fun_i_unary_t) CP->op->fun) (sp)
+          ? sp+1 : sp+2);
+       goto apply_control_done;
+
+      case MPEXPR_TYPE_LOGICAL_AND & MPEXPR_TYPE_MASK_SPECIAL:
+       TRACE (printf ("special logical and\n"));
+       (*p->mpX_set_si)
+         (sp,
+          (long)
+          ((* (mpexpr_fun_i_unary_t) CP->op->fun) (sp)
+           && (* (mpexpr_fun_i_unary_t) CP->op->fun) (sp+1)));
+       goto apply_control_done;
+
+      case MPEXPR_TYPE_LOGICAL_OR & MPEXPR_TYPE_MASK_SPECIAL:
+       TRACE (printf ("special logical and\n"));
+       (*p->mpX_set_si)
+         (sp,
+          (long)
+          ((* (mpexpr_fun_i_unary_t) CP->op->fun) (sp)
+           || (* (mpexpr_fun_i_unary_t) CP->op->fun) (sp+1)));
+       goto apply_control_done;
+
+      case MPEXPR_TYPE_MAX & MPEXPR_TYPE_MASK_SPECIAL:
+       TRACE (printf ("special max\n"));
+       if ((* (mpexpr_fun_i_binary_t) CP->op->fun) (sp, sp+1) < 0)
+         (*p->mpX_swap) (sp, sp+1);
+       goto apply_control_done;
+      case MPEXPR_TYPE_MIN & MPEXPR_TYPE_MASK_SPECIAL:
+       TRACE (printf ("special min\n"));
+       if ((* (mpexpr_fun_i_binary_t) CP->op->fun) (sp, sp+1) > 0)
+         (*p->mpX_swap) (sp, sp+1);
+       goto apply_control_done;
+
+      default:
+       ERROR ("unrecognised special binary operator",
+              MPEXPR_RESULT_BAD_TABLE);
+      }
+
+      switch (CP->op->type & MPEXPR_TYPE_MASK_ARGSTYLE) {
+      case 0:
+       (* (mpexpr_fun_binary_t) CP->op->fun) (sp, sp, sp+1);
+       break;
+      case MPEXPR_TYPE_LAST_UI:
+       CHECK_UI (sp+1);
+       (* (mpexpr_fun_binary_ui_t) CP->op->fun)
+         (sp, sp, (*p->mpX_get_ui) (sp+1));
+       break;
+      case MPEXPR_TYPE_RESULT_INT:
+       (*p->mpX_set_si)
+         (sp,
+          (long) (* (mpexpr_fun_i_binary_t) CP->op->fun) (sp, sp+1));
+       break;
+      case MPEXPR_TYPE_LAST_UI | MPEXPR_TYPE_RESULT_INT:
+       CHECK_UI (sp+1);
+       (*p->mpX_set_si)
+         (sp,
+          (long) (* (mpexpr_fun_i_binary_ui_t) CP->op->fun)
+          (sp, (*p->mpX_get_ui) (sp+1)));
+       break;
+      default:
+       ERROR ("unrecognised binary argument calling style",
+              MPEXPR_RESULT_BAD_TABLE);
+      }
+    }
+    break;
+
+  case MPEXPR_TYPE_NARY(3):
+    {
+      mpX_ptr  sp;
+
+      CHECK_ARGCOUNT ("ternary", 3);
+      DATA_POP (2);
+      sp = SP;
+      TRACE (MPX_TRACE ("arg1", sp);
+            MPX_TRACE ("arg2", sp+1);
+            MPX_TRACE ("arg3", sp+1));
+
+      switch (CP->op->type & MPEXPR_TYPE_MASK_ARGSTYLE) {
+      case 0:
+       (* (mpexpr_fun_ternary_t) CP->op->fun) (sp, sp, sp+1, sp+2);
+       break;
+      case MPEXPR_TYPE_LAST_UI:
+       CHECK_UI (sp+2);
+       (* (mpexpr_fun_ternary_ui_t) CP->op->fun)
+         (sp, sp, sp+1, (*p->mpX_get_ui) (sp+2));
+       break;
+      case MPEXPR_TYPE_RESULT_INT:
+       (*p->mpX_set_si)
+         (sp,
+          (long) (* (mpexpr_fun_i_ternary_t) CP->op->fun)
+          (sp, sp+1, sp+2));
+       break;
+      case MPEXPR_TYPE_LAST_UI | MPEXPR_TYPE_RESULT_INT:
+       CHECK_UI (sp+2);
+       (*p->mpX_set_si)
+         (sp,
+          (long) (* (mpexpr_fun_i_ternary_ui_t) CP->op->fun)
+          (sp, sp+1, (*p->mpX_get_ui) (sp+2)));
+       break;
+      default:
+       ERROR ("unrecognised binary argument calling style",
+              MPEXPR_RESULT_BAD_TABLE);
+      }
+    }
+    break;
+
+  default:
+    TRACE (printf ("unrecognised operator type: 0x%X\n", CP->op->type));
+    ERROR ("", MPEXPR_RESULT_PARSE_ERROR);
+  }
+
+ apply_control_done:
+  TRACE (MPX_TRACE ("result", SP));
+  CONTROL_POP ();
+  goto another_operator;
+
+ done:
+  if (p->error_code == MPEXPR_RESULT_OK)
+    {
+      if (p->data_top != 0)
+       {
+         TRACE (printf ("data stack want top at 0, got %d\n", p->data_top));
+         p->error_code = MPEXPR_RESULT_PARSE_ERROR;
+       }
+      else
+       (*p->mpX_set_or_swap) (p->res, SP);
+    }
+
+  {
+    int  i;
+    for (i = 0; i < p->data_inited; i++)
+      {
+       TRACE (printf ("clear %d\n", i));
+       (*p->mpX_clear) (p->data_stack+i);
+      }
+  }
+
+  FREE_FUNC_TYPE (p->data_stack, p->data_alloc, union mpX_t);
+  FREE_FUNC_TYPE (p->control_stack, p->control_alloc, struct mpexpr_control_t);
+
+  return p->error_code;
+}