aboutsummaryrefslogtreecommitdiff
path: root/parex
diff options
context:
space:
mode:
authorMike Buland <eichlan@xagasoft.com>2007-02-17 16:31:55 +0000
committerMike Buland <eichlan@xagasoft.com>2007-02-17 16:31:55 +0000
commit89eeeff54f0b3ce30be5b046fc3899fdeb5ebb40 (patch)
treea4a8fc2e184df736f61c1dea1e8627dd3667b071 /parex
parent27c626112a7114f9bdc4f7739f9ec05ae9fcbee1 (diff)
downloadlibbu++-89eeeff54f0b3ce30be5b046fc3899fdeb5ebb40.tar.gz
libbu++-89eeeff54f0b3ce30be5b046fc3899fdeb5ebb40.tar.bz2
libbu++-89eeeff54f0b3ce30be5b046fc3899fdeb5ebb40.tar.xz
libbu++-89eeeff54f0b3ce30be5b046fc3899fdeb5ebb40.zip
Tweaked the stream classes, added an example, and the begining of a formula
parser.
Diffstat (limited to 'parex')
-rw-r--r--parex/egg.h127
-rw-r--r--parex/eval.c651
-rw-r--r--parex/evaltest.c50
3 files changed, 828 insertions, 0 deletions
diff --git a/parex/egg.h b/parex/egg.h
new file mode 100644
index 0000000..6830edd
--- /dev/null
+++ b/parex/egg.h
@@ -0,0 +1,127 @@
1/*
2 * EGG.H - part of the EGG system.
3 *
4 * Global definitions and function prototypes.
5 *
6 * By Shawn Hargreaves.
7 */
8
9
10#ifndef __EGG_H__
11#define __EGG_H__
12
13
14#ifndef TRUE
15#define TRUE -1
16#define FALSE 0
17#endif
18
19#ifndef MIN
20#define MIN(x,y) (((x) < (y)) ? (x) : (y))
21#define MAX(x,y) (((x) > (y)) ? (x) : (y))
22#define MID(x,y,z) MAX((x), MIN((y), (z)))
23#endif
24
25#ifndef ABS
26#define ABS(x) (((x) >= 0) ? (x) : (-(x)))
27#endif
28
29#ifndef ALLEGRO_H
30struct BITMAP;
31#endif
32
33
34
35/* possible particle commands (ie. language keywords) */
36#define EGG_COMMAND_INIT 1
37#define EGG_COMMAND_SET 2
38#define EGG_COMMAND_IF 3
39#define EGG_COMMAND_LAY 4
40#define EGG_COMMAND_DIE 5
41
42
43
44/* each particle is controlled by a command list */
45typedef struct EGG_COMMAND
46{
47 int type; /* command type */
48 int line; /* source line number */
49 char *var; /* variable to be set */
50 char *exp; /* arithmetic expression */
51 struct EGG_COMMAND *cmd; /* nested, child command list (if) */
52 struct EGG_COMMAND *cmd2; /* more child commands (else) */
53 struct EGG_COMMAND *next; /* linked list pointer */
54} EGG_COMMAND;
55
56
57
58/* each type of particle is defined by one of these structures */
59typedef struct EGG_TYPE
60{
61 char *name; /* type name */
62 EGG_COMMAND *cmd; /* command list */
63 struct EGG_TYPE *next; /* linked list pointer */
64} EGG_TYPE;
65
66
67
68/* particles store their state variables in these structures */
69typedef struct EGG_VARIABLE
70{
71 char *name; /* variable name */
72 double val; /* current value */
73 struct EGG_VARIABLE *next; /* linked list pointer */
74} EGG_VARIABLE;
75
76
77
78/* a specific particle instance */
79typedef struct EGG_PARTICLE
80{
81 int num; /* unique ID number */
82 EGG_TYPE *type; /* type information */
83 EGG_VARIABLE *var; /* list of variables */
84 struct EGG_PARTICLE *next; /* linked list pointer */
85} EGG_PARTICLE;
86
87
88
89/* everything you need to know about an EGG particle system */
90typedef struct EGG
91{
92 int frame; /* animation frame number */
93 int part_count; /* number of active particles */
94 int part_num; /* particle ID counter */
95 EGG_PARTICLE *part; /* list of particle instances */
96 EGG_TYPE *type; /* list of particle types */
97 EGG_VARIABLE *var; /* list of global variables */
98} EGG;
99
100
101
102/* script loading functions */
103EGG *load_egg(char *filename, char *error);
104EGG *use_egg(void *data, int length, char *error);
105
106/* script unloading function */
107void destroy_egg(EGG *egg);
108
109/* script interpreter */
110int update_egg(EGG *egg, char *error);
111
112/* renders the current animation state into an Allegro bitmap */
113void lay_egg(EGG *egg, struct BITMAP *bmp);
114
115/* internal utility functions */
116double evaluate(char *equation, int *error, double (*variable)(char *name));
117
118int process_egg_cmd(EGG *egg, EGG_PARTICLE *part, EGG_PARTICLE *other, EGG_COMMAND *cmd, int init, int set, char *error);
119void destroy_egg_cmd(EGG_COMMAND *cmd);
120
121double get_egg_variable(EGG_PARTICLE *part, EGG_PARTICLE *other, EGG *egg, char *name);
122void set_egg_variable(EGG_PARTICLE *part, EGG_PARTICLE *other, EGG *egg, char *name, double value);
123
124
125
126#endif /* __EGG_H__ */
127
diff --git a/parex/eval.c b/parex/eval.c
new file mode 100644
index 0000000..7eb9607
--- /dev/null
+++ b/parex/eval.c
@@ -0,0 +1,651 @@
1/*
2 * EVAL.C - part of the EGG system.
3 *
4 * Helper routine for evaluating arithmetic expressions.
5 *
6 * By Shawn Hargreaves.
7 */
8
9
10#include <stdlib.h>
11#include <string.h>
12#include <stdio.h>
13#include <ctype.h>
14#include <math.h>
15
16#ifndef M_PI
17#define M_PI 3.14159265358979323846
18#endif
19
20#ifndef M_E
21#define M_E 2.7182818284590452354
22#endif
23
24#include "egg.h"
25
26
27
28/* convert radians <-> degrees */
29#define DEG2RAD(x) ((x)*M_PI/180.0)
30#define RAD2DEG(x) ((x)*180.0/M_PI)
31
32
33
34/* internal state information */
35static int evaluate_error;
36
37static char operator_stack[256];
38static double value_stack[256];
39
40static int stack_depth;
41
42static double current_val;
43
44static int current_valid;
45
46
47
48/* operator tokens */
49#define OP_OPEN_PAREN '('
50#define OP_CLOSE_PAREN ')'
51#define OP_PLUS '+'
52#define OP_MINUS '-'
53#define OP_MUL '*'
54#define OP_DIV '/'
55#define OP_POWER '^'
56#define OP_NEGATE '~'
57#define OP_SQRT 'q'
58#define OP_SIN 's'
59#define OP_COS 'c'
60#define OP_TAN 't'
61#define OP_ASIN 'S'
62#define OP_ACOS 'C'
63#define OP_ATAN 'T'
64#define OP_LOG 'l'
65#define OP_LN 'L'
66#define OP_CEIL 'e'
67#define OP_FLOOR 'f'
68#define OP_ROUND 'r'
69#define OP_ABS 'a'
70#define OP_MOD '%'
71#define OP_EQUALS '='
72#define OP_NOT_EQUALS '#'
73#define OP_LESS '<'
74#define OP_GREATER '>'
75#define OP_LESS_EQUALS '{'
76#define OP_GREATER_EQUALS '}'
77#define OP_OR '|'
78#define OP_AND '&'
79#define OP_NOT '!'
80#define OP_TERMINATOR ' '
81
82
83
84/* precedence:
85 * Returns the precedence of the specified operator.
86 */
87static int precedence(char op)
88{
89 switch (op) {
90
91 case OP_OPEN_PAREN:
92 case OP_CLOSE_PAREN:
93 return 1;
94
95 case OP_TERMINATOR:
96 return 2;
97
98 case OP_OR:
99 case OP_AND:
100 return 3;
101
102 case OP_EQUALS:
103 case OP_NOT_EQUALS:
104 case OP_LESS:
105 case OP_GREATER:
106 case OP_LESS_EQUALS:
107 case OP_GREATER_EQUALS:
108 return 4;
109
110 case OP_PLUS:
111 case OP_MINUS:
112 return 5;
113
114 case OP_MUL:
115 case OP_DIV:
116 case OP_MOD:
117 return 6;
118
119 case OP_POWER:
120 return 7;
121
122 case OP_NEGATE:
123 case OP_NOT:
124 case OP_SQRT:
125 case OP_SIN:
126 case OP_COS:
127 case OP_TAN:
128 case OP_ASIN:
129 case OP_ACOS:
130 case OP_ATAN:
131 case OP_LOG:
132 case OP_LN:
133 case OP_CEIL:
134 case OP_FLOOR:
135 case OP_ROUND:
136 case OP_ABS:
137 return 8;
138
139 default:
140 return -1;
141 }
142}
143
144
145
146/* is_unary:
147 * Checks whether an operator is unary.
148 */
149static int is_unary(char op)
150{
151 if ((op == OP_NEGATE) || (op == OP_SQRT) || (op == OP_SIN) ||
152 (op == OP_COS) || (op == OP_TAN) || (op == OP_ASIN) ||
153 (op == OP_ACOS) || (op == OP_ATAN) || (op == OP_LOG) ||
154 (op == OP_LN) || (op == OP_CEIL) || (op == OP_FLOOR) ||
155 (op == OP_ROUND) || (op == OP_ABS) || (op == OP_NOT))
156 return TRUE;
157
158 return FALSE;
159}
160
161
162
163/* add_value:
164 * Processes a new numeric value from the input string.
165 */
166static void add_value(double val)
167{
168 if (current_valid) {
169 evaluate_error = TRUE;
170 return;
171 }
172
173 current_val = val;
174 current_valid = TRUE;
175}
176
177
178
179/* add_operator:
180 * Processes a new operator from the input string.
181 */
182static void add_operator(char op)
183{
184 /* bodge for unary negation */
185 if ((op == OP_MINUS) && (!current_valid))
186 op = OP_NEGATE;
187
188 /* check validity */
189 if ((op == OP_PLUS) || (op == OP_MINUS) ||
190 (op == OP_MUL) || (op == OP_DIV) ||
191 (op == OP_POWER) || (op == OP_MOD) ||
192 (op == OP_EQUALS) || (op == OP_NOT_EQUALS) ||
193 (op == OP_LESS) || (op == OP_GREATER) ||
194 (op == OP_LESS_EQUALS) || (op == OP_GREATER_EQUALS) ||
195 (op == OP_OR) || (op == OP_AND)) {
196
197 if (!current_valid) {
198 evaluate_error = TRUE;
199 return;
200 }
201 }
202
203 /* evaluate */
204 if (op != OP_OPEN_PAREN) {
205 while ((stack_depth > 0) &&
206 ((precedence(op) <= precedence(operator_stack[stack_depth-1]) && (!is_unary(operator_stack[stack_depth-1]))) ||
207 (precedence(op) < precedence(operator_stack[stack_depth-1]) && (is_unary(operator_stack[stack_depth-1]))))) {
208
209 stack_depth--;
210
211 switch (operator_stack[stack_depth]) {
212
213 case OP_PLUS:
214 current_val = value_stack[stack_depth] + current_val;
215 break;
216
217 case OP_MINUS:
218 current_val = value_stack[stack_depth] - current_val;
219 break;
220
221 case OP_MUL:
222 current_val = value_stack[stack_depth] * current_val;
223 break;
224
225 case OP_DIV:
226 if (current_val != 0)
227 current_val = value_stack[stack_depth] / current_val;
228 else
229 current_val = 0;
230 break;
231
232 case OP_POWER:
233 current_val = pow(value_stack[stack_depth], current_val);
234 break;
235
236 case OP_NEGATE:
237 current_val = -current_val;
238 break;
239
240 case OP_SQRT:
241 if (current_val >= 0)
242 current_val = sqrt(current_val);
243 else
244 current_val = 0;
245 break;
246
247 case OP_SIN:
248 current_val = sin(DEG2RAD(current_val));
249 break;
250
251 case OP_COS:
252 current_val = cos(DEG2RAD(current_val));
253 break;
254
255 case OP_TAN:
256 current_val = tan(DEG2RAD(current_val));
257 break;
258
259 case OP_ASIN:
260 if ((current_val >= -1) && (current_val <= 1))
261 current_val = RAD2DEG(asin(current_val));
262 else
263 current_val = 0;
264 break;
265
266 case OP_ACOS:
267 if ((current_val >= -1) && (current_val <= 1))
268 current_val = RAD2DEG(acos(current_val));
269 else
270 current_val = 0;
271 break;
272
273 case OP_ATAN:
274 current_val = RAD2DEG(atan(current_val));
275 break;
276
277 case OP_LOG:
278 if (current_val > 0)
279 current_val = log10(current_val);
280 else
281 current_val = 0;
282 break;
283
284 case OP_LN:
285 if (current_val > 0)
286 current_val = log(current_val);
287 else
288 current_val = 0;
289 break;
290
291 case OP_CEIL:
292 current_val = ceil(current_val);
293 break;
294
295 case OP_FLOOR:
296 current_val = floor(current_val);
297 break;
298
299 case OP_ROUND:
300 if (current_val < 0)
301 current_val = (int)(current_val - 0.5);
302 else
303 current_val = (int)(current_val + 0.5);
304 break;
305
306 case OP_ABS:
307 current_val = fabs(current_val);
308 break;
309
310 case OP_MOD:
311 if (current_val >= 1)
312 current_val = fmod(value_stack[stack_depth], current_val);
313 else
314 current_val = 0;
315 break;
316
317 case OP_EQUALS:
318 current_val = (value_stack[stack_depth] == current_val);
319 break;
320
321 case OP_NOT_EQUALS:
322 current_val = (value_stack[stack_depth] != current_val);
323 break;
324
325 case OP_LESS:
326 current_val = (value_stack[stack_depth] < current_val);
327 break;
328
329 case OP_GREATER:
330 current_val = (value_stack[stack_depth] > current_val);
331 break;
332
333 case OP_LESS_EQUALS:
334 current_val = (value_stack[stack_depth] <= current_val);
335 break;
336
337 case OP_GREATER_EQUALS:
338 current_val = (value_stack[stack_depth] >= current_val);
339 break;
340
341 case OP_OR:
342 current_val = ((int)value_stack[stack_depth] || (int)current_val);
343 break;
344
345 case OP_AND:
346 current_val = ((int)value_stack[stack_depth] && (int)current_val);
347 break;
348
349 case OP_NOT:
350 current_val = !(int)current_val;
351 break;
352
353 case OP_OPEN_PAREN:
354 if (op == OP_CLOSE_PAREN)
355 return;
356 break;
357 }
358 }
359 }
360
361 /* push onto the stack */
362 if (op != OP_CLOSE_PAREN) {
363 operator_stack[stack_depth] = op;
364 value_stack[stack_depth] = current_val;
365 stack_depth++;
366 current_val = 0;
367 current_valid = FALSE;
368 }
369 else {
370 if (stack_depth <= 0)
371 evaluate_error = TRUE;
372 }
373}
374
375
376
377/* evaluate:
378 * Top level evaluation function.
379 */
380double evaluate(char *equation, int *error, double (*variable)(char *name))
381{
382 char buf[256];
383 double val;
384 int i;
385
386 stack_depth = 0;
387 current_val = 0;
388 current_valid = FALSE;
389 evaluate_error = FALSE;
390
391 while ((*equation) && (!evaluate_error)) {
392 /* skip whitespace */
393 while (isspace(*equation))
394 equation++;
395
396 switch (*equation) {
397
398 case '+':
399 /* addition */
400 add_operator(OP_PLUS);
401 equation++;
402 break;
403
404 case '-':
405 /* subtraction */
406 add_operator(OP_MINUS);
407 equation++;
408 break;
409
410 case '*':
411 /* multiplication */
412 add_operator(OP_MUL);
413 equation++;
414 break;
415
416 case '/':
417 /* division */
418 add_operator(OP_DIV);
419 equation++;
420 break;
421
422 case '^':
423 /* rasing to a power (_not_ XOR!) */
424 add_operator(OP_POWER);
425 equation++;
426 break;
427
428 case '%':
429 /* modulus */
430 add_operator(OP_MOD);
431 equation++;
432 break;
433
434 case '|':
435 /* logical or */
436 add_operator(OP_OR);
437 equation++;
438 break;
439
440 case '&':
441 /* logical and */
442 add_operator(OP_AND);
443 equation++;
444 break;
445
446 case '=':
447 /* equality test (requires dual ==) */
448 if (equation[1] == '=') {
449 add_operator(OP_EQUALS);
450 equation += 2;
451 }
452 else
453 evaluate_error = TRUE;
454 break;
455
456 case '!':
457 /* could be inequality test or logical not */
458 if (equation[1] == '=') {
459 add_operator(OP_NOT_EQUALS);
460 equation += 2;
461 }
462 else {
463 add_operator(OP_NOT);
464 equation++;
465 }
466 break;
467
468 case '<':
469 /* could be less than or less/equal test */
470 if (equation[1] == '=') {
471 add_operator(OP_LESS_EQUALS);
472 equation += 2;
473 }
474 else {
475 add_operator(OP_LESS);
476 equation++;
477 }
478 break;
479
480 case '>':
481 /* could be greater than or greater/equal test */
482 if (equation[1] == '=') {
483 add_operator(OP_GREATER_EQUALS);
484 equation += 2;
485 }
486 else {
487 add_operator(OP_GREATER);
488 equation++;
489 }
490 break;
491
492 case '(':
493 /* open bracket */
494 add_operator(OP_OPEN_PAREN);
495 equation++;
496 break;
497
498 case ')':
499 /* close bracket */
500 add_operator(OP_CLOSE_PAREN);
501 equation++;
502 break;
503
504 case '0':
505 /* special case for hex constants (0x prefix) */
506 if (equation[1] == 'x') {
507 equation += 2;
508
509 for (i=0; isxdigit(equation[i]); i++)
510 buf[i] = equation[i];
511
512 buf[i] = 0;
513 equation += i;
514
515 val = strtol(buf, NULL, 16);
516 add_value(val);
517 break;
518 }
519 /* else fall through */
520
521 case '1':
522 case '2':
523 case '3':
524 case '4':
525 case '5':
526 case '6':
527 case '7':
528 case '8':
529 case '9':
530 /* floating point constant */
531 for (i=0; isdigit(equation[i]) || (equation[i] == '.'); i++)
532 buf[i] = equation[i];
533
534 buf[i] = 0;
535 equation += i;
536
537 val = atof(buf);
538 add_value(val);
539 break;
540
541 default:
542 /* this is a string, could be a variable or function */
543 for (i=0; (isalpha(equation[i])) || (equation[i] == '_'); i++)
544 buf[i] = tolower(equation[i]);
545
546 buf[i] = 0;
547 equation += i;
548
549 if (strcmp(buf, "pi") == 0) {
550 /* pi (built in constant) */
551 add_value(M_PI);
552 }
553 else if (strcmp(buf, "e") == 0) {
554 /* e (built in constant) */
555 add_value(M_E);
556 }
557 else if (strcmp(buf, "sqrt") == 0) {
558 /* square root function */
559 add_operator(OP_SQRT);
560 }
561 else if (strcmp(buf, "sin") == 0) {
562 /* sin function */
563 add_operator(OP_SIN);
564 }
565 else if (strcmp(buf, "cos") == 0) {
566 /* cos function */
567 add_operator(OP_COS);
568 }
569 else if (strcmp(buf, "tan") == 0) {
570 /* tan function */
571 add_operator(OP_TAN);
572 }
573 else if (strcmp(buf, "asin") == 0) {
574 /* inverse sin function */
575 add_operator(OP_ASIN);
576 }
577 else if (strcmp(buf, "acos") == 0) {
578 /* inverse cos function */
579 add_operator(OP_ACOS);
580 }
581 else if (strcmp(buf, "atan") == 0) {
582 /* inverse tan function */
583 add_operator(OP_ATAN);
584 }
585 else if (strcmp(buf, "log") == 0) {
586 /* base 10 logarithm function */
587 add_operator(OP_LOG);
588 }
589 else if (strcmp(buf, "ln") == 0) {
590 /* natural logarithm function */
591 add_operator(OP_LN);
592 }
593 else if (strcmp(buf, "ceil") == 0) {
594 /* round upwards function */
595 add_operator(OP_CEIL);
596 }
597 else if (strcmp(buf, "floor") == 0) {
598 /* round downwards function */
599 add_operator(OP_FLOOR);
600 }
601 else if (strcmp(buf, "round") == 0) {
602 /* round to nearest integer function */
603 add_operator(OP_ROUND);
604 }
605 else if (strcmp(buf, "abs") == 0) {
606 /* absolute value function */
607 add_operator(OP_ABS);
608 }
609 else if (strcmp(buf, "rand") == 0) {
610 /* random number between 0 and 1 */
611 add_value((rand()&32767)/32767.0);
612 }
613 else {
614 /* user-supplied callback for looking up variables */
615 if ((buf[0]) && (variable)) {
616 add_value(variable(buf));
617 }
618 else {
619 if (error)
620 *error = TRUE;
621
622 return 0;
623 }
624 }
625 break;
626 }
627 }
628
629 if ((evaluate_error) || (!current_valid)) {
630 if (error)
631 *error = TRUE;
632
633 return 0;
634 }
635
636 /* force a stack flush */
637 add_operator(OP_TERMINATOR);
638
639 if (stack_depth != 1) {
640 if (error)
641 *error = TRUE;
642
643 return 0;
644 }
645
646 if (error)
647 *error = FALSE;
648
649 return value_stack[0];
650}
651
diff --git a/parex/evaltest.c b/parex/evaltest.c
new file mode 100644
index 0000000..7e8641e
--- /dev/null
+++ b/parex/evaltest.c
@@ -0,0 +1,50 @@
1/*
2 * EVALTEST.C - part of the EGG system.
3 *
4 * Test program for the expression evaluator.
5 *
6 * By Shawn Hargreaves.
7 */
8
9
10#define USE_CONSOLE
11
12
13#include <stdlib.h>
14#include <stdio.h>
15#include <time.h>
16
17#include "egg.h"
18
19
20
21int main()
22{
23 char buf[256];
24 double result;
25 int error;
26
27 srand(time(NULL));
28
29 printf("\nTest program for the EGG expression evaluator module.\n\nEnter a formula, or a blank line to quit.\n\n\n");
30
31 for (;;) {
32 printf("> ");
33 fflush(stdout);
34
35 if (!fgets(buf, sizeof(buf), stdin) || (!buf[0]))
36 break;
37
38 result = evaluate(buf, &error, NULL);
39
40 if (error)
41 printf("\nError in expression!\n\n\n");
42 else
43 printf("\nevaluate(\"%s\") = %f\n\n\n", buf, result);
44 }
45
46 printf("\n\n");
47
48 return 0;
49}
50