From 91edb3a29c403c689c4fcc3c5d3890ce9e2ee056 Mon Sep 17 00:00:00 2001 From: Franklin Wei Date: Sat, 19 May 2018 22:25:33 -0400 Subject: make more robust --- yacas.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/yacas.c b/yacas.c index ce0539b..4ff9dcb 100644 --- a/yacas.c +++ b/yacas.c @@ -1,3 +1,68 @@ +/* + * Franklin Wei + * 19 May 2018 + * Northwest Guilford High School + * yaCAS - "Yet Another Computer Algebra System" + * + * This is a program to manipulate algebraic expressions. It features + * a way of representing arbitrary expressions in memory, a parser to + * convert to and from standard mathematical notation, and functions + * to simplify, differentiate, and integrate such + * expressions. Additionally, it can function as a simple numerical + * calculator. + * + * Expressions are represented by an abstract syntax tree, a node of + * which is represented by a "struct expression" object (see + * definition below). Expressions are passed by reference to most + * functions, but most functions will allocate an entirely new + * expression tree to return their results. This results in a not + * insignificant amount of overhead as memory allocations and frees + * take place, but also greatly simplifies the memory model. + * + * Variables are supported through a simple hash map. Strings are + * mapped to expression trees through the "struct variable" object, + * and the expression trees are then substituted into the computation + * as needed. There is a special variable called . that stores the + * result of the previous computation. + * + * Differentiation is performed recursively on the expression + * tree. The differentiation of most expressions is performed by + * recursing down the tree, applying the basic rules of + * differentiation to combine the derivatives of child nodes. See the + * "diff" function for implementation. + * + * The expression parser is rather limited when it comes to + * tokenization. In its current form, it relies on tokens in the + * expression, say, "x ^ 2", being separated with spaces. That is, + * typing an expression like "x^2", without spaces between tokens, + * will result in undesired behavior (in this case, the parser wil + * view the token "x^2" as a variable name, not an expression). Just + * be sure to always separate tokens with a space, and all will be + * well. + * + * Finally, when using the exponential function, the form "exp ( x )" + * is preferred over "e ^ x", since the parser will recognize exp() as + * a special function and know about its special properties, while e^x + * will be treated as some constant raised to a power, without the + * special features of the exp() being recognized. + * + * Please note that this is a work in progress. The simplification, + * equality checking, and integration algorithms used are rudimentary + * at best, although differentiation should be relatively robust. + * + * Usage instructions: compile with no optimization, as this may cause + * undefined behavior, and link with -lm and -lreadline: + * + * cc -O0 yacas.c -o yacas -lm -lreadline + * + * Then run with no arguments and you should be presented with a + * "yaCAS> " prompt. From here, you can type "help" at the command + * line for an overview of the basic syntax. + * + * Readline can be disabled by commenting out the #define below (in + * which case the getline() function will be used instead): + */ + #define USE_READLINE #include @@ -5,8 +70,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -18,6 +83,8 @@ #endif /* tool for manipulating algebraic expressions */ + +/* packed to save memory with large expressions */ struct expression { char type; /* T_* */ union { @@ -747,6 +814,7 @@ struct expression *eval_expr(struct expression *expr) case T_CONST: return new_const(expr->constant); case T_VAR: + /* this could fail upon a circular reference */ if(is_var(expr->varname)) return eval_expr(getvar(expr->varname)); @@ -1648,6 +1716,7 @@ int main(int argc, char *argv[]) setvar("pi", new_const(M_PI), true); setvar("e", new_const(M_E), true); setvar("memdiag", new_const(0), false); + setvar(".", new_const(0), false); while(1) { -- cgit v1.1