2002-03-17 06:57:03 +00:00
|
|
|
/*
|
|
|
|
|
|
|
|
EXP equation evaluation routines
|
|
|
|
Copyright (C) 2000, 2001 Brian Koropoff
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU General Public License
|
|
|
|
as published by the Free Software Foundation; either version 2
|
|
|
|
of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This program 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 General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, US
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
2002-04-07 04:57:42 +00:00
|
|
|
#include "QF/qtypes.h"
|
2002-11-13 03:03:37 +00:00
|
|
|
#include "QF/va.h"
|
2002-04-07 04:57:42 +00:00
|
|
|
|
2002-05-27 20:28:10 +00:00
|
|
|
#include "exp.h"
|
|
|
|
#include "ops.h"
|
|
|
|
|
2002-03-17 06:57:03 +00:00
|
|
|
exp_error_t EXP_ERROR;
|
2002-11-13 03:03:37 +00:00
|
|
|
char *exp_error_msg = 0;
|
2002-03-17 06:57:03 +00:00
|
|
|
|
|
|
|
optable_t optable[] =
|
|
|
|
{
|
2002-04-24 22:33:04 +00:00
|
|
|
{"!", OP_Not, 1},
|
|
|
|
{"**", OP_Exp, 2},
|
|
|
|
{"/", OP_Div, 2},
|
2002-08-23 21:18:00 +00:00
|
|
|
{"-", OP_Negate, 1},
|
2002-04-24 22:33:04 +00:00
|
|
|
{"*", OP_Mult, 2},
|
|
|
|
{"+", OP_Add, 2},
|
|
|
|
{"-", OP_Sub, 2},
|
|
|
|
{"==", OP_Eq, 2},
|
|
|
|
{"!=", OP_Neq, 2},
|
|
|
|
{">=", OP_GreaterThanEqual, 2},
|
|
|
|
{">", OP_GreaterThan, 2},
|
|
|
|
{"<=", OP_LessThanEqual, 2},
|
|
|
|
{"<", OP_LessThan, 2},
|
|
|
|
{"||", OP_Or, 2},
|
|
|
|
{"or", OP_Or, 2},
|
|
|
|
{"&&", OP_And, 2},
|
|
|
|
{"and", OP_And, 2},
|
2002-04-28 08:30:54 +00:00
|
|
|
{"", 0, 0}
|
|
|
|
};
|
|
|
|
|
|
|
|
functable_t functable[] =
|
|
|
|
{
|
2002-12-08 20:55:40 +00:00
|
|
|
{"sqrt", Func_Sqrt, 1},
|
|
|
|
{"abs", Func_Abs, 1},
|
2002-04-28 08:30:54 +00:00
|
|
|
{"sin", Func_Sin, 1},
|
|
|
|
{"cos", Func_Cos, 1},
|
|
|
|
{"tan", Func_Tan, 1},
|
|
|
|
{"asin", Func_Asin, 1},
|
|
|
|
{"acos", Func_Acos, 1},
|
|
|
|
{"atan", Func_Atan, 1},
|
|
|
|
{"", 0, 0}
|
2002-03-17 06:57:03 +00:00
|
|
|
};
|
|
|
|
|
2002-11-13 03:03:37 +00:00
|
|
|
// Error handling
|
2003-01-06 18:28:13 +00:00
|
|
|
static exp_error_t
|
2002-11-13 03:03:37 +00:00
|
|
|
EXP_Error (exp_error_t err, const char *msg)
|
|
|
|
{
|
|
|
|
EXP_ERROR = err;
|
|
|
|
if (exp_error_msg)
|
|
|
|
free (exp_error_msg);
|
|
|
|
exp_error_msg = strdup(msg);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *
|
|
|
|
EXP_GetErrorMsg (void)
|
|
|
|
{
|
|
|
|
return exp_error_msg;
|
|
|
|
}
|
|
|
|
|
2003-01-06 18:28:13 +00:00
|
|
|
static token
|
2002-11-05 19:12:51 +00:00
|
|
|
*EXP_NewToken (void)
|
2002-03-17 06:57:03 +00:00
|
|
|
{
|
|
|
|
token *new;
|
|
|
|
|
|
|
|
new = malloc(sizeof(token));
|
|
|
|
|
|
|
|
if (!new)
|
|
|
|
return 0;
|
|
|
|
new->generic.type = TOKEN_GENERIC;
|
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
2002-04-28 08:30:54 +00:00
|
|
|
/*
|
2002-11-05 19:12:51 +00:00
|
|
|
int
|
|
|
|
EXP_FindIndexByFunc (opfunc func)
|
2002-04-24 22:33:04 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; optable[i].func; i++)
|
|
|
|
if (func == optable[i].func)
|
|
|
|
return i;
|
|
|
|
return -1;
|
|
|
|
}
|
2002-04-28 08:30:54 +00:00
|
|
|
*/
|
2002-04-24 22:33:04 +00:00
|
|
|
|
2003-01-06 18:28:13 +00:00
|
|
|
static optable_t *
|
2002-11-05 19:12:51 +00:00
|
|
|
EXP_FindOpByStr (const char *str)
|
2002-04-24 22:33:04 +00:00
|
|
|
{
|
|
|
|
int i, len, fi;
|
|
|
|
|
|
|
|
for (i = 0, len = 0, fi = -1; optable[i].func; i++)
|
|
|
|
if (!strncmp(str, optable[i].str, strlen(optable[i].str)) && strlen(optable[i].str) > len) {
|
|
|
|
len = strlen(optable[i].str);
|
|
|
|
fi = i;
|
|
|
|
}
|
2002-04-28 08:30:54 +00:00
|
|
|
if (fi >= 0)
|
|
|
|
return optable+fi;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2003-01-06 18:28:13 +00:00
|
|
|
static functable_t *
|
2002-11-05 19:12:51 +00:00
|
|
|
EXP_FindFuncByStr (const char *str)
|
2002-04-28 08:30:54 +00:00
|
|
|
{
|
|
|
|
int i, len, fi;
|
|
|
|
|
|
|
|
for (i = 0, len = 0, fi = -1; functable[i].func; i++)
|
|
|
|
if (!strncmp(str, functable[i].str, strlen(functable[i].str)) && strlen(functable[i].str) > len) {
|
|
|
|
len = strlen(functable[i].str);
|
|
|
|
fi = i;
|
|
|
|
}
|
|
|
|
if (fi >= 0)
|
|
|
|
return functable+fi;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2003-01-06 18:28:13 +00:00
|
|
|
static int
|
2002-11-05 19:12:51 +00:00
|
|
|
EXP_ContainsCommas (token *chain)
|
2002-04-28 08:30:54 +00:00
|
|
|
{
|
|
|
|
token *cur;
|
|
|
|
int paren = 0;
|
|
|
|
for (cur = chain; cur; cur = cur->generic.next) {
|
|
|
|
if (cur->generic.type == TOKEN_OPAREN)
|
|
|
|
paren++;
|
|
|
|
if (cur->generic.type == TOKEN_CPAREN)
|
|
|
|
paren--;
|
|
|
|
if (!paren)
|
|
|
|
return 0;
|
|
|
|
if (cur->generic.type == TOKEN_COMMA)
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return -1; // We should never get here
|
|
|
|
}
|
|
|
|
|
2003-01-06 18:28:13 +00:00
|
|
|
static int
|
2002-11-05 19:12:51 +00:00
|
|
|
EXP_DoFunction (token *chain)
|
2002-04-28 08:30:54 +00:00
|
|
|
{
|
|
|
|
token *cur, *temp;
|
|
|
|
double *oplist = 0;
|
|
|
|
double value;
|
|
|
|
unsigned int numops = 0;
|
|
|
|
|
|
|
|
|
|
|
|
for (cur = chain->generic.next; cur; cur = temp) {
|
|
|
|
if (cur->generic.type != TOKEN_CPAREN)
|
|
|
|
temp = cur->generic.next;
|
|
|
|
else
|
|
|
|
temp = 0;
|
|
|
|
if (cur->generic.type == TOKEN_NUM) {
|
|
|
|
numops++;
|
|
|
|
oplist = realloc(oplist, sizeof(double)*numops);
|
|
|
|
oplist[numops-1] = cur->num.value;
|
|
|
|
}
|
|
|
|
EXP_RemoveToken (cur);
|
|
|
|
}
|
|
|
|
if (numops == chain->func.func->operands) {
|
|
|
|
value = chain->func.func->func(oplist, numops); // Heh
|
|
|
|
chain->generic.type = TOKEN_NUM;
|
|
|
|
chain->num.value = value;
|
|
|
|
if (oplist)
|
|
|
|
free (oplist);
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
if (oplist)
|
|
|
|
free (oplist);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return -2; // We shouldn't get here
|
2002-04-24 22:33:04 +00:00
|
|
|
}
|
|
|
|
|
2003-01-06 18:28:13 +00:00
|
|
|
static int
|
2002-11-05 19:12:51 +00:00
|
|
|
EXP_DoUnary (token *chain)
|
2002-08-23 21:18:00 +00:00
|
|
|
{
|
|
|
|
if (chain->generic.next->generic.type == TOKEN_OP)
|
|
|
|
EXP_DoUnary (chain->generic.next);
|
|
|
|
if (chain->generic.next->generic.type != TOKEN_NUM)
|
|
|
|
return -1; // In theory, this should never happen
|
|
|
|
chain->generic.next->num.value = chain->op.op->func(chain->generic.next->num.value, 0);
|
|
|
|
EXP_RemoveToken (chain);
|
|
|
|
return 0;
|
|
|
|
}
|
2002-04-28 08:30:54 +00:00
|
|
|
|
2002-11-05 19:12:51 +00:00
|
|
|
token *
|
|
|
|
EXP_ParseString (char *str)
|
2002-03-17 06:57:03 +00:00
|
|
|
{
|
|
|
|
char buf[256];
|
2002-04-24 22:33:04 +00:00
|
|
|
|
2002-03-17 06:57:03 +00:00
|
|
|
token *chain, *new, *cur;
|
2002-04-28 08:30:54 +00:00
|
|
|
int i,m;
|
|
|
|
optable_t *op;
|
|
|
|
functable_t *func;
|
2002-04-24 22:33:04 +00:00
|
|
|
|
2002-03-17 06:57:03 +00:00
|
|
|
cur = chain = EXP_NewToken();
|
|
|
|
chain->generic.type = TOKEN_OPAREN;
|
|
|
|
chain->generic.prev = 0;
|
|
|
|
chain->generic.next = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < strlen(str); i++)
|
|
|
|
{
|
|
|
|
m = 0;
|
2002-04-24 22:33:04 +00:00
|
|
|
while(isspace((byte)str[i]))
|
2002-03-17 06:57:03 +00:00
|
|
|
i++;
|
2002-04-24 22:33:04 +00:00
|
|
|
if (!str[i])
|
|
|
|
break;
|
2002-04-07 04:54:19 +00:00
|
|
|
if (isdigit((byte)str[i]) || str[i] == '.')
|
2002-03-17 06:57:03 +00:00
|
|
|
{
|
2002-04-28 08:30:54 +00:00
|
|
|
while ((isdigit((byte)str[i]) // A number
|
|
|
|
|| str[i] == '.' // A decimal point
|
|
|
|
|| str[i] == 'e' // An exponent
|
|
|
|
|| ((str[i] == '-' || str[i] == '+') && str[i-1] == 'e')) // A + or - after an exponent
|
|
|
|
&& i < strlen(str) // We are within the string
|
|
|
|
&& m < 256) // And there is space in the buffer
|
2002-03-17 06:57:03 +00:00
|
|
|
buf[m++] = str[i++];
|
|
|
|
buf[m] = 0;
|
|
|
|
new = EXP_NewToken();
|
|
|
|
new->generic.type = TOKEN_NUM;
|
|
|
|
new->num.value = atof(buf);
|
|
|
|
new->generic.prev = cur;
|
|
|
|
new->generic.next = 0;
|
|
|
|
cur->generic.next = new;
|
|
|
|
cur = new;
|
|
|
|
i--;
|
|
|
|
}
|
2002-04-28 08:30:54 +00:00
|
|
|
else if (str[i] == ',')
|
|
|
|
{
|
|
|
|
new = EXP_NewToken();
|
|
|
|
new->generic.type = TOKEN_COMMA;
|
|
|
|
new->generic.prev = cur;
|
|
|
|
new->generic.next = 0;
|
|
|
|
cur->generic.next = new;
|
|
|
|
cur = new;
|
|
|
|
}
|
2002-03-17 06:57:03 +00:00
|
|
|
else if (str[i] == '(')
|
|
|
|
{
|
|
|
|
new = EXP_NewToken();
|
|
|
|
new->generic.type = TOKEN_OPAREN;
|
|
|
|
new->generic.prev = cur;
|
|
|
|
new->generic.next = 0;
|
|
|
|
cur->generic.next = new;
|
|
|
|
cur = new;
|
|
|
|
}
|
|
|
|
else if (str[i] == ')')
|
|
|
|
{
|
|
|
|
new = EXP_NewToken();
|
|
|
|
new->generic.type = TOKEN_CPAREN;
|
|
|
|
new->generic.prev = cur;
|
|
|
|
new->generic.next = 0;
|
|
|
|
cur->generic.next = new;
|
|
|
|
cur = new;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2002-04-28 08:30:54 +00:00
|
|
|
while(!(isdigit((byte)str[i])) && !isspace((byte)str[i])
|
|
|
|
&& str[i] != '.' && str[i] != '(' && str[i] != ')'
|
|
|
|
&& str[i] != ',' && m < 256) {
|
2002-04-24 22:33:04 +00:00
|
|
|
buf[m++] = str[i++];
|
|
|
|
}
|
2002-03-17 06:57:03 +00:00
|
|
|
buf[m] = 0;
|
|
|
|
if (m)
|
|
|
|
{
|
2002-04-28 08:30:54 +00:00
|
|
|
if ((op = EXP_FindOpByStr (buf))) {
|
|
|
|
i -= (m - strlen(op->str) + 1);
|
2002-04-24 22:33:04 +00:00
|
|
|
new = EXP_NewToken();
|
|
|
|
new->generic.type = TOKEN_OP;
|
2002-08-23 21:18:00 +00:00
|
|
|
if (*(op->str) == '-') // HACK HACK HACK
|
2002-11-13 03:03:37 +00:00
|
|
|
op = optable + 6; // Always assume subtraction for - initially
|
2002-04-28 08:30:54 +00:00
|
|
|
new->op.op = op;
|
|
|
|
new->generic.prev = cur;
|
|
|
|
new->generic.next = 0;
|
|
|
|
cur->generic.next = new;
|
|
|
|
cur = new;
|
|
|
|
} else if ((func = EXP_FindFuncByStr(buf))) {
|
|
|
|
i -= (m - strlen(func->str) + 1);
|
|
|
|
new = EXP_NewToken();
|
|
|
|
new->generic.type = TOKEN_FUNC;
|
|
|
|
new->func.func = func;
|
2002-04-24 22:33:04 +00:00
|
|
|
new->generic.prev = cur;
|
|
|
|
new->generic.next = 0;
|
|
|
|
cur->generic.next = new;
|
|
|
|
cur = new;
|
|
|
|
} else {
|
2002-03-20 22:57:26 +00:00
|
|
|
EXP_DestroyTokens (chain);
|
2002-11-13 03:03:37 +00:00
|
|
|
EXP_Error (EXP_E_INVOP, va("Unknown operator or function '%s'.", buf));
|
2002-03-17 06:57:03 +00:00
|
|
|
return 0;
|
2002-03-20 22:57:26 +00:00
|
|
|
}
|
2002-03-17 06:57:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
new = EXP_NewToken();
|
|
|
|
new->generic.type = TOKEN_CPAREN;
|
|
|
|
new->generic.prev = cur;
|
|
|
|
new->generic.next = 0;
|
|
|
|
cur->generic.next = new;
|
|
|
|
return chain;
|
|
|
|
}
|
|
|
|
|
2002-11-05 19:12:51 +00:00
|
|
|
exp_error_t
|
|
|
|
EXP_SimplifyTokens (token *chain)
|
2002-03-17 06:57:03 +00:00
|
|
|
{
|
2002-04-28 08:30:54 +00:00
|
|
|
exp_error_t res;
|
2002-03-17 06:57:03 +00:00
|
|
|
int i;
|
|
|
|
token *cur;
|
|
|
|
token *temp;
|
|
|
|
|
|
|
|
/* First, get rid of parentheses */
|
|
|
|
|
|
|
|
for (cur = chain->generic.next; cur->generic.type != TOKEN_CPAREN; cur = cur->generic.next)
|
|
|
|
{
|
|
|
|
if (cur->generic.type == TOKEN_OPAREN)
|
|
|
|
{
|
2002-04-28 08:30:54 +00:00
|
|
|
res = EXP_SimplifyTokens(cur); /* Call ourself to simplify parentheses content */
|
|
|
|
if (res)
|
|
|
|
return res;
|
|
|
|
if (cur->generic.prev->generic.type == TOKEN_FUNC) { // These are arguments to a function
|
|
|
|
cur = cur->generic.prev;
|
|
|
|
if (EXP_DoFunction (cur))
|
2002-11-13 03:03:37 +00:00
|
|
|
return EXP_Error (EXP_E_SYNTAX, va("Invalid number of arguments to function '%s'.", cur->func.func->str));
|
2002-04-28 08:30:54 +00:00
|
|
|
} else {
|
|
|
|
if (EXP_ContainsCommas (cur))
|
2002-11-13 03:03:37 +00:00
|
|
|
return EXP_Error (EXP_E_SYNTAX, "Comma used outside of a function argument list.");
|
2002-04-28 08:30:54 +00:00
|
|
|
temp = cur;
|
|
|
|
cur = cur->generic.next;
|
|
|
|
EXP_RemoveToken(temp); /* Remove parentheses, leaving value behind */
|
|
|
|
EXP_RemoveToken(cur->generic.next);
|
|
|
|
}
|
2002-03-17 06:57:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Next, evaluate all operators in order of operations */
|
|
|
|
|
|
|
|
for (i = 0; optable[i].func; i++)
|
|
|
|
{
|
|
|
|
for (cur = chain->generic.next; cur->generic.type != TOKEN_CPAREN; cur = cur->generic.next)
|
|
|
|
{
|
2002-04-28 08:30:54 +00:00
|
|
|
if (cur->generic.type == TOKEN_OP && cur->op.op == optable + i && cur->generic.next) {
|
2002-08-23 21:18:00 +00:00
|
|
|
// If a unary operator is in our way, it gets evaluated early
|
|
|
|
if (cur->generic.next->generic.type == TOKEN_OP)
|
|
|
|
if (EXP_DoUnary (cur->generic.next))
|
2002-11-13 03:03:37 +00:00
|
|
|
return EXP_Error (EXP_E_SYNTAX, va("Unary operator '%s' not followed by a unary operator or numerical value.", cur->generic.next->op.op->str));
|
2002-04-24 22:33:04 +00:00
|
|
|
if (optable[i].operands == 1 && cur->generic.next->generic.type == TOKEN_NUM) {
|
|
|
|
cur->generic.next->num.value = optable[i].func(cur->generic.next->num.value, 0);
|
|
|
|
temp = cur;
|
|
|
|
cur = cur->generic.next;
|
|
|
|
EXP_RemoveToken(temp);
|
|
|
|
}
|
|
|
|
else if (cur->generic.prev->generic.type == TOKEN_NUM && cur->generic.next->generic.type == TOKEN_NUM)
|
2002-03-17 06:57:03 +00:00
|
|
|
{
|
|
|
|
cur->generic.prev->num.value = optable[i].func(cur->generic.prev->num.value, cur->generic.next->num.value);
|
|
|
|
temp = cur;
|
|
|
|
cur = cur->generic.prev;
|
|
|
|
EXP_RemoveToken(temp->generic.next);
|
|
|
|
EXP_RemoveToken(temp);
|
|
|
|
}
|
2002-04-24 22:33:04 +00:00
|
|
|
}
|
2002-03-17 06:57:03 +00:00
|
|
|
}
|
|
|
|
}
|
2002-04-28 08:30:54 +00:00
|
|
|
return EXP_E_NORMAL;
|
2002-03-17 06:57:03 +00:00
|
|
|
}
|
|
|
|
|
2002-11-05 19:12:51 +00:00
|
|
|
void
|
|
|
|
EXP_RemoveToken (token *tok)
|
2002-03-17 06:57:03 +00:00
|
|
|
{
|
|
|
|
tok->generic.prev->generic.next = tok->generic.next;
|
|
|
|
tok->generic.next->generic.prev = tok->generic.prev;
|
|
|
|
free(tok);
|
|
|
|
}
|
|
|
|
|
2002-11-05 19:12:51 +00:00
|
|
|
void
|
|
|
|
EXP_DestroyTokens (token *chain)
|
2002-03-17 06:57:03 +00:00
|
|
|
{
|
|
|
|
token *temp;
|
|
|
|
for (;chain; chain = temp)
|
|
|
|
{
|
|
|
|
temp = chain->generic.next;
|
|
|
|
free(chain);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-11-05 19:12:51 +00:00
|
|
|
double
|
|
|
|
EXP_Evaluate (char *str)
|
2002-03-17 06:57:03 +00:00
|
|
|
{
|
|
|
|
token *chain;
|
2002-04-28 08:30:54 +00:00
|
|
|
double res;
|
2002-03-17 06:57:03 +00:00
|
|
|
|
2002-03-20 04:24:31 +00:00
|
|
|
EXP_ERROR = EXP_E_NORMAL;
|
|
|
|
|
2002-03-17 06:57:03 +00:00
|
|
|
if (!(chain = EXP_ParseString (str)))
|
|
|
|
return 0;
|
2002-11-13 03:03:37 +00:00
|
|
|
if (EXP_Validate (chain))
|
2002-04-28 08:30:54 +00:00
|
|
|
{
|
|
|
|
EXP_DestroyTokens (chain);
|
|
|
|
return 0;
|
|
|
|
}
|
2002-11-13 03:03:37 +00:00
|
|
|
if (EXP_SimplifyTokens (chain))
|
2002-03-17 06:57:03 +00:00
|
|
|
{
|
2002-03-20 22:57:26 +00:00
|
|
|
EXP_DestroyTokens (chain);
|
2002-03-17 06:57:03 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
res = chain->generic.next->num.value;
|
|
|
|
EXP_DestroyTokens (chain);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2002-11-05 19:12:51 +00:00
|
|
|
void
|
|
|
|
EXP_InsertTokenAfter (token *spot, token *new)
|
2002-03-17 06:57:03 +00:00
|
|
|
{
|
|
|
|
spot->generic.next->generic.prev = new;
|
|
|
|
new->generic.next = spot->generic.next;
|
|
|
|
new->generic.prev = spot;
|
|
|
|
spot->generic.next = new;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-11-05 19:12:51 +00:00
|
|
|
exp_error_t
|
|
|
|
EXP_Validate (token *chain)
|
2002-03-17 06:57:03 +00:00
|
|
|
{
|
|
|
|
token *cur, *new;
|
|
|
|
int paren = 0;
|
2002-04-24 22:33:04 +00:00
|
|
|
|
2002-03-17 06:57:03 +00:00
|
|
|
for (cur = chain; cur->generic.next; cur = cur->generic.next)
|
|
|
|
{
|
|
|
|
if (cur->generic.type == TOKEN_OPAREN)
|
|
|
|
paren++;
|
|
|
|
if (cur->generic.type == TOKEN_CPAREN)
|
|
|
|
paren--;
|
|
|
|
/* Implied multiplication */
|
2002-11-13 03:03:37 +00:00
|
|
|
if ((cur->generic.type == TOKEN_NUM && (
|
|
|
|
cur->generic.next->generic.type == TOKEN_OPAREN || // 5(1+1)
|
|
|
|
cur->generic.next->generic.type == TOKEN_FUNC || // 5 sin (1+1)
|
|
|
|
(cur->generic.next->generic.type == TOKEN_OP && cur->generic.next->op.op->operands == 1))) || // 5!(1+1)
|
|
|
|
(cur->generic.type == TOKEN_CPAREN && (
|
|
|
|
cur->generic.next->generic.type == TOKEN_NUM || // (1+1)5
|
|
|
|
cur->generic.next->generic.type == TOKEN_OPAREN))) // (1+1)(1+1)
|
2002-03-17 06:57:03 +00:00
|
|
|
{
|
|
|
|
new = EXP_NewToken ();
|
|
|
|
new->generic.type = TOKEN_OP;
|
2002-04-28 08:30:54 +00:00
|
|
|
new->op.op = EXP_FindOpByStr ("*");
|
2002-03-17 06:57:03 +00:00
|
|
|
EXP_InsertTokenAfter (cur, new);
|
|
|
|
}
|
2002-04-28 08:30:54 +00:00
|
|
|
else if ((cur->generic.type == TOKEN_OP || cur->generic.type == TOKEN_OPAREN) && cur->generic.next->generic.type == TOKEN_OP)
|
2002-03-17 06:57:03 +00:00
|
|
|
{
|
2002-04-28 08:30:54 +00:00
|
|
|
if (cur->generic.next->op.op->func == OP_Sub) /* Stupid hack for negation */
|
2002-08-23 21:18:00 +00:00
|
|
|
cur->generic.next->op.op = optable + 3;
|
2002-04-28 08:30:54 +00:00
|
|
|
else if (cur->generic.next->op.op->operands == 2)
|
2002-11-13 03:03:37 +00:00
|
|
|
return EXP_Error (EXP_E_SYNTAX, va ("Operator '%s' does not follow a number or numerical value.", cur->generic.next->op.op->str));
|
2002-03-17 06:57:03 +00:00
|
|
|
}
|
2002-04-28 08:30:54 +00:00
|
|
|
else if (cur->generic.type == TOKEN_FUNC && cur->generic.next->generic.type != TOKEN_OPAREN)
|
2002-11-13 03:03:37 +00:00
|
|
|
return EXP_Error (EXP_E_SYNTAX, va("Function '%s' called without an argument list.", cur->func.func->str));
|
2002-04-28 08:30:54 +00:00
|
|
|
else if (cur->generic.type == TOKEN_COMMA && ((cur->generic.prev->generic.type != TOKEN_CPAREN
|
2002-11-13 03:03:37 +00:00
|
|
|
&& cur->generic.prev->generic.type != TOKEN_NUM) || paren <= 1))
|
|
|
|
return EXP_Error (EXP_E_SYNTAX, "Comma used outside of a function or after a non-number.");
|
2002-03-27 06:24:19 +00:00
|
|
|
else if (cur->generic.type == TOKEN_OP && cur->generic.next->generic.type == TOKEN_CPAREN)
|
2002-11-13 03:03:37 +00:00
|
|
|
return EXP_Error (EXP_E_SYNTAX, va("Operator '%s' is missing an operand.", cur->op.op->str));
|
2002-03-17 06:57:03 +00:00
|
|
|
else if (cur->generic.type == TOKEN_NUM && cur->generic.next->generic.type == TOKEN_NUM)
|
2002-11-13 03:03:37 +00:00
|
|
|
return EXP_Error (EXP_E_SYNTAX, "Double number error (two numbers next two each other without an operator).");
|
2002-03-17 06:57:03 +00:00
|
|
|
else if (cur->generic.type == TOKEN_OPAREN && cur->generic.next->generic.type == TOKEN_CPAREN)
|
2002-11-13 03:03:37 +00:00
|
|
|
return EXP_Error (EXP_E_PAREN, "Empty parentheses found.");
|
2002-03-17 06:57:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
paren--;
|
|
|
|
if (paren)
|
2002-11-13 03:03:37 +00:00
|
|
|
return EXP_Error (EXP_E_PAREN, "Parenthesis mismatch.");
|
2002-03-17 06:57:03 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2002-11-05 19:12:51 +00:00
|
|
|
void
|
|
|
|
EXP_PrintTokens (token *chain)
|
2002-03-17 06:57:03 +00:00
|
|
|
{
|
|
|
|
for (; chain; chain = chain->generic.next)
|
|
|
|
switch (chain->generic.type)
|
|
|
|
{
|
|
|
|
case TOKEN_OPAREN:
|
|
|
|
printf("(");
|
|
|
|
break;
|
|
|
|
case TOKEN_CPAREN:
|
|
|
|
printf(")");
|
|
|
|
break;
|
2002-04-28 08:30:54 +00:00
|
|
|
case TOKEN_COMMA:
|
|
|
|
printf(",");
|
|
|
|
break;
|
2002-03-17 06:57:03 +00:00
|
|
|
case TOKEN_NUM:
|
|
|
|
printf("%f", chain->num.value);
|
|
|
|
break;
|
|
|
|
case TOKEN_OP:
|
2002-04-28 08:30:54 +00:00
|
|
|
printf("%s", chain->op.op->str);
|
|
|
|
break;
|
|
|
|
case TOKEN_FUNC:
|
|
|
|
printf("%s", chain->func.func->str);
|
|
|
|
break;
|
2002-03-17 06:57:03 +00:00
|
|
|
case TOKEN_GENERIC:
|
|
|
|
break;
|
|
|
|
}
|
2002-04-24 22:33:04 +00:00
|
|
|
printf("\n");
|
2002-03-17 06:57:03 +00:00
|
|
|
}
|