diff options
| author | Franklin Wei <me@fwei.tk> | 2018-05-17 21:41:46 -0400 |
|---|---|---|
| committer | Franklin Wei <me@fwei.tk> | 2018-05-17 21:41:46 -0400 |
| commit | 07232541bda7f3f6aa6286f7e76e3f75e12a46d8 (patch) | |
| tree | 04dd8d579268fc876d9802e92022e6470f58fc42 | |
| parent | cceb7b4a87225e8507407d02ca814e5e1c9d2dac (diff) | |
| download | yacas-07232541bda7f3f6aa6286f7e76e3f75e12a46d8.zip yacas-07232541bda7f3f6aa6286f7e76e3f75e12a46d8.tar.gz yacas-07232541bda7f3f6aa6286f7e76e3f75e12a46d8.tar.bz2 yacas-07232541bda7f3f6aa6286f7e76e3f75e12a46d8.tar.xz | |
new features
| -rw-r--r-- | yacas.c | 224 |
1 files changed, 157 insertions, 67 deletions
@@ -3,6 +3,7 @@ #include <assert.h> #include <ctype.h> #include <math.h> +#include <setjmp.h> #include <stdbool.h> #include <stdio.h> #include <stdint.h> @@ -31,9 +32,7 @@ struct expression { struct expression *operand, *operand2; } specfunc; /* T_SPECFUNC */ }; - - int refcount; -}; +} __attribute__((packed)); enum { OP_POW = 0, OP_MUL, OP_DIV, OP_ADD, OP_SUB, OP_DIFF, OP_ASSIGN, OP_EQUALS, OP_LAST, /* not a real operator: */ OP_LPAREN }; enum { T_CONST = 0, T_VAR, T_SUBEXPR, T_SPECFUNC }; @@ -76,6 +75,15 @@ void setvar(const char *name, struct expression *value, bool constant); bool simp_progress = false; bool memdiag = false; bool interactive = true; +bool vars_ok = true; + +jmp_buf restore; + +void __attribute__((noreturn)) fail(const char *msg) +{ + fprintf(stderr, "%s\n", msg); + longjmp(restore, 1); +} void __attribute__((noreturn)) fatal(const char *msg) { @@ -129,7 +137,7 @@ struct expression *new_op(int op, struct expression *l, struct expression *r) memusage += sizeof(*new); check_mem(); if(check_boolean("memdiag")) - printf("new op %p %s\n", new, op_names[op]); + printf("new op %p %s\n", new, op_names[(int)op]); return new; } @@ -141,7 +149,7 @@ struct expression *new_specfunc(int fn, struct expression *expr) new->specfunc.operand = expr; memusage += sizeof(*new); if(check_boolean("memdiag")) - printf("new specfunc %p %s\n", new, fn_names[fn]); + printf("new specfunc %p %s\n", new, fn_names[(int)fn]); return new; } @@ -291,7 +299,14 @@ bool expr_equals(struct expression *l, struct expression *r) switch(l->type) { case T_SUBEXPR: - ret = false; + if(l->subexpr.op == r->subexpr.op && + ((expr_equals(l->subexpr.left, r->subexpr.left) && expr_equals(l->subexpr.right, r->subexpr.right)) || + (op_commutative[l->subexpr.op] && + expr_equals(l->subexpr.left, r->subexpr.right) && expr_equals(l->subexpr.right, r->subexpr.left)))) + ret = true; + else + ret = false; + free_expr(l); free_expr(r); return ret; @@ -363,7 +378,7 @@ bool is_constvar(const char *name) return false; } -/* value must be persistent */ +/* value must be persistent, but name doesn't need to be */ void setvar(const char *name, struct expression *value, bool constant) { uint32_t idx = var_hash(name) % VARMAP_SIZE; @@ -401,12 +416,36 @@ void dumpvars(void) { printf("%s = ", i->name); print_expr(i->value); - printf("(%s)\n", type_names[i->value->type]); + printf("(%s)\n", type_names[(int)i->value->type]); i = i->next; } } } +void freevars(void) +{ + /* can no longer access vars */ + vars_ok = false; + + for(int idx = 0; idx < VARMAP_SIZE; ++idx) + { + struct variable *i = varmap[idx]; + while(i) + { + free_expr(i->value); + free(i->name); + struct variable *next = i->next; + free(i); + i = next; + } + } +} + +void sigint(int sig) +{ + freevars(); +} + bool is_var(const char *name) { uint32_t idx = var_hash(name) % VARMAP_SIZE; @@ -467,6 +506,8 @@ bool delvar(const char *name) /* check whether a variable is nonzero without any intermediate functions */ bool check_boolean(const char *varname) { + if(!vars_ok) + return false; struct expression *expr = getvar(varname); if(!expr) return false; @@ -601,9 +642,9 @@ struct expression *eval_op(int op, struct expression *lexpr, struct expression * { bool have_old = is_var(rexpr->subexpr.left->varname); - /* set variable (duplicate because setvar frees it */ - struct expression *old; + struct expression *old = NULL; + /* save state */ if(have_old) { old = dup_expr(getvar(rexpr->subexpr.left->varname)); @@ -614,30 +655,32 @@ struct expression *eval_op(int op, struct expression *lexpr, struct expression * /* get derivative */ struct expression *deriv = diff_and_free(eval_expr(lexpr), rexpr->subexpr.left->varname); + /* set variable (duplicate because setvar frees it later */ setvar(rexpr->subexpr.left->varname, dup_expr(rexpr->subexpr.right), false); double val = eval_numexpr_and_free(deriv); - /* don't care about const */ + /* restore */ if(have_old) setvar(rexpr->subexpr.left->varname, old, false); - /* otherwise keep it around */ + else + delvar(rexpr->subexpr.left->varname); return new_const(val); } /* fall through */ default: - fatal("diff operator must take variable as second operand"); + fail("diff operator must take variable as second operand"); } } else if(op == OP_ASSIGN) { if(lexpr->type != T_VAR) - fatal("assignment requires variable as first operand"); + fail("assignment requires variable as first operand"); if(is_constvar(lexpr->varname)) - fatal("cannot change constant value"); + fail("cannot change constant value"); if(expr_references(rexpr, lexpr->varname)) - fatal("circular reference"); + fail("circular reference"); setvar(lexpr->varname, dup_expr(rexpr), false); return dup_expr(rexpr); @@ -675,7 +718,7 @@ struct expression *eval_op(int op, struct expression *lexpr, struct expression * ret = pow(l, r); break; default: - fatal("fall through"); + fail("fall through"); } return new_const(ret); } @@ -701,7 +744,7 @@ struct expression *eval_expr(struct expression *expr) return new_specfunc(expr->specfunc.fn, eval_expr(expr->specfunc.operand)); return new_const(eval_specfunc(expr->specfunc.fn, eval_numexpr(expr->specfunc.operand))); default: - fatal("fall through"); + fail("falll through"); } } @@ -719,7 +762,7 @@ double eval_numexpr(struct expression *expr) break; default: free_expr(result); - fatal("attempted to numerically evaluate non-numeric expression"); + fail("attempted to evaluate a non-numeric expression"); } free_expr(result); return x; @@ -867,6 +910,17 @@ struct expression *simp(struct expression *expr) simp_progress = true; return simp(expr->subexpr.left); } +#if 0 + if(is_constant(expr->subexpr.right) && eval_numexpr(expr->subexpr.right) < 0) + { + simp_progress = true; + return new_op(OP_DIV, + new_const(1), + new_op(OP_POW, + simp(expr->subexpr.left), + new_const(-eval_numexpr(expr->subexpr.right)))); + } +#endif if(expr->subexpr.left->type == T_SUBEXPR && expr->subexpr.left->subexpr.op == OP_POW && is_constant(expr->subexpr.left->subexpr.right) && is_constant(expr->subexpr.right)) { simp_progress = true; @@ -885,7 +939,19 @@ struct expression *simp(struct expression *expr) } break; case T_SPECFUNC: - return new_specfunc(expr->specfunc.fn, simp(expr->specfunc.operand)); + switch(expr->specfunc.fn) + { + case FN_NEGATE: + if(is_constant(expr->specfunc.operand)) + { + double val = eval_numexpr(expr->specfunc.operand); + if(val < 0) + return new_const(-val); + } + /* fall through */ + default: + return new_specfunc(expr->specfunc.fn, simp(expr->specfunc.operand)); + } default: fatal("fall through"); } @@ -965,7 +1031,7 @@ struct expression *diff(struct expression *expr, const char *var) /*return diff_and_free(diff(expr->subexpr.left, expr->subexpr.right->varname), var);*/ case OP_ASSIGN: /* todo: how to make this logical? */ - fatal("cannot differentiate assignment"); + fail("cannot differentiate assignment operator (did you mean == ?)"); case OP_EQUALS: return simp_and_free(new_op(OP_EQUALS, diff(expr->subexpr.left, var), diff(expr->subexpr.right, var))); /* todo: how to make this logical? */ @@ -1228,6 +1294,10 @@ void free_stacks(struct expression **opstack, int osp, struct expression **numst bool is_num(const char *str) { bool had_digit = false; + + if(*str == '-') + ++str; + while(*str) { if(!(isdigit(*str) || *str == '.')) @@ -1331,7 +1401,7 @@ struct expression *parse_expr(const char *infix) struct expression *op = new_op(s2op(tok), NULL, NULL); while(osp > 0 && (opstack[osp-1]->type == T_SPECFUNC || (expr_prec(*opstack[osp-1]) > expr_prec(*op)) || - (expr_prec(*opstack[osp-1]) == expr_prec(*op) && op_assoc[opstack[osp-1]->subexpr.op] == LEFT))) + (expr_prec(*opstack[osp-1]) == expr_prec(*op) && op_assoc[(int)opstack[osp-1]->subexpr.op] == LEFT))) { if(!execop(numstack, &nsp, opstack, &osp)) { @@ -1421,74 +1491,94 @@ int main(int argc, char *argv[]) { interactive = isatty(STDIN_FILENO); + if(interactive) + { + printf("Welcome to yaCAS\n\nCopyright (C) 2018 Franklin Wei\n\nType \"help\" for a quick overview of the supported features.\n\n"); + } + + atexit(freevars); + setvar("pi", new_const(M_PI), true); setvar("e", new_const(M_E), true); setvar("memdiag", new_const(0), false); while(1) { - peakmem = 0; + if(setjmp(restore) == 0) + { + peakmem = 0; - char *line = NULL; + char *line = NULL; #ifndef USE_READLINE - if(interactive) - printf("yaCAS> "); - fflush(stdout); + if(interactive) + printf("yaCAS> "); + fflush(stdout); - size_t n = 0; - ssize_t len = getline(&line, &n, stdin); + size_t n = 0; + ssize_t len = getline(&line, &n, stdin); - /* remove newline */ - if(line[len - 1] == '\n') - line[len-- - 1] = '\0'; + /* remove newline */ + if(line[len - 1] == '\n') + line[len-- - 1] = '\0'; - if(len < 0) - return 0; + if(len < 0) + return 0; #else - line = readline(interactive ? "yaCAS> " : ""); - if(line && *line) - add_history(line); + line = readline(interactive ? "yaCAS> " : ""); + if(line && *line) + add_history(line); #endif - if(!line) - return 0; + if(!line) + return 0; - if(!strcmp(line, "dump")) - { - dumpvars(); - free(line); - continue; - } + if(!strcmp(line, "dump")) + { + dumpvars(); + free(line); + continue; + } + else if(!strcmp(line, "help")) + { + printf("yaCAS requires a space between all tokens, so instead of \"x^2\", type \"x ^ 2\"\n"); - clock_t start = clock(); + printf("Calculation: try \"atan ( 1 ) / pi\"\n"); + printf("Differentiation: try \"x ^ 2 diff x\", \"log ( y ) * x diff x\"\n"); + printf("Special functions: try \"exp ( pi ) - pi\"\n"); + free(line); + continue; + } - struct expression *top = parse_expr(line); - free(line); + clock_t start = clock(); - if(top) - { - top = eval_and_free(top); - top = simp_and_free_max(top); - print_expr(top); + struct expression *top = parse_expr(line); + free(line); - printf("(%s)\n", type_names[top->type]); + if(top) + { + top = eval_and_free(top); + top = simp_and_free_max(top); + print_expr(top); - setvar(".", dup_expr(top), true); + printf("(%s)\n", type_names[(int)top->type]); - free_expr(top); - } - else - { - printf("malformed expression\n"); - } + setvar(".", dup_expr(top), true); - clock_t diff = clock() - start; - if(diff > CLOCKS_PER_SEC / 10) - printf("time: %d.%.03d sec\n", diff / CLOCKS_PER_SEC, diff % CLOCKS_PER_SEC / (CLOCKS_PER_SEC / 1000)); + free_expr(top); + } + else + { + printf("malformed expression\n"); + } - if(eval_numexpr_and_free(new_var("memdiag")) == 2) - { - printf("memory: %d peak: %d\n", memusage, peakmem); + clock_t diff = clock() - start; + if(diff > CLOCKS_PER_SEC / 10) + printf("time: %ld.%.03ld sec\n", diff / CLOCKS_PER_SEC, diff % CLOCKS_PER_SEC / (CLOCKS_PER_SEC / 1000)); + + if(eval_numexpr_and_free(new_var("memdiag")) == 2) + { + printf("memory: %d peak: %d\n", memusage, peakmem); + } } } } |