From 89eeeff54f0b3ce30be5b046fc3899fdeb5ebb40 Mon Sep 17 00:00:00 2001 From: Mike Buland Date: Sat, 17 Feb 2007 16:31:55 +0000 Subject: Tweaked the stream classes, added an example, and the begining of a formula parser. --- parex/egg.h | 127 +++++++++++ parex/eval.c | 651 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ parex/evaltest.c | 50 +++++ 3 files changed, 828 insertions(+) create mode 100644 parex/egg.h create mode 100644 parex/eval.c create mode 100644 parex/evaltest.c (limited to 'parex') 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 @@ +/* + * EGG.H - part of the EGG system. + * + * Global definitions and function prototypes. + * + * By Shawn Hargreaves. + */ + + +#ifndef __EGG_H__ +#define __EGG_H__ + + +#ifndef TRUE +#define TRUE -1 +#define FALSE 0 +#endif + +#ifndef MIN +#define MIN(x,y) (((x) < (y)) ? (x) : (y)) +#define MAX(x,y) (((x) > (y)) ? (x) : (y)) +#define MID(x,y,z) MAX((x), MIN((y), (z))) +#endif + +#ifndef ABS +#define ABS(x) (((x) >= 0) ? (x) : (-(x))) +#endif + +#ifndef ALLEGRO_H +struct BITMAP; +#endif + + + +/* possible particle commands (ie. language keywords) */ +#define EGG_COMMAND_INIT 1 +#define EGG_COMMAND_SET 2 +#define EGG_COMMAND_IF 3 +#define EGG_COMMAND_LAY 4 +#define EGG_COMMAND_DIE 5 + + + +/* each particle is controlled by a command list */ +typedef struct EGG_COMMAND +{ + int type; /* command type */ + int line; /* source line number */ + char *var; /* variable to be set */ + char *exp; /* arithmetic expression */ + struct EGG_COMMAND *cmd; /* nested, child command list (if) */ + struct EGG_COMMAND *cmd2; /* more child commands (else) */ + struct EGG_COMMAND *next; /* linked list pointer */ +} EGG_COMMAND; + + + +/* each type of particle is defined by one of these structures */ +typedef struct EGG_TYPE +{ + char *name; /* type name */ + EGG_COMMAND *cmd; /* command list */ + struct EGG_TYPE *next; /* linked list pointer */ +} EGG_TYPE; + + + +/* particles store their state variables in these structures */ +typedef struct EGG_VARIABLE +{ + char *name; /* variable name */ + double val; /* current value */ + struct EGG_VARIABLE *next; /* linked list pointer */ +} EGG_VARIABLE; + + + +/* a specific particle instance */ +typedef struct EGG_PARTICLE +{ + int num; /* unique ID number */ + EGG_TYPE *type; /* type information */ + EGG_VARIABLE *var; /* list of variables */ + struct EGG_PARTICLE *next; /* linked list pointer */ +} EGG_PARTICLE; + + + +/* everything you need to know about an EGG particle system */ +typedef struct EGG +{ + int frame; /* animation frame number */ + int part_count; /* number of active particles */ + int part_num; /* particle ID counter */ + EGG_PARTICLE *part; /* list of particle instances */ + EGG_TYPE *type; /* list of particle types */ + EGG_VARIABLE *var; /* list of global variables */ +} EGG; + + + +/* script loading functions */ +EGG *load_egg(char *filename, char *error); +EGG *use_egg(void *data, int length, char *error); + +/* script unloading function */ +void destroy_egg(EGG *egg); + +/* script interpreter */ +int update_egg(EGG *egg, char *error); + +/* renders the current animation state into an Allegro bitmap */ +void lay_egg(EGG *egg, struct BITMAP *bmp); + +/* internal utility functions */ +double evaluate(char *equation, int *error, double (*variable)(char *name)); + +int process_egg_cmd(EGG *egg, EGG_PARTICLE *part, EGG_PARTICLE *other, EGG_COMMAND *cmd, int init, int set, char *error); +void destroy_egg_cmd(EGG_COMMAND *cmd); + +double get_egg_variable(EGG_PARTICLE *part, EGG_PARTICLE *other, EGG *egg, char *name); +void set_egg_variable(EGG_PARTICLE *part, EGG_PARTICLE *other, EGG *egg, char *name, double value); + + + +#endif /* __EGG_H__ */ + 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 @@ +/* + * EVAL.C - part of the EGG system. + * + * Helper routine for evaluating arithmetic expressions. + * + * By Shawn Hargreaves. + */ + + +#include +#include +#include +#include +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#ifndef M_E +#define M_E 2.7182818284590452354 +#endif + +#include "egg.h" + + + +/* convert radians <-> degrees */ +#define DEG2RAD(x) ((x)*M_PI/180.0) +#define RAD2DEG(x) ((x)*180.0/M_PI) + + + +/* internal state information */ +static int evaluate_error; + +static char operator_stack[256]; +static double value_stack[256]; + +static int stack_depth; + +static double current_val; + +static int current_valid; + + + +/* operator tokens */ +#define OP_OPEN_PAREN '(' +#define OP_CLOSE_PAREN ')' +#define OP_PLUS '+' +#define OP_MINUS '-' +#define OP_MUL '*' +#define OP_DIV '/' +#define OP_POWER '^' +#define OP_NEGATE '~' +#define OP_SQRT 'q' +#define OP_SIN 's' +#define OP_COS 'c' +#define OP_TAN 't' +#define OP_ASIN 'S' +#define OP_ACOS 'C' +#define OP_ATAN 'T' +#define OP_LOG 'l' +#define OP_LN 'L' +#define OP_CEIL 'e' +#define OP_FLOOR 'f' +#define OP_ROUND 'r' +#define OP_ABS 'a' +#define OP_MOD '%' +#define OP_EQUALS '=' +#define OP_NOT_EQUALS '#' +#define OP_LESS '<' +#define OP_GREATER '>' +#define OP_LESS_EQUALS '{' +#define OP_GREATER_EQUALS '}' +#define OP_OR '|' +#define OP_AND '&' +#define OP_NOT '!' +#define OP_TERMINATOR ' ' + + + +/* precedence: + * Returns the precedence of the specified operator. + */ +static int precedence(char op) +{ + switch (op) { + + case OP_OPEN_PAREN: + case OP_CLOSE_PAREN: + return 1; + + case OP_TERMINATOR: + return 2; + + case OP_OR: + case OP_AND: + return 3; + + case OP_EQUALS: + case OP_NOT_EQUALS: + case OP_LESS: + case OP_GREATER: + case OP_LESS_EQUALS: + case OP_GREATER_EQUALS: + return 4; + + case OP_PLUS: + case OP_MINUS: + return 5; + + case OP_MUL: + case OP_DIV: + case OP_MOD: + return 6; + + case OP_POWER: + return 7; + + case OP_NEGATE: + case OP_NOT: + case OP_SQRT: + case OP_SIN: + case OP_COS: + case OP_TAN: + case OP_ASIN: + case OP_ACOS: + case OP_ATAN: + case OP_LOG: + case OP_LN: + case OP_CEIL: + case OP_FLOOR: + case OP_ROUND: + case OP_ABS: + return 8; + + default: + return -1; + } +} + + + +/* is_unary: + * Checks whether an operator is unary. + */ +static int is_unary(char op) +{ + if ((op == OP_NEGATE) || (op == OP_SQRT) || (op == OP_SIN) || + (op == OP_COS) || (op == OP_TAN) || (op == OP_ASIN) || + (op == OP_ACOS) || (op == OP_ATAN) || (op == OP_LOG) || + (op == OP_LN) || (op == OP_CEIL) || (op == OP_FLOOR) || + (op == OP_ROUND) || (op == OP_ABS) || (op == OP_NOT)) + return TRUE; + + return FALSE; +} + + + +/* add_value: + * Processes a new numeric value from the input string. + */ +static void add_value(double val) +{ + if (current_valid) { + evaluate_error = TRUE; + return; + } + + current_val = val; + current_valid = TRUE; +} + + + +/* add_operator: + * Processes a new operator from the input string. + */ +static void add_operator(char op) +{ + /* bodge for unary negation */ + if ((op == OP_MINUS) && (!current_valid)) + op = OP_NEGATE; + + /* check validity */ + if ((op == OP_PLUS) || (op == OP_MINUS) || + (op == OP_MUL) || (op == OP_DIV) || + (op == OP_POWER) || (op == OP_MOD) || + (op == OP_EQUALS) || (op == OP_NOT_EQUALS) || + (op == OP_LESS) || (op == OP_GREATER) || + (op == OP_LESS_EQUALS) || (op == OP_GREATER_EQUALS) || + (op == OP_OR) || (op == OP_AND)) { + + if (!current_valid) { + evaluate_error = TRUE; + return; + } + } + + /* evaluate */ + if (op != OP_OPEN_PAREN) { + while ((stack_depth > 0) && + ((precedence(op) <= precedence(operator_stack[stack_depth-1]) && (!is_unary(operator_stack[stack_depth-1]))) || + (precedence(op) < precedence(operator_stack[stack_depth-1]) && (is_unary(operator_stack[stack_depth-1]))))) { + + stack_depth--; + + switch (operator_stack[stack_depth]) { + + case OP_PLUS: + current_val = value_stack[stack_depth] + current_val; + break; + + case OP_MINUS: + current_val = value_stack[stack_depth] - current_val; + break; + + case OP_MUL: + current_val = value_stack[stack_depth] * current_val; + break; + + case OP_DIV: + if (current_val != 0) + current_val = value_stack[stack_depth] / current_val; + else + current_val = 0; + break; + + case OP_POWER: + current_val = pow(value_stack[stack_depth], current_val); + break; + + case OP_NEGATE: + current_val = -current_val; + break; + + case OP_SQRT: + if (current_val >= 0) + current_val = sqrt(current_val); + else + current_val = 0; + break; + + case OP_SIN: + current_val = sin(DEG2RAD(current_val)); + break; + + case OP_COS: + current_val = cos(DEG2RAD(current_val)); + break; + + case OP_TAN: + current_val = tan(DEG2RAD(current_val)); + break; + + case OP_ASIN: + if ((current_val >= -1) && (current_val <= 1)) + current_val = RAD2DEG(asin(current_val)); + else + current_val = 0; + break; + + case OP_ACOS: + if ((current_val >= -1) && (current_val <= 1)) + current_val = RAD2DEG(acos(current_val)); + else + current_val = 0; + break; + + case OP_ATAN: + current_val = RAD2DEG(atan(current_val)); + break; + + case OP_LOG: + if (current_val > 0) + current_val = log10(current_val); + else + current_val = 0; + break; + + case OP_LN: + if (current_val > 0) + current_val = log(current_val); + else + current_val = 0; + break; + + case OP_CEIL: + current_val = ceil(current_val); + break; + + case OP_FLOOR: + current_val = floor(current_val); + break; + + case OP_ROUND: + if (current_val < 0) + current_val = (int)(current_val - 0.5); + else + current_val = (int)(current_val + 0.5); + break; + + case OP_ABS: + current_val = fabs(current_val); + break; + + case OP_MOD: + if (current_val >= 1) + current_val = fmod(value_stack[stack_depth], current_val); + else + current_val = 0; + break; + + case OP_EQUALS: + current_val = (value_stack[stack_depth] == current_val); + break; + + case OP_NOT_EQUALS: + current_val = (value_stack[stack_depth] != current_val); + break; + + case OP_LESS: + current_val = (value_stack[stack_depth] < current_val); + break; + + case OP_GREATER: + current_val = (value_stack[stack_depth] > current_val); + break; + + case OP_LESS_EQUALS: + current_val = (value_stack[stack_depth] <= current_val); + break; + + case OP_GREATER_EQUALS: + current_val = (value_stack[stack_depth] >= current_val); + break; + + case OP_OR: + current_val = ((int)value_stack[stack_depth] || (int)current_val); + break; + + case OP_AND: + current_val = ((int)value_stack[stack_depth] && (int)current_val); + break; + + case OP_NOT: + current_val = !(int)current_val; + break; + + case OP_OPEN_PAREN: + if (op == OP_CLOSE_PAREN) + return; + break; + } + } + } + + /* push onto the stack */ + if (op != OP_CLOSE_PAREN) { + operator_stack[stack_depth] = op; + value_stack[stack_depth] = current_val; + stack_depth++; + current_val = 0; + current_valid = FALSE; + } + else { + if (stack_depth <= 0) + evaluate_error = TRUE; + } +} + + + +/* evaluate: + * Top level evaluation function. + */ +double evaluate(char *equation, int *error, double (*variable)(char *name)) +{ + char buf[256]; + double val; + int i; + + stack_depth = 0; + current_val = 0; + current_valid = FALSE; + evaluate_error = FALSE; + + while ((*equation) && (!evaluate_error)) { + /* skip whitespace */ + while (isspace(*equation)) + equation++; + + switch (*equation) { + + case '+': + /* addition */ + add_operator(OP_PLUS); + equation++; + break; + + case '-': + /* subtraction */ + add_operator(OP_MINUS); + equation++; + break; + + case '*': + /* multiplication */ + add_operator(OP_MUL); + equation++; + break; + + case '/': + /* division */ + add_operator(OP_DIV); + equation++; + break; + + case '^': + /* rasing to a power (_not_ XOR!) */ + add_operator(OP_POWER); + equation++; + break; + + case '%': + /* modulus */ + add_operator(OP_MOD); + equation++; + break; + + case '|': + /* logical or */ + add_operator(OP_OR); + equation++; + break; + + case '&': + /* logical and */ + add_operator(OP_AND); + equation++; + break; + + case '=': + /* equality test (requires dual ==) */ + if (equation[1] == '=') { + add_operator(OP_EQUALS); + equation += 2; + } + else + evaluate_error = TRUE; + break; + + case '!': + /* could be inequality test or logical not */ + if (equation[1] == '=') { + add_operator(OP_NOT_EQUALS); + equation += 2; + } + else { + add_operator(OP_NOT); + equation++; + } + break; + + case '<': + /* could be less than or less/equal test */ + if (equation[1] == '=') { + add_operator(OP_LESS_EQUALS); + equation += 2; + } + else { + add_operator(OP_LESS); + equation++; + } + break; + + case '>': + /* could be greater than or greater/equal test */ + if (equation[1] == '=') { + add_operator(OP_GREATER_EQUALS); + equation += 2; + } + else { + add_operator(OP_GREATER); + equation++; + } + break; + + case '(': + /* open bracket */ + add_operator(OP_OPEN_PAREN); + equation++; + break; + + case ')': + /* close bracket */ + add_operator(OP_CLOSE_PAREN); + equation++; + break; + + case '0': + /* special case for hex constants (0x prefix) */ + if (equation[1] == 'x') { + equation += 2; + + for (i=0; isxdigit(equation[i]); i++) + buf[i] = equation[i]; + + buf[i] = 0; + equation += i; + + val = strtol(buf, NULL, 16); + add_value(val); + break; + } + /* else fall through */ + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + /* floating point constant */ + for (i=0; isdigit(equation[i]) || (equation[i] == '.'); i++) + buf[i] = equation[i]; + + buf[i] = 0; + equation += i; + + val = atof(buf); + add_value(val); + break; + + default: + /* this is a string, could be a variable or function */ + for (i=0; (isalpha(equation[i])) || (equation[i] == '_'); i++) + buf[i] = tolower(equation[i]); + + buf[i] = 0; + equation += i; + + if (strcmp(buf, "pi") == 0) { + /* pi (built in constant) */ + add_value(M_PI); + } + else if (strcmp(buf, "e") == 0) { + /* e (built in constant) */ + add_value(M_E); + } + else if (strcmp(buf, "sqrt") == 0) { + /* square root function */ + add_operator(OP_SQRT); + } + else if (strcmp(buf, "sin") == 0) { + /* sin function */ + add_operator(OP_SIN); + } + else if (strcmp(buf, "cos") == 0) { + /* cos function */ + add_operator(OP_COS); + } + else if (strcmp(buf, "tan") == 0) { + /* tan function */ + add_operator(OP_TAN); + } + else if (strcmp(buf, "asin") == 0) { + /* inverse sin function */ + add_operator(OP_ASIN); + } + else if (strcmp(buf, "acos") == 0) { + /* inverse cos function */ + add_operator(OP_ACOS); + } + else if (strcmp(buf, "atan") == 0) { + /* inverse tan function */ + add_operator(OP_ATAN); + } + else if (strcmp(buf, "log") == 0) { + /* base 10 logarithm function */ + add_operator(OP_LOG); + } + else if (strcmp(buf, "ln") == 0) { + /* natural logarithm function */ + add_operator(OP_LN); + } + else if (strcmp(buf, "ceil") == 0) { + /* round upwards function */ + add_operator(OP_CEIL); + } + else if (strcmp(buf, "floor") == 0) { + /* round downwards function */ + add_operator(OP_FLOOR); + } + else if (strcmp(buf, "round") == 0) { + /* round to nearest integer function */ + add_operator(OP_ROUND); + } + else if (strcmp(buf, "abs") == 0) { + /* absolute value function */ + add_operator(OP_ABS); + } + else if (strcmp(buf, "rand") == 0) { + /* random number between 0 and 1 */ + add_value((rand()&32767)/32767.0); + } + else { + /* user-supplied callback for looking up variables */ + if ((buf[0]) && (variable)) { + add_value(variable(buf)); + } + else { + if (error) + *error = TRUE; + + return 0; + } + } + break; + } + } + + if ((evaluate_error) || (!current_valid)) { + if (error) + *error = TRUE; + + return 0; + } + + /* force a stack flush */ + add_operator(OP_TERMINATOR); + + if (stack_depth != 1) { + if (error) + *error = TRUE; + + return 0; + } + + if (error) + *error = FALSE; + + return value_stack[0]; +} + 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 @@ +/* + * EVALTEST.C - part of the EGG system. + * + * Test program for the expression evaluator. + * + * By Shawn Hargreaves. + */ + + +#define USE_CONSOLE + + +#include +#include +#include + +#include "egg.h" + + + +int main() +{ + char buf[256]; + double result; + int error; + + srand(time(NULL)); + + printf("\nTest program for the EGG expression evaluator module.\n\nEnter a formula, or a blank line to quit.\n\n\n"); + + for (;;) { + printf("> "); + fflush(stdout); + + if (!fgets(buf, sizeof(buf), stdin) || (!buf[0])) + break; + + result = evaluate(buf, &error, NULL); + + if (error) + printf("\nError in expression!\n\n\n"); + else + printf("\nevaluate(\"%s\") = %f\n\n\n", buf, result); + } + + printf("\n\n"); + + return 0; +} + -- cgit v1.2.3