basic functions

General support for very basic functions (currently only sqrt()).
This commit is contained in:
r4 2021-11-21 18:15:22 +01:00
parent 7a65de22bb
commit d1cf60794d
1 changed files with 60 additions and 1 deletions

61
main.c
View File

@ -11,11 +11,13 @@ typedef struct Tok {
enum { enum {
TokOp, TokOp,
TokNum, TokNum,
TokFunc,
} kind; } kind;
union { union {
real Num; real Num;
char Char; char Char;
char *Str;
}; };
} Tok; } Tok;
@ -47,6 +49,7 @@ 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'))
void push_tok(Tok t) { void push_tok(Tok t) {
if (toks_size+1 < TOKS_CAP) if (toks_size+1 < TOKS_CAP)
@ -85,6 +88,23 @@ void tokenize(char *expr) {
continue; continue;
} }
if (IS_ALPHA(c)) {
char *buf = malloc(32);
buf[0] = c;
size_t i = 1;
while (i < 31 && IS_ALPHA(curr[i])) {
buf[i] = curr[i];
i++;
}
curr += i - 1;
buf[i++] = 0;
push_tok((Tok){.kind = TokFunc, .Str = buf});
can_be_neg_num = false;
continue;
}
if (c == '(') if (c == '(')
paren_depth++; paren_depth++;
else if (c == ')') { else if (c == ')') {
@ -135,6 +155,9 @@ void print_toks() {
case TokNum: case TokNum:
printf("%.2f ", toks[i].Num); printf("%.2f ", toks[i].Num);
break; break;
case TokFunc:
printf("%s ", toks[i].Str);
break;
default: default:
fprintf(stderr, "Error: unhandled token\n"); fprintf(stderr, "Error: unhandled token\n");
exit(1); exit(1);
@ -160,11 +183,39 @@ real eval(Tok *t) {
real res = eval(t + 1); real res = eval(t + 1);
size_t i; size_t i;
for (i = 2; !(t[i].kind == TokOp && t[i].Char == ')'); i++); for (i = 2; !(t[i].kind == TokOp && t[i].Char == ')'); i++);
del_toks(t + 1, t + i); del_toks(t + 2, t + i + 1);
/* Put the newly evaluated value into place. */ /* Put the newly evaluated value into place. */
t[1].kind = TokNum; t[1].kind = TokNum;
t[1].Num = res; t[1].Num = res;
} }
/* Collapse function. */
if (t[1].kind == TokFunc) {
print_toks();
if (!(t[2].kind == TokOp && t[2].Char == '(') || t + 2 >= toks + toks_size) {
fprintf(stderr, "Error: expected '(' token after function\n");
exit(1);
}
real inner_res = eval(t + 2);
size_t i;
for (i = 3; !(t[i].kind == TokOp && t[i].Char == ')'); i++);
del_toks(t + 2, t + i + 1);
real outer_res;
if (strcmp(t[1].Str, "sqrt") == 0) {
outer_res = sqrt(inner_res);
} else {
fprintf(stderr, "Error: unknown function name: %s\n", t[1].Str);
exit(1);
}
t[1].kind = TokNum;
t[1].Num = outer_res;
print_toks();
}
if (!(t[0].kind == TokOp && t[1].kind == TokNum && t[2].kind == TokOp)) { if (!(t[0].kind == TokOp && t[1].kind == TokNum && t[2].kind == TokOp)) {
fprintf(stderr, "Error: invalid token order\n"); fprintf(stderr, "Error: invalid token order\n");
@ -205,6 +256,13 @@ real eval(Tok *t) {
} }
} }
void cleanup() {
for (size_t i = 0; i < toks_size; i++) {
if (toks[i].kind == TokFunc)
free(toks[i].Str);
}
}
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");
@ -214,4 +272,5 @@ int main(int argc, char **argv) {
print_toks(); print_toks();
real res = eval(toks); real res = eval(toks);
printf("Result: %f\n", res); printf("Result: %f\n", res);
cleanup();
} }