PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/quakeforge/trunk/libs/gib/exp.c

#
C | 548 lines | 464 code | 44 blank | 40 comment | 170 complexity | 0a6221d52b9b83e5693322ff932e3c80 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, AGPL-3.0, AGPL-1.0, Unlicense
  1. /*
  2. EXP equation evaluation routines
  3. Copyright (C) 2000, 2001 Brian Koropoff
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License
  6. as published by the Free Software Foundation; either version 2
  7. of the License, or (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, US
  15. */
  16. #ifdef HAVE_CONFIG_H
  17. # include "config.h"
  18. #endif
  19. #include <stdlib.h>
  20. #include <ctype.h>
  21. #include <string.h>
  22. #include <stdio.h>
  23. #include "QF/qtypes.h"
  24. #include "QF/va.h"
  25. #include "exp.h"
  26. #include "ops.h"
  27. exp_error_t EXP_ERROR;
  28. char *exp_error_msg = 0;
  29. optable_t optable[] = {
  30. {"~", OP_BitInv, 1},
  31. {"!", OP_Not, 1},
  32. {"&", OP_BitAnd, 2},
  33. {"|", OP_BitOr, 2},
  34. {"^", OP_BitXor, 2},
  35. {"**", OP_Exp, 2},
  36. {"/", OP_Div, 2},
  37. {"neg", OP_Negate, 1},
  38. {"*", OP_Mult, 2},
  39. {"+", OP_Add, 2},
  40. {"-", OP_Sub, 2},
  41. {"==", OP_Eq, 2},
  42. {"!=", OP_Neq, 2},
  43. {">=", OP_GreaterThanEqual, 2},
  44. {">", OP_GreaterThan, 2},
  45. {"<=", OP_LessThanEqual, 2},
  46. {"<", OP_LessThan, 2},
  47. {"||", OP_Or, 2},
  48. {"or", OP_Or, 2},
  49. {"&&", OP_And, 2},
  50. {"and", OP_And, 2},
  51. {"", 0, 0}
  52. };
  53. functable_t functable[] = {
  54. {"sqrt", Func_Sqrt, 1},
  55. {"abs", Func_Abs, 1},
  56. {"sin", Func_Sin, 1},
  57. {"cos", Func_Cos, 1},
  58. {"tan", Func_Tan, 1},
  59. {"asin", Func_Asin, 1},
  60. {"acos", Func_Acos, 1},
  61. {"atan", Func_Atan, 1},
  62. {"rand", Func_Rand, 2},
  63. {"trunc", Func_Trunc, 1},
  64. {"", 0, 0}
  65. };
  66. // Error handling
  67. static exp_error_t
  68. EXP_Error (exp_error_t err, const char *msg)
  69. {
  70. EXP_ERROR = err;
  71. if (exp_error_msg)
  72. free (exp_error_msg);
  73. exp_error_msg = strdup (msg);
  74. return err;
  75. }
  76. const char *
  77. EXP_GetErrorMsg (void)
  78. {
  79. return exp_error_msg;
  80. }
  81. static token *
  82. EXP_NewToken (void)
  83. {
  84. token *new;
  85. new = malloc (sizeof (token));
  86. if (!new)
  87. return 0;
  88. new->generic.type = TOKEN_GENERIC;
  89. return new;
  90. }
  91. /*
  92. int
  93. EXP_FindIndexByFunc (opfunc func)
  94. {
  95. int i;
  96. for (i = 0; optable[i].func; i++)
  97. if (func == optable[i].func)
  98. return i;
  99. return -1;
  100. }
  101. */
  102. static optable_t *
  103. EXP_FindOpByStr (const char *str)
  104. {
  105. size_t i, len;
  106. int fi;
  107. for (i = 0, len = 0, fi = -1; optable[i].func; i++)
  108. if (!strncmp (str, optable[i].str, strlen (optable[i].str))
  109. && strlen (optable[i].str) > len) {
  110. len = strlen (optable[i].str);
  111. fi = i;
  112. }
  113. if (fi >= 0)
  114. return optable + fi;
  115. else
  116. return 0;
  117. }
  118. static functable_t *
  119. EXP_FindFuncByStr (const char *str)
  120. {
  121. size_t i, len;
  122. int fi;
  123. for (i = 0, len = 0, fi = -1; functable[i].func; i++)
  124. if (!strncmp (str, functable[i].str, strlen (functable[i].str))
  125. && strlen (functable[i].str) > len) {
  126. len = strlen (functable[i].str);
  127. fi = i;
  128. }
  129. if (fi >= 0)
  130. return functable + fi;
  131. else
  132. return 0;
  133. }
  134. static int
  135. EXP_ContainsCommas (token * chain)
  136. {
  137. token *cur;
  138. int paren = 0;
  139. for (cur = chain; cur; cur = cur->generic.next) {
  140. if (cur->generic.type == TOKEN_OPAREN)
  141. paren++;
  142. if (cur->generic.type == TOKEN_CPAREN)
  143. paren--;
  144. if (!paren)
  145. return 0;
  146. if (cur->generic.type == TOKEN_COMMA)
  147. return 1;
  148. }
  149. return -1; // We should never get here
  150. }
  151. static int
  152. EXP_DoFunction (token * chain)
  153. {
  154. token *cur, *temp;
  155. double *oplist = 0;
  156. double value;
  157. unsigned int numops = 0;
  158. for (cur = chain->generic.next; cur; cur = temp) {
  159. if (cur->generic.type != TOKEN_CPAREN)
  160. temp = cur->generic.next;
  161. else
  162. temp = 0;
  163. if (cur->generic.type == TOKEN_NUM) {
  164. numops++;
  165. oplist = realloc (oplist, sizeof (double) * numops);
  166. oplist[numops - 1] = cur->num.value;
  167. }
  168. EXP_RemoveToken (cur);
  169. }
  170. if (numops == chain->func.func->operands) {
  171. value = chain->func.func->func (oplist, numops); // Heh
  172. chain->generic.type = TOKEN_NUM;
  173. chain->num.value = value;
  174. if (oplist)
  175. free (oplist);
  176. return 0;
  177. } else {
  178. if (oplist)
  179. free (oplist);
  180. return -1;
  181. }
  182. return -2; // We shouldn't get here
  183. }
  184. static int
  185. EXP_DoUnary (token * chain)
  186. {
  187. if (chain->generic.next->generic.type == TOKEN_OP)
  188. EXP_DoUnary (chain->generic.next);
  189. if (chain->generic.next->generic.type != TOKEN_NUM)
  190. return -1; // In theory, this should never happen
  191. chain->generic.next->num.value =
  192. chain->op.op->func (chain->generic.next->num.value, 0);
  193. EXP_RemoveToken (chain);
  194. return 0;
  195. }
  196. token *
  197. EXP_ParseString (char *str)
  198. {
  199. char buf[256];
  200. token *chain, *new, *cur;
  201. size_t i, m;
  202. optable_t *op;
  203. functable_t *func;
  204. cur = chain = EXP_NewToken ();
  205. chain->generic.type = TOKEN_OPAREN;
  206. chain->generic.prev = 0;
  207. chain->generic.next = 0;
  208. for (i = 0; i < strlen (str); i++) {
  209. m = 0;
  210. while (isspace ((byte) str[i]))
  211. i++;
  212. if (!str[i])
  213. break;
  214. if (isdigit ((byte) str[i]) || str[i] == '.') {
  215. while ((isdigit ((byte) str[i]) // A number
  216. || str[i] == '.' // A decimal point
  217. || str[i] == 'e' // An exponent
  218. || ((str[i] == '-' || str[i] == '+')
  219. && str[i - 1] == 'e')) // A + or - after an exponent
  220. && i < strlen (str) // We are within the string
  221. && m < 256) // And there is space in the buffer
  222. buf[m++] = str[i++];
  223. buf[m] = 0;
  224. new = EXP_NewToken ();
  225. new->generic.type = TOKEN_NUM;
  226. new->num.value = atof (buf);
  227. new->generic.prev = cur;
  228. new->generic.next = 0;
  229. cur->generic.next = new;
  230. cur = new;
  231. i--;
  232. } else if (str[i] == ',') {
  233. new = EXP_NewToken ();
  234. new->generic.type = TOKEN_COMMA;
  235. new->generic.prev = cur;
  236. new->generic.next = 0;
  237. cur->generic.next = new;
  238. cur = new;
  239. } else if (str[i] == '(') {
  240. new = EXP_NewToken ();
  241. new->generic.type = TOKEN_OPAREN;
  242. new->generic.prev = cur;
  243. new->generic.next = 0;
  244. cur->generic.next = new;
  245. cur = new;
  246. } else if (str[i] == ')') {
  247. new = EXP_NewToken ();
  248. new->generic.type = TOKEN_CPAREN;
  249. new->generic.prev = cur;
  250. new->generic.next = 0;
  251. cur->generic.next = new;
  252. cur = new;
  253. } else {
  254. while (!(isdigit ((byte) str[i])) && !isspace ((byte) str[i])
  255. && str[i] != '.' && str[i] != '(' && str[i] != ')'
  256. && str[i] != ',' && m < 256) {
  257. buf[m++] = str[i++];
  258. }
  259. buf[m] = 0;
  260. if (m) {
  261. if ((op = EXP_FindOpByStr (buf))) {
  262. i -= (m - strlen (op->str) + 1);
  263. new = EXP_NewToken ();
  264. new->generic.type = TOKEN_OP;
  265. new->op.op = op;
  266. new->generic.prev = cur;
  267. new->generic.next = 0;
  268. cur->generic.next = new;
  269. cur = new;
  270. } else if ((func = EXP_FindFuncByStr (buf))) {
  271. i -= (m - strlen (func->str) + 1);
  272. new = EXP_NewToken ();
  273. new->generic.type = TOKEN_FUNC;
  274. new->func.func = func;
  275. new->generic.prev = cur;
  276. new->generic.next = 0;
  277. cur->generic.next = new;
  278. cur = new;
  279. } else {
  280. EXP_DestroyTokens (chain);
  281. EXP_Error (EXP_E_INVOP,
  282. va ("Unknown operator or function '%s'.", buf));
  283. return 0;
  284. }
  285. }
  286. }
  287. }
  288. new = EXP_NewToken ();
  289. new->generic.type = TOKEN_CPAREN;
  290. new->generic.prev = cur;
  291. new->generic.next = 0;
  292. cur->generic.next = new;
  293. return chain;
  294. }
  295. exp_error_t
  296. EXP_SimplifyTokens (token * chain)
  297. {
  298. exp_error_t res;
  299. int i;
  300. token *cur;
  301. token *temp;
  302. /* First, get rid of parentheses */
  303. for (cur = chain->generic.next; cur->generic.type != TOKEN_CPAREN;
  304. cur = cur->generic.next) {
  305. if (cur->generic.type == TOKEN_OPAREN) {
  306. res = EXP_SimplifyTokens (cur); /* Call ourself to simplify
  307. parentheses content */
  308. if (res)
  309. return res;
  310. if (cur->generic.prev->generic.type == TOKEN_FUNC) {
  311. // These are arguments to a function
  312. cur = cur->generic.prev;
  313. if (EXP_DoFunction (cur))
  314. return EXP_Error (EXP_E_SYNTAX,
  315. va
  316. ("Invalid number of arguments to function '%s'.",
  317. cur->func.func->str));
  318. } else {
  319. if (EXP_ContainsCommas (cur))
  320. return EXP_Error (EXP_E_SYNTAX,
  321. "Comma used outside of a function argument list.");
  322. temp = cur;
  323. cur = cur->generic.next;
  324. EXP_RemoveToken (temp); /* Remove parentheses, leaving value
  325. behind */
  326. EXP_RemoveToken (cur->generic.next);
  327. }
  328. }
  329. }
  330. /* Next, evaluate all operators in order of operations */
  331. for (i = 0; optable[i].func; i++) {
  332. for (cur = chain->generic.next; cur->generic.type != TOKEN_CPAREN;
  333. cur = cur->generic.next) {
  334. if (cur->generic.type == TOKEN_OP && cur->op.op == optable + i
  335. && cur->generic.next) {
  336. // If a unary operator is in our way, it gets evaluated early
  337. if (cur->generic.next->generic.type == TOKEN_OP)
  338. if (EXP_DoUnary (cur->generic.next))
  339. return EXP_Error (EXP_E_SYNTAX,
  340. va
  341. ("Unary operator '%s' not followed by a unary operator or numerical value.",
  342. cur->generic.next->op.op->str));
  343. if (optable[i].operands == 1
  344. && cur->generic.next->generic.type == TOKEN_NUM) {
  345. cur->generic.next->num.value =
  346. optable[i].func (cur->generic.next->num.value, 0);
  347. temp = cur;
  348. cur = cur->generic.next;
  349. EXP_RemoveToken (temp);
  350. } else if (cur->generic.prev->generic.type == TOKEN_NUM
  351. && cur->generic.next->generic.type == TOKEN_NUM) {
  352. cur->generic.prev->num.value =
  353. optable[i].func (cur->generic.prev->num.value,
  354. cur->generic.next->num.value);
  355. temp = cur;
  356. cur = cur->generic.prev;
  357. EXP_RemoveToken (temp->generic.next);
  358. EXP_RemoveToken (temp);
  359. }
  360. }
  361. }
  362. }
  363. return EXP_E_NORMAL;
  364. }
  365. void
  366. EXP_RemoveToken (token * tok)
  367. {
  368. tok->generic.prev->generic.next = tok->generic.next;
  369. tok->generic.next->generic.prev = tok->generic.prev;
  370. free (tok);
  371. }
  372. void
  373. EXP_DestroyTokens (token * chain)
  374. {
  375. token *temp;
  376. for (; chain; chain = temp) {
  377. temp = chain->generic.next;
  378. free (chain);
  379. }
  380. }
  381. double
  382. EXP_Evaluate (char *str)
  383. {
  384. token *chain;
  385. double res;
  386. EXP_ERROR = EXP_E_NORMAL;
  387. if (!(chain = EXP_ParseString (str)))
  388. return 0;
  389. if (EXP_Validate (chain)) {
  390. EXP_DestroyTokens (chain);
  391. return 0;
  392. }
  393. if (EXP_SimplifyTokens (chain)) {
  394. EXP_DestroyTokens (chain);
  395. return 0;
  396. }
  397. res = chain->generic.next->num.value;
  398. EXP_DestroyTokens (chain);
  399. return res;
  400. }
  401. void
  402. EXP_InsertTokenAfter (token * spot, token * new)
  403. {
  404. spot->generic.next->generic.prev = new;
  405. new->generic.next = spot->generic.next;
  406. new->generic.prev = spot;
  407. spot->generic.next = new;
  408. }
  409. exp_error_t
  410. EXP_Validate (token * chain)
  411. {
  412. token *cur, *new;
  413. int paren = 0;
  414. for (cur = chain; cur->generic.next; cur = cur->generic.next) {
  415. if (cur->generic.type == TOKEN_OPAREN)
  416. paren++;
  417. if (cur->generic.type == TOKEN_CPAREN)
  418. paren--;
  419. /* Implied multiplication */
  420. if ((cur->generic.type == TOKEN_NUM && (cur->generic.next->generic.type == TOKEN_OPAREN || // 5(1+1)
  421. cur->generic.next->generic.type == TOKEN_FUNC || // 5 sin (1+1)
  422. (cur->generic.next->generic.type == TOKEN_OP && cur->generic.next->op.op->operands == 1))) || // 5!(1+1)
  423. (cur->generic.type == TOKEN_CPAREN && (cur->generic.next->generic.type == TOKEN_NUM || // (1+1)5
  424. cur->generic.next->generic.type == TOKEN_OPAREN))) // (1+1)(1+1)
  425. {
  426. new = EXP_NewToken ();
  427. new->generic.type = TOKEN_OP;
  428. new->op.op = EXP_FindOpByStr ("*");
  429. EXP_InsertTokenAfter (cur, new);
  430. } else if ((cur->generic.type == TOKEN_OP
  431. || cur->generic.type == TOKEN_OPAREN)
  432. && cur->generic.next->generic.type == TOKEN_OP) {
  433. if (cur->generic.next->op.op->func == OP_Sub) /* Stupid hack for
  434. negation */
  435. cur->generic.next->op.op = EXP_FindOpByStr ("neg");
  436. else if (cur->generic.next->op.op->operands == 2)
  437. return EXP_Error (EXP_E_SYNTAX,
  438. va
  439. ("Operator '%s' does not follow a number or numerical value.",
  440. cur->generic.next->op.op->str));
  441. } else if (cur->generic.type == TOKEN_FUNC
  442. && cur->generic.next->generic.type != TOKEN_OPAREN)
  443. return EXP_Error (EXP_E_SYNTAX,
  444. va
  445. ("Function '%s' called without an argument list.",
  446. cur->func.func->str));
  447. else if (cur->generic.type == TOKEN_COMMA
  448. &&
  449. ((cur->generic.prev->generic.type != TOKEN_CPAREN
  450. && cur->generic.prev->generic.type != TOKEN_NUM)
  451. || paren <= 1))
  452. return EXP_Error (EXP_E_SYNTAX,
  453. "Comma used outside of a function or after a non-number.");
  454. else if (cur->generic.type == TOKEN_OP
  455. && cur->generic.next->generic.type == TOKEN_CPAREN)
  456. return EXP_Error (EXP_E_SYNTAX,
  457. va ("Operator '%s' is missing an operand.",
  458. cur->op.op->str));
  459. else if (cur->generic.type == TOKEN_NUM
  460. && cur->generic.next->generic.type == TOKEN_NUM)
  461. return EXP_Error (EXP_E_SYNTAX,
  462. "Double number error (two numbers next two each other without an operator).");
  463. else if (cur->generic.type == TOKEN_OPAREN
  464. && cur->generic.next->generic.type == TOKEN_CPAREN)
  465. return EXP_Error (EXP_E_PAREN, "Empty parentheses found.");
  466. }
  467. paren--;
  468. if (paren)
  469. return EXP_Error (EXP_E_PAREN, "Parenthesis mismatch.");
  470. return 0;
  471. }
  472. void
  473. EXP_PrintTokens (token * chain)
  474. {
  475. for (; chain; chain = chain->generic.next)
  476. switch (chain->generic.type) {
  477. case TOKEN_OPAREN:
  478. printf ("(");
  479. break;
  480. case TOKEN_CPAREN:
  481. printf (")");
  482. break;
  483. case TOKEN_COMMA:
  484. printf (",");
  485. break;
  486. case TOKEN_NUM:
  487. printf ("%f", chain->num.value);
  488. break;
  489. case TOKEN_OP:
  490. printf ("%s", chain->op.op->str);
  491. break;
  492. case TOKEN_FUNC:
  493. printf ("%s", chain->func.func->str);
  494. break;
  495. case TOKEN_GENERIC:
  496. break;
  497. }
  498. printf ("\n");
  499. }