diff --git a/include/QF/exp.h b/include/QF/exp.h index 260730050..24c6ab837 100644 --- a/include/QF/exp.h +++ b/include/QF/exp.h @@ -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); diff --git a/include/QF/ops.h b/include/QF/ops.h index 2b3cfef44..6cb92f7d7 100644 --- a/include/QF/ops.h +++ b/include/QF/ops.h @@ -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 diff --git a/libs/util/cmd.c b/libs/util/cmd.c index b93d75277..9be250de9 100644 --- a/libs/util/cmd.c +++ b/libs/util/cmd.c @@ -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; diff --git a/libs/util/exp.c b/libs/util/exp.c index 4d5378275..50dda513e 100644 --- a/libs/util/exp.c +++ b/libs/util/exp.c @@ -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; } diff --git a/libs/util/ops.c b/libs/util/ops.c index 9279f6e4f..954ccc960 100644 --- a/libs/util/ops.c +++ b/libs/util/ops.c @@ -21,69 +21,100 @@ #include -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]); +} diff --git a/libs/video/renderer/gl/gl_screen.c b/libs/video/renderer/gl/gl_screen.c index 2ae47b826..2f0eb6111 100644 --- a/libs/video/renderer/gl/gl_screen.c +++ b/libs/video/renderer/gl/gl_screen.c @@ -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); diff --git a/libs/video/renderer/sw/screen.c b/libs/video/renderer/sw/screen.c index 9dd30c397..4c9a7ac14 100644 --- a/libs/video/renderer/sw/screen.c +++ b/libs/video/renderer/sw/screen.c @@ -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);