2008-06-25 03:33:36 -04:00
|
|
|
/* 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 2.1 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; see the file COPYING.LIB. If not, write to
|
|
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
|
|
MA 02110-1301, USA. */
|
|
|
|
|
|
|
|
#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;
|
|
|
|
}
|