summaryrefslogtreecommitdiff
path: root/expr.c
diff options
context:
space:
mode:
Diffstat (limited to 'expr.c')
-rw-r--r--expr.c215
1 files changed, 215 insertions, 0 deletions
diff --git a/expr.c b/expr.c
new file mode 100644
index 0000000..f50d42c
--- /dev/null
+++ b/expr.c
@@ -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);
+}