Compare commits

...

2 Commits

Author SHA1 Message Date
r4
b4ea1650b7 make EVERYTHING static 2021-11-23 17:16:21 +01:00
r4
e3c466e321 cleaner interface for defining functions 2021-11-23 17:13:59 +01:00

75
main.c
View File

@ -22,10 +22,10 @@ typedef struct Tok {
} Tok; } Tok;
#define TOKS_CAP 65536 #define TOKS_CAP 65536
Tok toks[TOKS_CAP]; static Tok toks[TOKS_CAP];
size_t toks_size = 0; static size_t toks_size = 0;
uint8_t op_prec[256] = { static uint8_t op_prec[256] = {
['('] = 0, /* A precedence of 0 is reserved for delimiters. */ ['('] = 0, /* A precedence of 0 is reserved for delimiters. */
[')'] = 0, [')'] = 0,
[','] = 0, [','] = 0,
@ -37,7 +37,7 @@ uint8_t op_prec[256] = {
}; };
#define OP_PREC(tok_char) (op_prec[(size_t)tok_char]) #define OP_PREC(tok_char) (op_prec[(size_t)tok_char])
enum { static enum {
OrderLtr, OrderLtr,
OrderRtl, OrderRtl,
} op_order[256] = { } op_order[256] = {
@ -54,12 +54,27 @@ enum {
#define IS_FLOAT(c) ((c >= '0' && c <= '9') || c == '.') #define IS_FLOAT(c) ((c >= '0' && c <= '9') || c == '.')
#define IS_ALPHA(c) ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) #define IS_ALPHA(c) ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
void push_tok(Tok t) { typedef struct Function {
const char *name;
real (*func)(real *args);
size_t n_args;
} Function;
#define FUNCTIONS_CAP 256
static Function functions[FUNCTIONS_CAP];
static size_t functions_size = 0;
static void push_tok(Tok t) {
if (toks_size+1 < TOKS_CAP) if (toks_size+1 < TOKS_CAP)
toks[toks_size++] = t; toks[toks_size++] = t;
} }
void tokenize(char *expr) { static void add_func(const char *name, real (*func)(real *args), size_t n_args) {
if (functions_size+1 < FUNCTIONS_CAP)
functions[functions_size++] = (Function){.name = name, .func = func, .n_args = n_args};
}
static void tokenize(char *expr) {
push_tok((Tok){.kind = TokOp, .Char = '('}); push_tok((Tok){.kind = TokOp, .Char = '('});
size_t paren_depth = 0; size_t paren_depth = 0;
@ -137,7 +152,7 @@ void tokenize(char *expr) {
push_tok((Tok){.kind = TokOp, .Char = ')'}); push_tok((Tok){.kind = TokOp, .Char = ')'});
} }
void print_toks() { static void print_toks() {
for (size_t i = 0; i < toks_size; i++) { for (size_t i = 0; i < toks_size; i++) {
switch (toks[i].kind) { switch (toks[i].kind) {
case TokOp: case TokOp:
@ -162,12 +177,12 @@ void print_toks() {
} }
/* Delete tokens from begin to end (excluding end itself). */ /* Delete tokens from begin to end (excluding end itself). */
void del_toks(Tok *begin, Tok *end) { static void del_toks(Tok *begin, Tok *end) {
memmove(begin, end, (toks_size - (end - toks)) * sizeof(Tok)); memmove(begin, end, (toks_size - (end - toks)) * sizeof(Tok));
toks_size -= end - begin; toks_size -= end - begin;
} }
real eval(Tok *t) { static real eval(Tok *t) {
if (!(t[0].kind == TokOp && OP_PREC(t[0].Char) == 0)) { if (!(t[0].kind == TokOp && OP_PREC(t[0].Char) == 0)) {
fprintf(stderr, "Error: expected delimiter at beginning of expression\n"); fprintf(stderr, "Error: expected delimiter at beginning of expression\n");
exit(1); exit(1);
@ -222,22 +237,23 @@ real eval(Tok *t) {
t -= 2; t -= 2;
real outer_res; real outer_res;
if (strcmp(t[1].Str, "sqrt") == 0) { bool func_found = false;
if (arg_results_size != 1) { for (size_t i = 0; i < functions_size; i++) {
fprintf(stderr, "Error: function sqrt() requires exactly 1 argument\n"); if (strcmp(t[1].Str, functions[i].name) == 0) {
exit(1); func_found = true;
if (arg_results_size != functions[i].n_args) {
const char *plural = functions[i].n_args == 1 ? "" : "s";
fprintf(stderr, "Error: function %s() requires exactly 1 argument%s\n", functions[i].name, plural);
exit(1);
}
outer_res = functions[i].func(arg_results);
} }
outer_res = sqrt(arg_results[0]); }
} else if (strcmp(t[1].Str, "pow") == 0) { if (!func_found) {
if (arg_results_size != 2) { fprintf(stderr, "Error: unknown function: %s()\n", t[1].Str);
fprintf(stderr, "Error: function pow() requires exactly 2 arguments\n");
exit(1);
}
outer_res = pow(arg_results[0], arg_results[1]);
} else {
fprintf(stderr, "Error: unknown function name: %s\n", t[1].Str);
exit(1); exit(1);
} }
t[1].kind = TokNum; t[1].kind = TokNum;
t[1].Num = outer_res; t[1].Num = outer_res;
} }
@ -282,18 +298,31 @@ real eval(Tok *t) {
} }
} }
void cleanup() { static void cleanup() {
for (size_t i = 0; i < toks_size; i++) { for (size_t i = 0; i < toks_size; i++) {
if (toks[i].kind == TokFunc) if (toks[i].kind == TokFunc)
free(toks[i].Str); free(toks[i].Str);
} }
} }
static real fn_sqrt(real *args) { return sqrt(args[0]); }
static real fn_pow(real *args) { return pow(args[0], args[1]); }
static real fn_mod(real *args) { return fmod(args[0], args[1]); }
static real fn_round(real *args) { return round(args[0]); }
static real fn_floor(real *args) { return floor(args[0]); }
static real fn_ceil(real *args) { return ceil(args[0]); }
int main(int argc, char **argv) { int main(int argc, char **argv) {
if (argc != 2 || strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) { if (argc != 2 || strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) {
fprintf(stderr, "Usage: ./exp \"<expression>\"\n"); fprintf(stderr, "Usage: ./exp \"<expression>\"\n");
exit(1); exit(1);
} }
add_func("sqrt", fn_sqrt, 1);
add_func("pow", fn_pow, 2);
add_func("mod", fn_mod, 2);
add_func("round", fn_round, 1);
add_func("floor", fn_floor, 1);
add_func("ceil", fn_ceil, 1);
tokenize(argv[1]); tokenize(argv[1]);
print_toks(); print_toks();
real res = eval(toks); real res = eval(toks);