diff options
Diffstat (limited to 'expr.c')
-rw-r--r-- | expr.c | 215 |
1 files changed, 215 insertions, 0 deletions
@@ -0,0 +1,215 @@ +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <math.h> +#include <string.h> + +#include "sheets.h" + +/* +EXPR := VALUE +| VALUE POSTFIX +| '(' EXPR ')' + +VALUE := NUM +| LOC + +POSTFIX := OP EXPR +OP := '+' | '-' | '*' | '/' | '^' +lOC := '[' NUM ',' NUM ']' +*/ + +#define TODO(s) fprintf(stderr, "TODO:%d: %s\n", __LINE__, s); exit(1); + +typedef struct expr expr; +typedef struct value value; /* for recursive case */ +typedef struct postfix postfix; + +typedef enum exprType { + VALUE, + POSTFIX, + NESTED, +} exprType; + +typedef enum valueType { + NUM, + LOC, +} valueType; + +typedef struct loc { + int x, y; +} loc; + +typedef struct expr { + char *expr; + exprType t; + union { + expr *nested; + struct { + value *value; + postfix *postfix; + }; + }; +} expr; + +typedef struct postfix { + char op; + expr *expr; +} postfix; + +typedef struct value { + valueType t; + union { + int num; + loc *loc; + }; +} value; + +char isop(char c) { + switch (c) { + case '+': + case '-': + case '*': + case '/': + case '^': + return c; + } + return 0; +} + +char *in; +char *Pin; + +void consumeleading(char c) { +loop: + if (*in == c) { + in++; + goto loop; + } +} + +expr *parseExpr(); + +loc *parseLoc() { + consumeleading(' '); + loc *l = malloc(sizeof(loc)); + if (*in == '[') { + int i; + sscanf(in, "[%d,%d]%n", &l->x, &l->y, &i); + if (i != 0) { + in += i; + return l; + } + TODO("ERROR"); + } + return NULL; +} + +postfix *parsePostfix() { + consumeleading(' '); + postfix *p = malloc(sizeof(postfix)); + if (p->op = isop(*in)) { + in++; + if (p->expr = parseExpr()) + return p; + } + return NULL; +} + +value *parseValue() { + consumeleading(' '); + value *v = malloc(sizeof(value)); + if (v->loc = parseLoc()) { + v->t = LOC; + return v; + } + else if (isdigit(*in)) { + int i; + sscanf(in, "%d%n", &v->num, &i); + if (i != 0) { + in += i; + v->t = NUM; + return v; + } + TODO("ERROR"); + } + return NULL; +} + +expr *parseExpr() { + consumeleading(' '); + expr *e = malloc(sizeof(expr)); + if (e->value = parseValue()) { + e->t = VALUE; + if (e->postfix = parsePostfix()) + e->t = POSTFIX; + + return e; + } + + else if (*in == '(') { + in++; + if (e->nested = parseExpr()) + if (*in == ')') { + e->t = NESTED; + return e; + } + TODO("ERROR"); + } + + return NULL; +} + +double evalExpr(expr *e, sheet *s); + +double evalValue(value *v, sheet *s) { + if (v->t == NUM) return v->num; + return evalExpr(s->cells[v->loc->y][v->loc->x], s); +} + +double evalPostfix(value *v, postfix *p, sheet *s) { + switch (p->op) { + case '+': return evalValue(v, s) + evalExpr(p->expr, s); + case '-': return evalValue(v, s) - evalExpr(p->expr, s); + case '*': return evalValue(v, s) * evalExpr(p->expr, s); + case '/': return evalValue(v, s) / evalExpr(p->expr, s); + case '^': return pow(evalValue(v, s), evalExpr(p->expr, s)); + } +} + +double evalExpr(expr *e, sheet *s) { + switch (e->t) { + case VALUE: return evalValue(e->value, s); break; + case POSTFIX: return evalPostfix(e->value, e->postfix, s); break; + case NESTED: return evalExpr(e->nested, s); + } +} + +expr *makeNumber(int n) { + in = Pin; + memset(in, 0, 255); + snprintf(in, 16, "%d", n); + expr *e = parseExpr(); + e->expr = strdup(Pin); + return e; +} + +void freeValue(value *v) { + if (v->t == LOC) free(v->loc); + free(v); +} + +void freePostfix(value *v, postfix *p) { + freeValue(v); + freeExpr(p->expr); + free(p); +} + +void freeExpr(expr *e) { + switch (e->t) { + case VALUE: freeValue(e->value); break; + case POSTFIX: freePostfix(e->value, e->postfix); break; + case NESTED: freeExpr(e->nested); break; + } + free(e); +} |