Added argument-list functions to EXP with trig and inverse trig functions

to start.  More will follow.  Switched to doubles from floats and allowed
numbers with exponents to be parsed properly.  Also changed the number of
significant digits printed to 10.  Changed fov so it is now capped at 1,
not 10.  The float value of fov is now used instead of int, so more
precision can be attained at small values.
This commit is contained in:
Brian Koropoff 2002-04-28 08:30:54 +00:00
parent b93f37fb00
commit ff901a312f
7 changed files with 276 additions and 81 deletions

View file

@ -22,11 +22,26 @@
#ifndef __exp_h
#define __exp_h
typedef enum {TOKEN_GENERIC, TOKEN_NUM, TOKEN_OP, TOKEN_OPAREN, TOKEN_CPAREN}
typedef enum {TOKEN_GENERIC, TOKEN_NUM, TOKEN_OP, TOKEN_FUNC, TOKEN_OPAREN, TOKEN_CPAREN, TOKEN_COMMA}
token_type;
typedef enum {EXP_E_NORMAL = 0, EXP_E_PARSE, EXP_E_INVOP, EXP_E_PAREN,
EXP_E_INVPARAM, EXP_E_SYNTAX} exp_error_t;
typedef float (*opfunc) (float op1, float op2);
typedef double (*opfunc) (double op1, double op2);
typedef double (*funcfunc) (double *oplist, unsigned int numops);
typedef struct optable_s
{
char *str;
opfunc func;
unsigned int operands;
} optable_t;
typedef struct functable_s
{
char *str;
funcfunc func; // Heh
unsigned int operands;
} functable_t;
typedef struct token_generic_s
{
@ -38,37 +53,38 @@ typedef struct token_num_s
{
token_type type;
union token_u *prev, *next;
float value;
double value;
} token_num;
typedef struct token_op_s
{
token_type type;
union token_u *prev, *next;
opfunc func;
optable_t *op;
} token_op;
typedef struct token_func_s
{
token_type type;
union token_u *prev, *next;
functable_t *func;
} token_func;
typedef union token_u
{
token_generic generic;
token_num num;
token_op op;
token_func func;
} token;
typedef struct optable_s
{
char *str;
opfunc func;
unsigned int operands;
} optable_t;
extern exp_error_t EXP_ERROR;
token *EXP_ParseString (char *str);
void EXP_SimplifyTokens (token *chain);
exp_error_t EXP_SimplifyTokens (token *chain);
void EXP_RemoveToken (token *tok);
void EXP_DestroyTokens (token *chain);
float EXP_Evaluate (char *str);
double EXP_Evaluate (char *str);
void EXP_InsertTokenAfter (token *spot, token *new);
exp_error_t EXP_Validate (token *chain);
void EXP_PrintTokens (token *chain);

View file

@ -22,18 +22,25 @@
#ifndef __ops_h
#define __ops_h
float OP_Not (float op1, float op2);
float OP_Add (float op1, float op2);
float OP_Sub (float op1, float op2);
float OP_Mult (float op1, float op2);
float OP_Div (float op1, float op2);
float OP_Exp (float op1, float op2);
float OP_Eq (float op1, float op2);
float OP_Neq (float op1, float op2);
float OP_Or (float op1, float op2);
float OP_And (float op1, float op2);
float OP_GreaterThan (float op1, float op2);
float OP_LessThan (float op1, float op2);
float OP_GreaterThanEqual (float op1, float op2);
float OP_LessThanEqual (float op1, float op2);
double OP_Not (double op1, double op2);
double OP_Add (double op1, double op2);
double OP_Sub (double op1, double op2);
double OP_Mult (double op1, double op2);
double OP_Div (double op1, double op2);
double OP_Exp (double op1, double op2);
double OP_Eq (double op1, double op2);
double OP_Neq (double op1, double op2);
double OP_Or (double op1, double op2);
double OP_And (double op1, double op2);
double OP_GreaterThan (double op1, double op2);
double OP_LessThan (double op1, double op2);
double OP_GreaterThanEqual (double op1, double op2);
double OP_LessThanEqual (double op1, double op2);
double Func_Sin (double *oplist, unsigned int numops);
double Func_Cos (double *oplist, unsigned int numops);
double Func_Tan (double *oplist, unsigned int numops);
double Func_Asin (double *oplist, unsigned int numops);
double Func_Acos (double *oplist, unsigned int numops);
double Func_Atan (double *oplist, unsigned int numops);
#endif // __ops_h

View file

@ -1256,7 +1256,7 @@ Cmd_ProcessMath (dstring_t * dstr)
{
dstring_t *statement;
int i, n;
float value;
double value;
char *temp;
int ret = 0;
@ -1276,7 +1276,7 @@ Cmd_ProcessMath (dstring_t * dstr)
dstring_insert (statement, dstr->str + i + 2, n - 2, 0);
value = EXP_Evaluate (statement->str);
if (EXP_ERROR == EXP_E_NORMAL) {
temp = va ("%g", value);
temp = va ("%.10g", value);
dstring_snip (dstr, i, n + 1); // Nuke the statement
dstring_insertstr (dstr, temp, i); // Stick in the value
i += strlen (temp) - 1;
@ -1293,7 +1293,7 @@ Cmd_ProcessMath (dstring_t * dstr)
/*
Cmd_ProcessEscapes
Looks for the escape character \ and
removes it. Special cases exist for
\\ and \n; otherwise, it is simply
@ -2167,6 +2167,9 @@ Cmd_Init (void)
cmd_maxloop = Cvar_Get ("cmd_maxloop", "0", CVAR_NONE, NULL, "Controls the "
"maximum number of iterations a loop in GIB can do "
"before being forcefully terminated. 0 is infinite.");
// Constants for the math interpreter
// We don't need to assign the return values to anything because these are never used elsewhere
Cvar_Get ("M_PI", "3.1415926535897932384626433832795029", CVAR_ROM, NULL, "Pi");
}
char *com_token;

View file

@ -48,7 +48,18 @@ optable_t optable[] =
{"or", OP_Or, 2},
{"&&", OP_And, 2},
{"and", OP_And, 2},
{"", 0}
{"", 0, 0}
};
functable_t functable[] =
{
{"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}
};
token *EXP_NewToken (void)
@ -63,6 +74,7 @@ token *EXP_NewToken (void)
return new;
}
/*
int EXP_FindIndexByFunc (opfunc func)
{
int i;
@ -71,8 +83,9 @@ int EXP_FindIndexByFunc (opfunc func)
return i;
return -1;
}
*/
int EXP_FindIndexByStr (const char *str)
optable_t *EXP_FindOpByStr (const char *str)
{
int i, len, fi;
@ -81,15 +94,88 @@ int EXP_FindIndexByStr (const char *str)
len = strlen(optable[i].str);
fi = i;
}
return fi;
if (fi >= 0)
return optable+fi;
else
return 0;
}
functable_t *EXP_FindFuncByStr (const char *str)
{
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;
}
int EXP_ContainsCommas (token *chain)
{
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
}
int EXP_DoFunction (token *chain)
{
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
}
token *EXP_ParseString (char *str)
{
char buf[256];
token *chain, *new, *cur;
int i,m,fi;
int i,m;
optable_t *op;
functable_t *func;
cur = chain = EXP_NewToken();
chain->generic.type = TOKEN_OPAREN;
@ -105,7 +191,12 @@ token *EXP_ParseString (char *str)
break;
if (isdigit((byte)str[i]) || str[i] == '.')
{
while ((isdigit((byte)str[i]) || str[i] == '.') && i < strlen(str) && m < 256)
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
buf[m++] = str[i++];
buf[m] = 0;
new = EXP_NewToken();
@ -117,6 +208,15 @@ token *EXP_ParseString (char *str)
cur = new;
i--;
}
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;
}
else if (str[i] == '(')
{
new = EXP_NewToken();
@ -137,18 +237,28 @@ token *EXP_ParseString (char *str)
}
else
{
while(!(isdigit((byte)str[i])) && !isspace((byte)str[i]) && str[i] != '.' && str[i] != '(' && str[i] != ')' && m < 256) {
while(!(isdigit((byte)str[i])) && !isspace((byte)str[i])
&& str[i] != '.' && str[i] != '(' && str[i] != ')'
&& str[i] != ',' && m < 256) {
buf[m++] = str[i++];
}
buf[m] = 0;
if (m)
{
fi = EXP_FindIndexByStr (buf);
if (fi != -1) {
i -= (m - strlen(optable[fi].str) + 1);
if ((op = EXP_FindOpByStr (buf))) {
i -= (m - strlen(op->str) + 1);
new = EXP_NewToken();
new->generic.type = TOKEN_OP;
new->op.func = optable[fi].func;
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;
new->generic.prev = cur;
new->generic.next = 0;
cur->generic.next = new;
@ -168,8 +278,9 @@ token *EXP_ParseString (char *str)
return chain;
}
void EXP_SimplifyTokens (token *chain)
exp_error_t EXP_SimplifyTokens (token *chain)
{
exp_error_t res;
int i;
token *cur;
token *temp;
@ -180,11 +291,21 @@ void EXP_SimplifyTokens (token *chain)
{
if (cur->generic.type == TOKEN_OPAREN)
{
EXP_SimplifyTokens(cur); /* Call ourself to simplify parentheses content */
temp = cur;
cur = cur->generic.next;
EXP_RemoveToken(temp); /* Remove parentheses, leaving value behind */
EXP_RemoveToken(cur->generic.next);
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))
return EXP_E_SYNTAX;
} else {
if (EXP_ContainsCommas (cur))
return EXP_E_SYNTAX;
temp = cur;
cur = cur->generic.next;
EXP_RemoveToken(temp); /* Remove parentheses, leaving value behind */
EXP_RemoveToken(cur->generic.next);
}
}
}
@ -194,7 +315,7 @@ void EXP_SimplifyTokens (token *chain)
{
for (cur = chain->generic.next; cur->generic.type != TOKEN_CPAREN; cur = cur->generic.next)
{
if (cur->generic.type == TOKEN_OP && cur->op.func == optable[i].func && cur->generic.next) {
if (cur->generic.type == TOKEN_OP && cur->op.op == optable + i && cur->generic.next) {
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;
@ -212,6 +333,7 @@ void EXP_SimplifyTokens (token *chain)
}
}
}
return EXP_E_NORMAL;
}
void EXP_RemoveToken (token *tok)
@ -231,10 +353,10 @@ void EXP_DestroyTokens (token *chain)
}
}
float EXP_Evaluate (char *str)
double EXP_Evaluate (char *str)
{
token *chain;
float res;
double res;
EXP_ERROR = EXP_E_NORMAL;
@ -244,17 +366,23 @@ float EXP_Evaluate (char *str)
return 0;
}
res = EXP_Validate (chain);
if (res)
{
EXP_DestroyTokens (chain);
EXP_ERROR = res;
return 0;
}
res = EXP_SimplifyTokens (chain);
if (res)
{
EXP_DestroyTokens (chain);
EXP_ERROR = res;
return 0;
}
EXP_SimplifyTokens (chain);
res = chain->generic.next->num.value;
EXP_DestroyTokens (chain);
return res;
}
@ -282,29 +410,34 @@ exp_error_t EXP_Validate (token *chain)
/* Implied multiplication */
if ((cur->generic.type == TOKEN_NUM && cur->generic.next->generic.type == TOKEN_OPAREN) || /* 5(1+1) */
(cur->generic.type == TOKEN_CPAREN && cur->generic.next->generic.type == TOKEN_NUM) || /* (1+1)5 */
(cur->generic.type == TOKEN_CPAREN && cur->generic.next->generic.type == TOKEN_OPAREN)) /* (1+1)(1+1) */
(cur->generic.type == TOKEN_CPAREN && cur->generic.next->generic.type == TOKEN_OPAREN) || /* (1+1)(1+1) */
(cur->generic.type == TOKEN_NUM && cur->generic.next->generic.type == TOKEN_FUNC)) /* 4sin(1) */
{
new = EXP_NewToken ();
new->generic.type = TOKEN_OP;
new->op.func = OP_Mult;
new->op.op = EXP_FindOpByStr ("*");
EXP_InsertTokenAfter (cur, new);
}
if ((cur->generic.type == TOKEN_OP || cur->generic.type == TOKEN_OPAREN) && cur->generic.next->generic.type == TOKEN_OP)
else if ((cur->generic.type == TOKEN_OP || cur->generic.type == TOKEN_OPAREN) && cur->generic.next->generic.type == TOKEN_OP)
{
if (cur->generic.next->op.func == OP_Sub) /* Stupid hack for negation */
if (cur->generic.next->op.op->func == OP_Sub) /* Stupid hack for negation */
{
cur = cur->generic.next;
cur->generic.type = TOKEN_NUM;
cur->num.value = -1.0;
new = EXP_NewToken();
new->generic.type = TOKEN_OP;
new->op.func = OP_Mult;
new->op.op = EXP_FindOpByStr ("*");
EXP_InsertTokenAfter (cur, new);
}
else if (optable[EXP_FindIndexByFunc(cur->generic.next->op.func)].operands == 2)
else if (cur->generic.next->op.op->operands == 2)
return EXP_E_SYNTAX; /* Operator misuse */
}
else if (cur->generic.type == TOKEN_FUNC && cur->generic.next->generic.type != TOKEN_OPAREN)
return EXP_E_SYNTAX; /* No arguments to funcion */
else if (cur->generic.type == TOKEN_COMMA && ((cur->generic.prev->generic.type != TOKEN_CPAREN
&& cur->generic.prev->generic.type != TOKEN_NUM) || paren <= 1))
return EXP_E_SYNTAX; /* Misused comma */
else if (cur->generic.type == TOKEN_OP && cur->generic.next->generic.type == TOKEN_CPAREN)
return EXP_E_SYNTAX; /* Missing operand */
else if (cur->generic.type == TOKEN_NUM && cur->generic.next->generic.type == TOKEN_NUM)
@ -322,7 +455,6 @@ exp_error_t EXP_Validate (token *chain)
void EXP_PrintTokens (token *chain)
{
int m;
for (; chain; chain = chain->generic.next)
switch (chain->generic.type)
{
@ -332,12 +464,18 @@ void EXP_PrintTokens (token *chain)
case TOKEN_CPAREN:
printf(")");
break;
case TOKEN_COMMA:
printf(",");
break;
case TOKEN_NUM:
printf("%f", chain->num.value);
break;
case TOKEN_OP:
for (m = 0; optable[m].func != chain->op.func; m++);
printf("%s", optable[m].str);
printf("%s", chain->op.op->str);
break;
case TOKEN_FUNC:
printf("%s", chain->func.func->str);
break;
case TOKEN_GENERIC:
break;
}

View file

@ -21,69 +21,100 @@
#include <math.h>
float OP_Not (float op1, float op2)
double OP_Not (double op1, double op2)
{
return !op1;
}
float OP_Add (float op1, float op2)
double OP_Add (double op1, double op2)
{
return op1 + op2;
}
float OP_Sub (float op1, float op2)
double OP_Sub (double op1, double op2)
{
return op1 - op2;
}
float OP_Mult (float op1, float op2)
double OP_Mult (double op1, double op2)
{
return op1 * op2;
}
float OP_Div (float op1, float op2)
double OP_Div (double op1, double op2)
{
return op1 / op2;
}
float OP_Exp (float op1, float op2)
double OP_Exp (double op1, double op2)
{
return pow(op1, op2);
}
float OP_Eq (float op1, float op2)
double OP_Eq (double op1, double op2)
{
return op1 == op2;
}
float OP_Neq (float op1, float op2)
double OP_Neq (double op1, double op2)
{
return op1 != op2;
}
float OP_Or (float op1, float op2)
double OP_Or (double op1, double op2)
{
return op1 || op2;
}
float OP_And (float op1, float op2)
double OP_And (double op1, double op2)
{
return op1 && op2;
}
float OP_GreaterThan (float op1, float op2)
double OP_GreaterThan (double op1, double op2)
{
return op1 > op2;
}
float OP_LessThan (float op1, float op2)
double OP_LessThan (double op1, double op2)
{
return op1 < op2;
}
float OP_GreaterThanEqual (float op1, float op2)
double OP_GreaterThanEqual (double op1, double op2)
{
return op1 >= op2;
}
float OP_LessThanEqual (float op1, float op2)
double OP_LessThanEqual (double op1, double op2)
{
return op1 <= op2;
}
double Func_Sin (double *oplist, unsigned int numops)
{
return sin (oplist[0]);
}
double Func_Cos (double *oplist, unsigned int numops)
{
return cos (oplist[0]);
}
double Func_Tan (double *oplist, unsigned int numops)
{
return tan (oplist[0]);
}
double Func_Asin (double *oplist, unsigned int numops)
{
return asin (oplist[0]);
}
double Func_Acos (double *oplist, unsigned int numops)
{
return acos (oplist[0]);
}
double Func_Atan (double *oplist, unsigned int numops)
{
return atan (oplist[0]);
}

View file

@ -273,7 +273,7 @@ SCR_CalcRefdef (void)
Cvar_SetValue (scr_viewsize, bound (30, scr_viewsize->int_val, 120));
// bound field of view
Cvar_SetValue (scr_fov, bound (10, scr_fov->value, 170));
Cvar_SetValue (scr_fov, bound (1, scr_fov->value, 170));
if (scr_viewsize->int_val >= 120)
sb_lines = 0; // no status bar at all
@ -313,7 +313,7 @@ SCR_CalcRefdef (void)
else
r_refdef.vrect.y = (h - r_refdef.vrect.height) / 2;
r_refdef.fov_x = scr_fov->int_val;
r_refdef.fov_x = scr_fov->value;
r_refdef.fov_y =
CalcFov (r_refdef.fov_x, r_refdef.vrect.width, r_refdef.vrect.height);

View file

@ -279,7 +279,7 @@ SCR_CalcRefdef (void)
Cvar_SetValue (scr_viewsize, bound (30, scr_viewsize->int_val, 120));
// bound field of view
Cvar_SetValue (scr_fov, bound (10, scr_fov->value, 170));
Cvar_SetValue (scr_fov, bound (1, scr_fov->value, 170));
if (scr_viewsize->int_val >= 120)
sb_lines = 0; // no status bar at all
@ -319,7 +319,7 @@ SCR_CalcRefdef (void)
else
r_refdef.vrect.y = (h - r_refdef.vrect.height) / 2;
r_refdef.fov_x = scr_fov->int_val;
r_refdef.fov_x = scr_fov->value;
r_refdef.fov_y =
CalcFov (r_refdef.fov_x, r_refdef.vrect.width, r_refdef.vrect.height);