Mercurial > hg > dmlib
view dmeval.c @ 495:30145d17aebd
Move certain editor related targets to the actual TARGETS variable, and
remove them from being separately specified in the "make clean" target,
thusly cleaning them only when editor is enabled in build configuration.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Fri, 16 Nov 2012 19:18:03 +0200 |
parents | 32250b436bca |
children | 05eb24a608f0 |
line wrap: on
line source
#include "dmeval.h" #include <stdio.h> #include <math.h> #define DM_MAX_ID_LEN 16 #define DM_MAX_BUF 512 #define DM_STACK_SIZE 512 static DMValue int_clip(DMValue v) { return (v < -1.0f) ? -1.0f : ((v > 1.0f) ? 1.0f : v); } /* Some basic functions */ static const DMEvalId dm_eval_basic[] = { { "sin", ID_FUNC, sin, NULL }, { "cos", ID_FUNC, cos, NULL }, { "clip", ID_FUNC, int_clip, NULL }, }; static const int ndm_eval_basic = sizeof(dm_eval_basic) / sizeof(dm_eval_basic[0]); void dm_eval_err_v(DMEvalContext *ev, const char *fmt, va_list ap) { #if 1 char *tmp = dm_strdup_vprintf(fmt, ap); ev->err = TRUE; if (ev->errStr != NULL) { ev->errStr = dm_strdup_printf("%s%s", ev->errStr, tmp); dmFree(tmp); } else ev->errStr = tmp; #else vfprintf(stdout, fmt, ap); #endif } void dm_eval_err(DMEvalContext *ev, const char *fmt, ...) { va_list ap; va_start(ap, fmt); dm_eval_err_v(ev, fmt, ap); va_end(ap); } DMEvalId *dm_eval_find_id(DMEvalContext *ev, const char *name) { int i; if (ev->ids == NULL) return NULL; for (i = 0; i < ev->nids; i++) if (strcmp(ev->ids[i].name, name) == 0) return &(ev->ids[i]); return NULL; } static DMEvalId *dm_eval_add_id(DMEvalContext *ev, const char *name, const int type) { DMEvalId *id = dm_eval_find_id(ev, name); if (id != NULL) return NULL; ev->ids = dmRealloc(ev->ids, sizeof(DMEvalId) * (ev->nids + 1)); if (ev->ids == NULL) { dm_eval_err(ev, "Could not reallocate eval ids array (#%d). Fatal error.\n", ev->nids + 1); return NULL; } id = &(ev->ids[ev->nids]); ev->nids++; id->name = dm_strdup(name); id->type = type; id->func = NULL; id->var = NULL; return id; } DMEvalId *dm_eval_add_var(DMEvalContext *ev, const char *name, DMValue *var) { DMEvalId *id = dm_eval_add_id(ev, name, ID_VAR); if (id == NULL) return NULL; id->var = var; return id; } DMEvalId *dm_eval_add_func(DMEvalContext *ev, const char *name, DMValue (*func)(DMValue)) { DMEvalId *id = dm_eval_add_id(ev, name, ID_VAR); if (id == NULL) return NULL; id->func = func; return id; } DMEvalContext *dm_eval_new(void) { int i; DMEvalContext *ev = dmCalloc(1, sizeof(DMEvalContext)); if (ev == NULL) return NULL; for (i = 0; i < ndm_eval_basic; i++) { const DMEvalId *id = &dm_eval_basic[i]; DMEvalId *nid = dm_eval_add_id(ev, id->name, id->type); nid->func = id->func; } return ev; } void dm_eval_free(DMEvalNode *node) { while (node != NULL) { DMEvalNode *next = node->next; if (node->subexpr != NULL) dm_eval_free(node->subexpr); dmFree(node); node = next; } } void dm_eval_clear_err(DMEvalContext *ev) { if (ev == NULL) return; dmFree(ev->errStr); ev->err = FALSE; ev->errStr = NULL; } void dm_eval_close(DMEvalContext *ev) { int i; if (ev == NULL) return; for (i = 0; i < ev->nids; i++) dmFree(ev->ids[i].name); dmFree(ev->ids); dm_eval_clear_err(ev); dmFree(ev); } static DMEvalNode *dm_eval_pop_node(DMEvalNode **list) { DMEvalNode *node; if (*list == NULL) return NULL; node = (*list)->prev; if (*list != node) { node->prev->next = NULL; (*list)->prev = node->prev; } else *list = NULL; return node; } static DMEvalNode *dm_eval_insert_node(DMEvalNode **list, DMEvalNode *node) { if (*list != NULL) { node->prev = (*list)->prev; (*list)->prev->next = node; (*list)->prev = node; } else { *list = node; node->prev = *list; } node->next = NULL; return node; } static DMEvalNode *dm_eval_push_node(DMEvalNode **list, DMEvalNode *src) { DMEvalNode *node = dmCalloc(1, sizeof(DMEvalNode)); if (node == NULL) return NULL; node->op = src->op; node->val = src->val; node->id = src->id; node->subexpr = src->subexpr; return dm_eval_insert_node(list, node); } static DMEvalNode *dm_eval_add_node(DMEvalNode **list, const int op) { DMEvalNode *node = dmCalloc(1, sizeof(DMEvalNode)); if (node == NULL) return NULL; node->op = op; return dm_eval_insert_node(list, node); } enum { PARSE_NONE = 0x0000, PARSE_START = 0x1000, PARSE_END = 0x2000, PARSE_ERROR = 0x8000, PARSE_IDENT = 0x0001, PARSE_WS = 0x0002, PARSE_CONST = 0x0004, PARSE_OPER = 0x0008, PARSE_SUBEXPR = 0x0010, }; #define DM_CHECK(x) { if (mode & PARSE_ ## x ) { if (str[0]) strcat(str, " or "); strcat(str, # x ); } } static char *dm_get_mode(int mode) { char str[128] = ""; DM_CHECK(START); DM_CHECK(END); DM_CHECK(IDENT); DM_CHECK(WS); DM_CHECK(CONST); DM_CHECK(OPER); DM_CHECK(SUBEXPR); return dm_strdup(str); } static void dm_set_mode(DMEvalContext *ev, const int mode) { if (mode != PARSE_ERROR && mode != PARSE_START && ev->expect != PARSE_NONE && (mode & ev->expect) == 0) { char *tmp1 = dm_get_mode(ev->expect), *tmp2 = dm_get_mode(mode); dm_eval_err(ev, "Expected [%s], got %s.\n", tmp1, tmp2); dmFree(tmp1); dmFree(tmp2); } ev->prev = ev->mode; ev->mode = mode; } static int dm_eval_parse_expr_do(DMEvalContext *ev, DMEvalNode **list, char **str, int depth) { char *c = *str; char tmpStr[DM_MAX_BUF + 2]; int tmpStrLen = 0; DMEvalNode *node = NULL, *func = NULL; BOOL first = FALSE, decimal = FALSE; ev->expect = PARSE_CONST | PARSE_IDENT | PARSE_SUBEXPR; ev->mode = PARSE_START; while (ev->mode != PARSE_ERROR && ev->mode != PARSE_END) { switch (ev->mode) { case PARSE_WS: // Skip whitespace if (isspace(*c)) c++; else dm_set_mode(ev, PARSE_START); break; case PARSE_SUBEXPR: { char *tmp = c + 1; ev->expect = PARSE_SUBEXPR | PARSE_IDENT | PARSE_CONST; if (func != NULL) { if (dm_eval_parse_expr_do(ev, &(func->subexpr), &tmp, depth + 1) != 0) { dm_eval_err(ev, "Function argument subexpression starting at '%s' contained errors.\n", c); dm_set_mode(ev, PARSE_ERROR); } func = NULL; } else { if ((node = dm_eval_add_node(list, OP_SUBEXPR)) == NULL) dm_set_mode(ev, PARSE_ERROR); else if (dm_eval_parse_expr_do(ev, &(node->subexpr), &tmp, depth + 1) != 0) { dm_eval_err(ev, "Subexpression starting at '%s' contained errors.\n", c); dm_set_mode(ev, PARSE_ERROR); } } if (ev->mode != PARSE_ERROR) { dm_set_mode(ev, PARSE_START); ev->expect = PARSE_OPER | PARSE_END; c = tmp; } } break; case PARSE_START: // Start if (*c == 0) dm_set_mode(ev, PARSE_END); else if (isspace(*c)) { ev->mode = PARSE_WS; } else if (*c == ')') { if (depth > 0) dm_set_mode(ev, PARSE_END); else { dm_eval_err(ev, "Invalid nesting near '%s' (depth %d).\n", c, depth); dm_set_mode(ev, PARSE_ERROR); } c++; } else if (*c == '(') dm_set_mode(ev, PARSE_SUBEXPR); else if (strchr("+-*/<>%&|!^", *c)) dm_set_mode(ev, PARSE_OPER); else if (isdigit(*c) || *c == '.') dm_set_mode(ev, PARSE_CONST); else if (isalpha(*c) || *c == '_') dm_set_mode(ev, PARSE_IDENT); else { dm_eval_err(ev, "Syntax error near '%s' (depth %d).\n", c, depth); dm_set_mode(ev, PARSE_ERROR); } first = TRUE; break; case PARSE_CONST: if (first) { first = FALSE; decimal = FALSE; tmpStrLen = 0; if (isdigit(*c) || *c == '-' || *c == '+' || *c == '.') { if (*c == '.') decimal = TRUE; tmpStr[tmpStrLen++] = *c++; } else { dm_eval_err(ev, "Invalid constant expression near '%s'.\n", c); dm_set_mode(ev, PARSE_ERROR); } } else { if (isdigit(*c) || (*c == '.' && !decimal)) { if (*c == '.') decimal = TRUE; tmpStr[tmpStrLen++] = *c++; } else { tmpStr[tmpStrLen] = 0; if ((node = dm_eval_add_node(list, OP_CONST)) == NULL) { dm_set_mode(ev, PARSE_ERROR); break; } node->val = atof(tmpStr); dm_set_mode(ev, PARSE_START); ev->expect = PARSE_OPER | PARSE_END; } } break; case PARSE_OPER: { int op = OP_NONE; switch (*c) { case '+': op = OP_ADD; c++; break; case '-': op = OP_SUB; c++; break; case '*': op = OP_MUL; c++; break; case '/': op = OP_DIV; c++; break; case '%': op = OP_MOD; c++; break; case '&': op = OP_AND; c++; break; case '|': op = OP_OR; c++; break; case '>': if (c[1] == '>') { c += 2; op = OP_RSHIFT; } else { dm_eval_err(ev, "Syntax error near '%s'.\n", c); dm_set_mode(ev, PARSE_ERROR); } break; case '<': if (c[1] == '<') { c += 2; op = OP_LSHIFT; } else { dm_eval_err(ev, "Syntax error near '%s'.\n", c); dm_set_mode(ev, PARSE_ERROR); } break; default: dm_eval_err(ev, "Unknown operator '%c'.\n", *c); dm_set_mode(ev, PARSE_ERROR); } if (op != OP_NONE) { if ((node = dm_eval_add_node(list, op)) != NULL) { ev->expect = PARSE_IDENT | PARSE_CONST | PARSE_SUBEXPR; dm_set_mode(ev, PARSE_START); } else dm_set_mode(ev, PARSE_ERROR); } } break; case PARSE_IDENT: if (isalnum(*c) || *c == '_') { if (first) { tmpStrLen = 0; first = FALSE; } if (tmpStrLen < DM_MAX_ID_LEN) tmpStr[tmpStrLen++] = *c++; else { tmpStr[tmpStrLen] = 0; dm_eval_err(ev, "Identifier too long! ('%s')\n", tmpStr); } } else { tmpStr[tmpStrLen] = 0; DMEvalId *id = dm_eval_find_id(ev, tmpStr); if (id != NULL) { if ((node = dm_eval_add_node(list, id->type == ID_FUNC ? OP_FUNC : OP_VAR)) != NULL) { node->id = id; if (id->type == ID_FUNC) { func = node; ev->expect = PARSE_SUBEXPR; } else ev->expect = PARSE_END | PARSE_OPER; dm_set_mode(ev, PARSE_START); } else dm_set_mode(ev, PARSE_ERROR); } else { dm_eval_err(ev, "No such identifier '%s'.\n", tmpStr); dm_set_mode(ev, PARSE_ERROR); } } break; } } *str = c; return (ev->mode == PARSE_ERROR) ? -1 : 0; } int dm_eval_parse_expr(DMEvalContext *ev, char *expr, DMEvalNode **result) { int ret; if (ev == NULL || result == NULL) return -1; ret = dm_eval_parse_expr_do(ev, result, &expr, 0); return ret; } static const char *dm_oper_names[OP_NOPERS] = { "NONE", "+", "-", "*", "/", "%", "<<", ">>", "&", "|", "^", "FUNC", "VAR", "SUBEXPR", "CONST" }; static void dm_print_optree_do(DMEvalContext *ev, DMEvalNode *node, const int level) { while (node != NULL) { switch (node->op) { case OP_FUNC: printf("%s(", node->id != NULL ? node->id->name : "?ERROR"); dm_print_optree_do(ev, node->subexpr, level + 1); printf(")"); break; case OP_VAR: printf("%s", node->id != NULL ? node->id->name : "?ERROR"); break; case OP_CONST: printf("%.1f", node->val); break; case OP_SUBEXPR: printf("("); if (node->subexpr != NULL) dm_print_optree_do(ev, node->subexpr, level + 1); else printf("?ERROR"); printf(")"); break; default: if (node->op > 0 && node->op < OP_NOPERS) printf("%s", dm_oper_names[node->op]); else printf("ERROR!"); break; } node = node->next; } } void dm_print_optree(DMEvalContext *ev, DMEvalNode *node) { dm_print_optree_do(ev, node, 0); printf("\n"); } /* operator precedence: HIGHEST "()" "*" "/" "%" "+" "-" "<<" ">>" "&" "^" "|" LOWEST */ int dm_eval_reorder(DMEvalContext *ev, DMEvalNode *node, DMEvalNode **result) { DMEvalNode *list = NULL; while (node != NULL) { DMEvalNode *tmp; switch (node->op) { case OP_SUBEXPR: case OP_FUNC: if ((tmp = dm_eval_push_node(&list, node)) == NULL) return -1; if (dm_eval_reorder(ev, node->subexpr, &(tmp->subexpr)) != 0) return -2; break; case OP_MUL: case OP_DIV: case OP_MOD: if ((tmp = dm_eval_pop_node(&list)) != NULL) { DMEvalNode *sub; if ((sub = dm_eval_add_node(&list, OP_SUBEXPR)) == NULL) return -3; dm_eval_push_node(&(sub->subexpr), tmp); dm_eval_add_node(&(sub->subexpr), node->op); node = node->next; if (node != NULL) { if (node->op == OP_FUNC || node->op == OP_SUBEXPR) { if ((tmp = dm_eval_push_node(&(sub->subexpr), node)) == NULL) return -178; if (dm_eval_reorder(ev, node->subexpr, &(tmp->subexpr)) != 0) return -66; } else dm_eval_push_node(&(sub->subexpr), node); } else return -72; } else return -24; break; case OP_LSHIFT: case OP_RSHIFT: case OP_ADD: case OP_SUB: case OP_AND: case OP_XOR: case OP_OR: case OP_VAR: case OP_CONST: if (dm_eval_push_node(&list, node) == NULL) return -32; break; default: dm_eval_err(ev, "Invalid opcode %d in node %p.\n", node->op, node); return -4; } node = node->next; } *result = list; return 0; } static int dm_eval_get(DMEvalContext *ev, DMEvalNode *node, DMValue *result) { DMValue tmp; if (node == NULL) return -32; switch (node->op) { case OP_VAR: *result = *(node->id->var); break; case OP_CONST: *result = node->val; break; case OP_SUBEXPR: if (dm_eval_exec(ev, node->subexpr, result) != 0) return -1; break; case OP_FUNC: if (dm_eval_exec(ev, node->subexpr, &tmp) != 0) return -1; *result = node->id->func(tmp); break; default: return -16; } return 0; } int dm_eval_exec(DMEvalContext *ev, DMEvalNode *tree, DMValue *presult) { DMValue result; DMEvalNode *node = tree; while (node != NULL) { DMValue tmp; switch (node->op) { case OP_LSHIFT: case OP_RSHIFT: case OP_AND: case OP_XOR: case OP_OR: case OP_MUL: case OP_DIV: case OP_MOD: if (dm_eval_get(ev, node->next, &tmp) != 0) return -6; switch (node->op) { case OP_DIV: if (tmp == 0) { dm_eval_err(ev, "Division by zero.\n"); return -1; } result /= tmp; break; case OP_MUL: result *= tmp; break; case OP_MOD: if (tmp == 0) { dm_eval_err(ev, "Division by zero.\n"); return -1; } result = DMCONVTYPE result % DMCONVTYPE tmp; break; case OP_LSHIFT: result = DMCONVTYPE result << DMCONVTYPE tmp; break; case OP_RSHIFT: result = DMCONVTYPE result >> DMCONVTYPE tmp; break; case OP_AND: result = DMCONVTYPE result & DMCONVTYPE tmp; break; case OP_OR: result = DMCONVTYPE result | DMCONVTYPE tmp; break; case OP_XOR: result = DMCONVTYPE result ^ DMCONVTYPE tmp; break; } node = node->next; break; case OP_ADD: case OP_SUB: if (dm_eval_get(ev, node->next, &tmp) != 0) return -6; switch (node->op) { case OP_ADD: result += tmp; break; case OP_SUB: result -= tmp; break; } node = node->next; break; default: if (dm_eval_get(ev, node, &result) != 0) { dm_eval_err(ev, "Invalid opcode %d in node %p.\n", node->op, node); return -4; } } node = node->next; } *presult = result; return 0; }