summaryrefslogtreecommitdiff
path: root/yacas.c
diff options
context:
space:
mode:
Diffstat (limited to 'yacas.c')
-rw-r--r--yacas.c224
1 files changed, 157 insertions, 67 deletions
diff --git a/yacas.c b/yacas.c
index 87eb16e..dc44f22 100644
--- a/yacas.c
+++ b/yacas.c
@@ -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);
+ }
}
}
}