diff options
author | thing1 <thing1@seacrossedlovers.xyz> | 2025-06-17 20:41:36 +0100 |
---|---|---|
committer | thing1 <thing1@seacrossedlovers.xyz> | 2025-06-17 20:41:36 +0100 |
commit | d07deb178676ca42778524e97f3b9729c83bff99 (patch) | |
tree | 8b52e65f796c925960bc2ef89b550429973168f8 |
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile | 16 | ||||
-rw-r--r-- | expr.c | 215 | ||||
-rw-r--r-- | expr.h | 61 | ||||
-rw-r--r-- | sheets.c | 268 | ||||
-rw-r--r-- | sheets.h | 8 |
6 files changed, 570 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9eb9543 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.o +sheets diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ad9f39f --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +CFLAGS = -std=c23 -Os -ggdb +LIBS = -lm + +SRC = sheets.c expr.c +OBJ = $(SRC:.c=.o) + +all: sheets + +.c.o: + cc -c ${CFLAGS} $< + +sheets: ${OBJ} + cc ${OBJ} -o sheets ${CFLAGS} ${LIBS} + +clean: + rm -rf *.o sheets @@ -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); +} @@ -0,0 +1,61 @@ +typedef struct sheet sheet; + +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; + +extern char *in; +extern char *Pin; + +char isop(char c); +void consumeleading(char c); +expr *parseExpr(); +loc *parseLoc(); +postfix *parsePostfix(); +value *parseValue(); +expr *parseExpr(); +double evalValue(value *v, sheet *s); +double evalPostfix(value *v, postfix *p, sheet *s); +double evalExpr(expr *e, sheet *s); +expr *makeNumber(int n); +void freeExpr(expr *e); diff --git a/sheets.c b/sheets.c new file mode 100644 index 0000000..c9cba5d --- /dev/null +++ b/sheets.c @@ -0,0 +1,268 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <ctype.h> +#include <string.h> +#include <math.h> + +#include "expr.h" +#include "sheets.h" + +#define ACCURACY 2 + +#define arg1(s, n) if (strcmp(argv[i], s) == 0) {\ + i++; \ + if (i >= argc) {\ + fprintf(stderr, "expected value after %s option\n", s);\ + exit(1);\ + } + +#define arg2(s, n) else if (strcmp(argv[i], s) == 0) {\ + i++; \ + if (i >= argc) {\ + fprintf(stderr, "expected value after %s option\n", s);\ + exit(1);\ + } + + +void clearstdin() { + while (getc(stdin) != '\n') continue; +} + +void freeSheet(sheet *s) { + for (int y = 0; y < s->height; y++) { + for (int x = 0; x < s->width; x++) + freeExpr(s->cells[y][x]); + free(s->cells[y]); + } + free(s->cells); + free(s); +} + +sheet *makeSheet(int x, int y) { + sheet *s = calloc(1, sizeof(sheet)); + s->width = x; + s->height = y; + + s->cells = calloc(1, sizeof(expr **) * y); + for (int i = 0; i < y; i++) { + s->cells[i] = calloc(1, sizeof(expr *) * x); + for (int j = 0; j < x; j++) + s->cells[i][j] = makeNumber(0); + } + return s; +} + +void export(sheet *s, FILE *f) { + fprintf(f, "%d %d\n", s->width, s->height); + for (int y = 0; y < s->height; y++) { + fprintf(f, "\n|"); + for (int x = 0; x < s->width; x++) + fprintf(f, "%s|", s->cells[y][x]->expr); + } + fprintf(f, "\n"); +} + +sheet *import(FILE *f) { + int width, height; + sheet *s; + + fscanf(f, "%d %d\n\n|", &width, &height); + s = makeSheet(width, height); + + for (int y = 0; y < s->height; y++) { + for (int x = 0; x < s->width; x++) { + in = Pin; + char expr[256] = {0}; + fscanf(f, "%[^|]s", expr); + fscanf(f, "|"); + + memcpy(in, expr, 256); + s->cells[y][x] = parseExpr(); + s->cells[y][x]->expr = strdup(expr); + } + fscanf(f, "\n|"); + } + return s; +} + +void inputln(char *s, int size, FILE *f) { + fflush(stdout); + fgets(s, size, f); + *strchr(s, '\n') = 0; +} + +void printSheetContents(sheet *s, int X, int Y) { + int longest[s->width]; + memset(longest, 0, sizeof(int) * s->width); + + for (int y = 0; y < s->height; y++) + for (int x = 0; x < s->width; x++) + if (strlen(s->cells[y][x]->expr) > longest[x]) + longest[x] = strlen(s->cells[y][x]->expr); + + for (int y = 0; y < s->height; y++) { + printf("\n|"); + for (int x = 0; x < s->width; x++) + if (strcmp(s->cells[y][x]->expr, "0") == 0) + printf(" %*s |", longest[x], " "); + else if (X == x && Y == y) + printf("\e[1;34m %*s \e[0m|", longest[x], s->cells[y][x]->expr); + else + printf(" %*s |", longest[x], s->cells[y][x]->expr); + } + printf("\n"); +} + +void shell(char *prompt, FILE *fin) { + sheet *s; + if (!fin) { +invalid: + int x = -1; + int y = -1; + printf("What size sheet do you want 'x,y': "); + scanf("%d,%d", &x, &y); + clearstdin(); + if (x == -1 || y == -1) { + printf("Please input a valid expression!\n"); + goto invalid; + } + s = makeSheet(x, y); + } else s = import(fin); + + + struct expr *e; + int x = 0; + int y = 0; + FILE *f; + char fname[256]; + + + for (;;) { + in = Pin; + printf("(%d, %d) %s", x, y, prompt); + memset(in, 0, 256); + inputln(in, 256, stdin); + + switch (in[0]) { + case 'u': y--; break; + case 'd': y++; break; + case 'l': x--; break; + case 'r': x++; break; + case 'q': + freeSheet(s); + printf("Exiting\n"); + exit(0); + case 'p': printf("(%d,%d) %s\n", x, y,s->cells[y][x]->expr); break; + case 'P': + printSheetContents(s, x, y); + break; + case 'e': + for (int i = 0; i < s->height; i++) { + printf("\n|"); + for (int j = 0; j < s->width; j++) + if (j == x && i == y) + printf("\e[1;34m %.*f \e[0m|", ACCURACY, evalExpr(s->cells[i][j], s)); + else + printf(" %.*f |", ACCURACY, evalExpr(s->cells[i][j], s)); + } + printf("\n"); + break; + case 'i': + in = Pin; + memset(in, 0, 256); + inputln(in, 256, stdin); + + freeExpr(s->cells[y][x]); + + s->cells[y][x] = parseExpr(); + s->cells[y][x]->expr = strdup(Pin); + + break; + case 's': + printf("file to save to: "); + inputln(fname, 256, stdin); + f = fopen(fname, "w"); + export(s, f); + fclose(f); + break; + case 'o': + printf("file to open: "); + inputln(fname, 256, stdin); + f = fopen(fname, "r"); + if (!f) + fprintf(stderr, "couldn't open file!\n"); + else { + freeSheet(s); + s = import(f); + fclose(f); + } + break; + default: + fprintf(stderr, "unknown cmd '%s'!\n", in); + break; + + } + + if (x > s->width) { + fprintf(stderr, "error: x:%d is out of range (0 - %d)\n", x, s->width); + x = s->width; + } + if (x < 0) { + fprintf(stderr, "error: x:%d is out of range (0 - %d)\n", x, s->width); + x = 0; + } + if (y > s->height) { + fprintf(stderr, "error: y:%d is out of range (0 - %d)\n", y, s->height); + y = s->height; + } + if (y < 0) { + fprintf(stderr, "error: y:%d is out of range (0 - %d)\n", y, s->height); + y = 0; + } + } +} + + +int main(int argc, char **argv) { + setbuf(stdout, NULL); + + in = malloc(256); + Pin = in; + + char *prompt = "> "; + FILE *input = NULL; + + for (int i = 1; i < argc; i++) { + arg1("-p", true) + prompt = argv[i]; + } + else { + input = fopen(argv[i], "r"); + if (!input) { + fprintf(stderr, "couldn't open file\n"); + exit(0); + } + } + } + + shell(prompt, input); + free(in); +} + + + + + + + + + + + + + + + + + diff --git a/sheets.h b/sheets.h new file mode 100644 index 0000000..23b7155 --- /dev/null +++ b/sheets.h @@ -0,0 +1,8 @@ +#ifndef __SHEETS_H_ +#define __SHEETS_H_ +#include "expr.h" +typedef struct sheet { + int width, height; + expr ***cells; +} sheet; +#endif |