605 lines
15 KiB
C
605 lines
15 KiB
C
/* #includes */ /*{{{C}}}*//*{{{*/
|
|
#ifndef NO_POSIX_SOURCE
|
|
#undef _POSIX_SOURCE
|
|
#define _POSIX_SOURCE 1
|
|
#undef _POSIX_C_SOURCE
|
|
#define _POSIX_C_SOURCE 2
|
|
#endif
|
|
|
|
#ifdef DMALLOC
|
|
#include "dmalloc.h"
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <float.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
extern char *strdup(const char* s);
|
|
#include <string.h>
|
|
|
|
|
|
#include "eval.h"
|
|
#include "func.h"
|
|
#include "main.h"
|
|
#include "misc.h"
|
|
#include "parser.h"
|
|
#include "scanner.h"
|
|
#include "sheet.h"
|
|
/*}}}*/
|
|
/* #defines */ /*{{{*/
|
|
#define MAXARGC 16
|
|
/*}}}*/
|
|
|
|
/* prototypes */ /*{{{*/
|
|
static Token term(Token *n[], int *i, EvalMethod meth);
|
|
/*}}}*/
|
|
|
|
/* full_eval_funcall -- evaluate the args of a funcall token and then
|
|
call the function on them
|
|
*/
|
|
static Token full_eval_funcall(Token *t)
|
|
{
|
|
assert(t->type == FUNCALL);
|
|
if (t->u.funcall.argc < 1)
|
|
return tfuncall(t->u.funcall.fident, t->u.funcall.argc, 0);
|
|
Token *eval_argv = malloc(t->u.funcall.argc*sizeof(Token));
|
|
for (size_t ai = 0; ai < t->u.funcall.argc; ++ai) {
|
|
eval_argv[ai] = evaltoken(t->u.funcall.argv[ai], FULL);
|
|
}
|
|
Token result = tfuncall(t->u.funcall.fident, t->u.funcall.argc, eval_argv);
|
|
/* To allow a function to return one of its arguments, we need
|
|
to be sure not to free that argument: */
|
|
for (size_t ai = 0; ai < t->u.funcall.argc; ++ai)
|
|
tfree_protected(&eval_argv[ai], result);
|
|
free(eval_argv);
|
|
return result;
|
|
}
|
|
|
|
/* primary -- parse and evaluate a primary term */ /*{{{*/
|
|
static Token primary(Token *n[], int *i, EvalMethod meth)
|
|
{
|
|
/* variables */ /*{{{*/
|
|
int argc;
|
|
int fident = -2;
|
|
Token *ident,argv[MAXARGC],result;
|
|
/*}}}*/
|
|
|
|
if (n[*i] == NULLTOKEN)
|
|
/* error */ /*{{{*/
|
|
{
|
|
duperror(&result, _("missing operator"));
|
|
return result;
|
|
}
|
|
/*}}}*/
|
|
switch (n[*i]->type)
|
|
{
|
|
/* STRING, FLOAT, INT */ /*{{{*/
|
|
case STRING:
|
|
case FLOAT:
|
|
case INT:
|
|
return tcopy(*n[(*i)++]);
|
|
/*}}}*/
|
|
|
|
/* LIDENT */ /*{{{*/
|
|
case LIDENT:
|
|
{
|
|
ident = n[*i];
|
|
++(*i);
|
|
if (meth == FULL) return findlabel(upd_sheet,ident->u.lident);
|
|
return tcopy(*ident);
|
|
}
|
|
/*}}}*/
|
|
|
|
/* OPERATOR */ /*{{{*/
|
|
case OPERATOR:
|
|
{
|
|
switch (n[*i]->u.op)
|
|
{
|
|
case OP: /* return paren term */ /*{{{*/
|
|
++(*i);
|
|
result = term(n, i, meth);
|
|
if (result.type == EEK) return result;
|
|
if (n[*i] != NULLTOKEN && n[*i]->type == OPERATOR && n[*i]->u.op==CP)
|
|
{
|
|
++(*i);
|
|
return result;
|
|
}
|
|
tfree(&result);
|
|
duperror(&result, _(") expected"));
|
|
return result;
|
|
/*}}}*/
|
|
/* Unary minus will be handled in powterm */
|
|
case CP:
|
|
duperror(&result, _("Extra umatched ')'"));
|
|
return result;
|
|
case COMMA:
|
|
duperror(&result, _("Occurrence of ',' outside parameter list"));
|
|
return result;
|
|
default:
|
|
/* Can also use any infix symbol as a function, but only with parens, not
|
|
bare */
|
|
if (n[(*i)+1] == NULLTOKEN || n[(*i)+1]->type != OPERATOR
|
|
|| n[(*i)+1]->u.op != OP)
|
|
{
|
|
const char *templ = "To use %s as function symbol, must use %s(...)";
|
|
result.type = EEK;
|
|
result.u.err = malloc(strlen(templ) + 2 * MAX_OP_NAME_LENGTH + 1);
|
|
sprintf(result.u.err, templ,
|
|
Op_Name[n[*i]->u.op], Op_Name[n[*i]->u.op]);
|
|
return result;
|
|
}
|
|
fident = identcode(Op_Name[n[*i]->u.op], strlen(Op_Name[n[*i]->u.op]));
|
|
/* FALL THROUGH TO PROCESS OPERATOR AS FUNCTION CALL */
|
|
}
|
|
}
|
|
/*}}}*/
|
|
|
|
/* FIDENT */ /*{{{*/
|
|
case FIDENT:
|
|
{
|
|
if (fident == -2) fident = n[*i]->u.fident;
|
|
++(*i);
|
|
if (n[*i] == NULLTOKEN || n[*i]->type != OPERATOR
|
|
|| n[*i]->u.op != OP)
|
|
argc = -1;
|
|
else /* parse arguments and closing paren of function call */ /*{{{*/
|
|
{
|
|
++(*i);
|
|
argc = 0;
|
|
if (!(n[*i] != NULLTOKEN && n[*i]->type==OPERATOR && n[*i]->u.op==CP))
|
|
/* parse at least one argument */ /*{{{*/
|
|
{
|
|
if (n[*i] != NULLTOKEN && n[*i]->type==OPERATOR && n[*i]->u.op==COMMA)
|
|
/* empty argument */ /*{{{*/
|
|
{
|
|
argv[argc].type = EMPTY;
|
|
}
|
|
/*}}}*/
|
|
else argv[argc] = term(n, i, meth);
|
|
if (argv[argc].type == EEK) return argv[argc];
|
|
++argc;
|
|
while (n[*i] != NULLTOKEN && n[*i]->type == OPERATOR
|
|
&& n[*i]->u.op == COMMA)
|
|
/* parse the following argument */ /*{{{*/
|
|
{
|
|
++(*i);
|
|
if (argc < MAXARGC)
|
|
{
|
|
if (n[*i] != NULLTOKEN && n[*i]->type == OPERATOR &&
|
|
(n[*i]->u.op == COMMA || n[*i]->u.op == CP))
|
|
{
|
|
argv[argc].type=EMPTY;
|
|
} else {
|
|
argv[argc] = term(n, i, meth);
|
|
if (argv[argc].type == EEK) {
|
|
for (size_t pa = 0; pa < argc; +pa) tfree(argv + pa);
|
|
return argv[argc];
|
|
}
|
|
}
|
|
} else {
|
|
duperror(&result, _("too many arguments"));
|
|
for (size_t j=0; j < argc; ++j) tfree(&argv[j]);
|
|
return result;
|
|
}
|
|
++argc;
|
|
}
|
|
/*}}}*/
|
|
}
|
|
/*}}}*/
|
|
if (n[*i] == NULLTOKEN || n[*i]->type != OPERATOR || n[*i]->u.op != CP)
|
|
/* ) expected */ /*{{{*/
|
|
{
|
|
for (size_t j = 0; j < argc; ++j) tfree(&argv[j]);
|
|
duperror(&result, _(") expected"));
|
|
return result;
|
|
}
|
|
++(*i);
|
|
}
|
|
if (meth == FULL)
|
|
{
|
|
result = tfuncall(fident, argc, argv);
|
|
/* To allow a function to return one of its arguments, we need
|
|
to be sure not to free that argument: */
|
|
for (size_t j = 0; j < argc; ++j) tfree_protected(&argv[j], result);
|
|
return result;
|
|
}
|
|
result.type = FUNCALL;
|
|
result.u.funcall.fident = fident;
|
|
result.u.funcall.argc = argc;
|
|
if (argc > 0)
|
|
{
|
|
result.u.funcall.argv = malloc(argc*sizeof(Token));
|
|
for (size_t ai = 0; ai < argc; ++ai)
|
|
result.u.funcall.argv[ai] = argv[ai];
|
|
} else result.u.funcall.argv = NULLTOKEN;
|
|
return result;
|
|
}
|
|
/*}}}*/
|
|
|
|
/* FUNCALL */ /*{{{*/
|
|
case FUNCALL:
|
|
if (meth == FULL) result = full_eval_funcall(n[*i]);
|
|
else result = tcopy(*n[*i]);
|
|
++(*i);
|
|
return result;
|
|
/*}}}*/
|
|
|
|
default: ; /* fall through */
|
|
}
|
|
duperror(&result, _("value expected"));
|
|
return result;
|
|
}
|
|
/*}}}*/
|
|
|
|
/* powterm -- parse and evaluate a x^y term */ /*{{{*/
|
|
static Token powterm(Token *n[], int *i, EvalMethod meth)
|
|
{
|
|
Token l;
|
|
size_t npows = 0;
|
|
|
|
if (n[*i] != NULLTOKEN && n[*i]->type == OPERATOR && n[*i]->u.op == MINUS)
|
|
{
|
|
/* A - symbol here is a pain. If it is being used as a function symbol, it
|
|
is higher precedence than exponentiation. If it is unary negation,
|
|
then it's lower precedence and we have to grab a powterm to the right,
|
|
and negate it. As far as I can tell the only way to tell is to
|
|
look ahead a term to see if there's a comma...
|
|
*/
|
|
bool unaryneg = true;
|
|
int j = *i + 1;
|
|
if (n[j] == NULLTOKEN)
|
|
return duperror(&l, _("A bare - is not a valid expression"));
|
|
if (n[j]->type == OPERATOR && n[j]->u.op == OP)
|
|
{
|
|
++j;
|
|
Token dummy = term(n, &j, meth);
|
|
if (n[j] != NULLTOKEN && n[j]->type == OPERATOR && n[j]->u.op == COMMA)
|
|
unaryneg = false;
|
|
tfree(&dummy);
|
|
}
|
|
if (unaryneg)
|
|
{
|
|
++(*i);
|
|
l = powterm(n, i, meth);
|
|
if (meth == FULL) return tneg(l);
|
|
if (l.type == EEK) return l;
|
|
if (TOKISNUM(l)) return tneg(l);
|
|
Token result;
|
|
result.type = FUNCALL;
|
|
result.u.funcall.fident = FUNC_NEGATE;
|
|
result.u.funcall.argc = 1;
|
|
result.u.funcall.argv = malloc(sizeof(Token));
|
|
result.u.funcall.argv[0] = l;
|
|
return result;
|
|
}
|
|
}
|
|
l = primary(n, i, meth);
|
|
if (l.type == EEK) return l;
|
|
while (n[*i] != NULLTOKEN && n[*i]->type == OPERATOR && n[*i]->u.op == POW)
|
|
{
|
|
Token r;
|
|
|
|
++(*i);
|
|
r = primary(n,i,meth);
|
|
if (meth == FULL)
|
|
{
|
|
Token result = tpow(l,r);
|
|
tfree(&l);
|
|
tfree(&r);
|
|
if (result.type == EEK) return result;
|
|
l = result;
|
|
} else {
|
|
if (r.type == EEK)
|
|
{
|
|
tfree(&l);
|
|
return r;
|
|
}
|
|
if (npows == 0)
|
|
{
|
|
Token tmp = l;
|
|
l.type = FUNCALL;
|
|
l.u.funcall.fident = FUNC_CARET;
|
|
l.u.funcall.argc = 1;
|
|
l.u.funcall.argv = malloc(MAXARGC * sizeof(Token));
|
|
l.u.funcall.argv[0] = tmp;
|
|
}
|
|
if (npows + 1 >= MAXARGC)
|
|
{
|
|
tfree(&l);
|
|
tfree(&r);
|
|
duperror(&l, _("Exceeded maximum sequence length of ^"));
|
|
return l;
|
|
}
|
|
++npows; ++(l.u.funcall.argc);
|
|
l.u.funcall.argv[npows] = r;
|
|
}
|
|
}
|
|
return l;
|
|
}
|
|
/*}}}*/
|
|
|
|
/* piterm -- parse and evaluate a product/division/modulo term */ /*{{{*/
|
|
static Token piterm(Token *n[], int *i, EvalMethod meth)
|
|
{
|
|
FunctionIdentifier mulident = FUNC_ASTERISK;
|
|
Token l;
|
|
Operator op = CP;
|
|
bool first_funcall = true;
|
|
|
|
l = powterm(n, i, meth);
|
|
if (l.type == EEK) return l;
|
|
if (n[*i] != NULLTOKEN && n[*i]->type == OPERATOR) op = n[*i]->u.op;
|
|
|
|
while (op == DIV || op == MUL || op == MOD)
|
|
{
|
|
Token r;
|
|
|
|
++(*i);
|
|
r = powterm(n, i, meth);
|
|
if (meth == FULL)
|
|
{
|
|
Token result;
|
|
switch (op)
|
|
{
|
|
case MUL: result = tmul(l,r); break;
|
|
case DIV: result = tdiv(l,r); break;
|
|
case MOD: result = tmod(l,r); break;
|
|
default: assert(0);
|
|
}
|
|
tfree(&l);
|
|
tfree(&r);
|
|
if (result.type == EEK) return result;
|
|
l = result;
|
|
} else {
|
|
if (r.type == EEK)
|
|
{
|
|
tfree(&l);
|
|
return r;
|
|
}
|
|
if (first_funcall || l.u.funcall.fident != mulident || op != MUL)
|
|
{
|
|
first_funcall = false;
|
|
Token tmp = l;
|
|
l.type = FUNCALL;
|
|
l.u.funcall.fident = identcode(Op_Name[op], strlen(Op_Name[op]));
|
|
l.u.funcall.argc = 2;
|
|
if (op == MUL) l.u.funcall.argv = malloc(MAXARGC * sizeof(Token));
|
|
else l.u.funcall.argv = malloc(2*sizeof(Token));
|
|
l.u.funcall.argv[0] = tmp;
|
|
l.u.funcall.argv[1] = r;
|
|
} else {
|
|
if (l.u.funcall.argc >= MAXARGC)
|
|
{
|
|
tfree(&r);
|
|
tfree(&l);
|
|
duperror(&l, _("Exceeded maximum sequence length of *"));
|
|
return l;
|
|
}
|
|
l.u.funcall.argv[(l.u.funcall.argc)++] = r;
|
|
}
|
|
}
|
|
if (n[*i] != NULLTOKEN && n[*i]->type == OPERATOR) op = n[*i]->u.op;
|
|
else op = CP;
|
|
}
|
|
return l;
|
|
}
|
|
/*}}}*/
|
|
|
|
/* factor -- parse and evaluate a factor of sums/differences */ /*{{{*/
|
|
static Token factor(Token *n[], int *i, EvalMethod meth)
|
|
{
|
|
FunctionIdentifier plusident = FUNC_PLUS_SYMBOL;
|
|
Token l;
|
|
Operator op = CP;
|
|
bool first_funcall = true;
|
|
|
|
l = piterm(n, i, meth);
|
|
if (l.type == EEK) return l;
|
|
if (n[*i] != NULLTOKEN && n[*i]->type == OPERATOR) op = n[*i]->u.op;
|
|
|
|
while (op == PLUS || op == MINUS)
|
|
{
|
|
Token r;
|
|
|
|
++(*i);
|
|
r = piterm(n, i, meth);
|
|
if (meth == FULL)
|
|
{
|
|
Token result = (op==PLUS ? tadd(l,r) : tsub(l,r));
|
|
tfree(&l);
|
|
tfree(&r);
|
|
if (result.type == EEK) return result;
|
|
l = result;
|
|
} else {
|
|
if (r.type == EEK)
|
|
{
|
|
tfree(&l);
|
|
return r;
|
|
}
|
|
if (first_funcall || l.u.funcall.fident != plusident || op != PLUS)
|
|
{
|
|
first_funcall = false;
|
|
Token tmp = l;
|
|
l.type = FUNCALL;
|
|
l.u.funcall.fident = identcode(Op_Name[op], strlen(Op_Name[op]));
|
|
l.u.funcall.argc = 2;
|
|
if (op == PLUS) l.u.funcall.argv = malloc(MAXARGC * sizeof(Token));
|
|
else l.u.funcall.argv = malloc(2*sizeof(Token));
|
|
l.u.funcall.argv[0] = tmp;
|
|
l.u.funcall.argv[1] = r;
|
|
} else {
|
|
if (l.u.funcall.argc >= MAXARGC)
|
|
{
|
|
tfree(&r);
|
|
tfree(&l);
|
|
duperror(&l, _("Exceeded maximum sequence length of +"));
|
|
return l;
|
|
}
|
|
l.u.funcall.argv[(l.u.funcall.argc)++] = r;
|
|
}
|
|
}
|
|
if (n[*i] != NULLTOKEN && n[*i]->type == OPERATOR) op = n[*i]->u.op;
|
|
else op = CP;
|
|
}
|
|
return l;
|
|
}
|
|
/*}}}*/
|
|
|
|
/* term -- parse and evaluate a relational term */ /*{{{*/
|
|
static Token term(Token *n[], int *i, EvalMethod meth)
|
|
{
|
|
Token l = factor(n, i, meth);
|
|
if (l.type == EEK) return l;
|
|
/* a < b < c used to mean (a < b) < c, but that does not make sense really
|
|
because there is not an ordering on bools (if we had a separate bool
|
|
type). So restrict to a single binary relation; one can still use parens
|
|
to get the old, odd behavior */
|
|
if (n[*i] != NULLTOKEN && n[*i]->type == OPERATOR
|
|
&& n[*i]->u.op >= LT && n[*i]->u.op <= NE)
|
|
{
|
|
Operator op = n[*i]->u.op;
|
|
Token result, r;
|
|
|
|
++(*i);
|
|
r = factor(n, i, meth);
|
|
if (meth == FULL)
|
|
{
|
|
switch (op)
|
|
{
|
|
case LT: result=tlt(l,r); break;
|
|
case LE: result=tle(l,r); break;
|
|
case GE: result=tge(l,r); break;
|
|
case GT: result=tgt(l,r); break;
|
|
case ISEQUAL: result=teq(l,r); break;
|
|
case ABOUTEQ: result=tabouteq(l,r); break;
|
|
case NE: result=tne(l,r); break;
|
|
default: assert(0);
|
|
}
|
|
tfree(&l);
|
|
tfree(&r);
|
|
if (result.type == EEK) return result;
|
|
l = result;
|
|
} else {
|
|
if (r.type == EEK)
|
|
{
|
|
tfree(&l);
|
|
return r;
|
|
}
|
|
Token tmp = l;
|
|
l.type = FUNCALL;
|
|
l.u.funcall.fident = identcode(Op_Name[op], strlen(Op_Name[op]));
|
|
l.u.funcall.argc = 2;
|
|
l.u.funcall.argv = malloc(2*sizeof(Token));
|
|
l.u.funcall.argv[0] = tmp;
|
|
l.u.funcall.argv[1] = r;
|
|
}
|
|
}
|
|
return l;
|
|
}
|
|
/*}}}*/
|
|
|
|
/* eval -- parse and evaluate nonempty token sequence
|
|
if the sequence might be empty, use eval_safe. */ /*{{{*/
|
|
Token eval(Token **n, EvalMethod meth)
|
|
{
|
|
Token l;
|
|
int i = 0;
|
|
bool first_funcall = true;
|
|
|
|
assert(meth == LITERAL || upd_sheet != (Sheet*)0);
|
|
l = term(n, &i, meth);
|
|
if (l.type == EEK) return l;
|
|
|
|
while (n[i] != NULLTOKEN)
|
|
{
|
|
Token r = term(n, &i, meth);
|
|
|
|
if (meth == FULL)
|
|
{
|
|
Token result = tconcat(l,r);
|
|
tfree(&l);
|
|
tfree(&r);
|
|
if (result.type == EEK) return result;
|
|
l = result;
|
|
} else {
|
|
if (r.type == EEK)
|
|
{
|
|
tfree(&l);
|
|
return r;
|
|
}
|
|
if (first_funcall)
|
|
{
|
|
first_funcall = false;
|
|
Token tmp = l;
|
|
l.type = FUNCALL;
|
|
l.u.funcall.fident = FUNC_CONCAT;
|
|
l.u.funcall.argc = 1;
|
|
l.u.funcall.argv = malloc(MAXARGC*sizeof(Token));
|
|
l.u.funcall.argv[0] = tmp;
|
|
}
|
|
if (l.u.funcall.argc >= MAXARGC)
|
|
{
|
|
tfree(&l);
|
|
tfree(&r);
|
|
duperror(&l, _("Exceeded max sequence lentgh of concatenated terms"));
|
|
return l;
|
|
}
|
|
l.u.funcall.argv[(l.u.funcall.argc)++] = r;
|
|
}
|
|
}
|
|
return l;
|
|
}
|
|
/*}}}*/
|
|
|
|
/* eval_safe -- like eval, but handles null pointer to token sequence */ /*{{{*/
|
|
Token eval_safe(Token **n, EvalMethod meth)
|
|
{
|
|
Token result;
|
|
if (n == EMPTY_TVEC || *n == NULLTOKEN)
|
|
{
|
|
result.type = EMPTY;
|
|
return result;
|
|
}
|
|
return eval(n, meth);
|
|
}
|
|
|
|
/* evaltoken -- like eval, but evaluate a single token
|
|
NOTE: caller owns the result and must arrrange that it be
|
|
eventually tfree()ed */ /*{{{*/
|
|
Token evaltoken(Token n, EvalMethod meth)
|
|
{
|
|
if (meth == LITERAL) return tcopy(n);
|
|
switch (n.type) {
|
|
case EMPTY: return n;
|
|
case STRING: return tcopy(n);
|
|
case FLOAT:
|
|
case INT:
|
|
return n;
|
|
case OPERATOR: {
|
|
Token err;
|
|
err.type = EEK;
|
|
const char *templ = _("Attempt to eval bare operator token: ");
|
|
err.u.err = malloc(strlen(templ) + MAX_OP_NAME_LENGTH + 2);
|
|
strcpy(err.u.err, templ);
|
|
strcat(err.u.err, Op_Name[n.u.op]);
|
|
return err;
|
|
}
|
|
case LIDENT: return findlabel(upd_sheet, n.u.lident);
|
|
case FIDENT: {
|
|
Token err;
|
|
err.type = EEK;
|
|
const char *templ = _("Attempt to eval bare function identifier token: ");
|
|
err.u.err = malloc(strlen(templ) + MAX_FUNC_NAME_LENGTH + 2);
|
|
strcpy(err.u.err, templ);
|
|
strcat(err.u.err, tfunc[n.u.fident].name);
|
|
return err;
|
|
}
|
|
case LOCATION: return n;
|
|
case FUNCALL: return full_eval_funcall(&n);
|
|
case EEK: return tcopy(n);
|
|
default: assert(0);
|
|
}
|
|
return n;
|
|
}
|