8 enum CASCharFlag {letter, end, white, number, op};
10 class CASCharFlags : CharFlags {
12 flags[0] = (byte)CASCharFlag::end;
13 Set("\t\n\v\f\r ", CASCharFlag::white);
14 Set("0123456789.", CASCharFlag::number);
15 Set("!%&()*+,-/:;<=>?@[\\]^{|}~", CASCharFlag::op);
19 CASCharFlags cascharflags {};
21 enum ExpressionType {null=0, i, f, c, v, openfunction, function, openparenthesis, parenthesis, prefix, postfix, binary};
23 //f means floating point
27 enum ExpressionClass {nullary=0, prefix, postfix, binary, vallary, nary};
28 //I define a vallary operator as one that binds a single expression on both sides (e.g. (), [], {}, "")
29 //An nary operator is one that binds an array of expressions on both sides (e.g. sum(3,4,5))
31 ExpressionClass expression_class[ExpressionType] = {
32 nullary, nullary, nullary, nullary, nullary, prefix, nary, prefix, vallary, prefix, postfix, binary
34 //function and parenthesis are weird classes. They are considered nullary, but they do contain data:
35 // function's arguments are in the Array<Expression> arg
36 // parenthesis's argument is in b, just like the prefix it was before
38 enum PrefixOperator {root=0, positive, negative};
39 int prefix_op_order[PrefixOperator] = {
42 const char *prefix_op_string[PrefixOperator] = {
46 enum PostfixOperator {factorial=0};
47 int postfix_op_order[PostfixOperator] = {
50 const char *postfix_op_string[PostfixOperator] = {
54 //A lot of CAS operations may weed out operators like subtract, but it is included to make it possible to maniuplate the expression for readability, etc.
55 enum BinaryOperator {add, subtract, multiply, divide, power, equals, comma};
56 int binary_op_order[BinaryOperator] = {
59 const char *binary_op_string[BinaryOperator] = {
60 "+", "-", "*", "/", "^", "=", ","
63 enum ParenthesisType {parenthesis, brackets, braces};
64 const char *parenthesis_type_string[ParenthesisType] = {
68 enum CASFunction {sum, product, polynomial, exp, ln, sin, cos, tan, arcsin, arccos, arctan};
70 const char *function_string[CASFunction] = {
71 "sum", "product", "polynomial", "exp", "ln", "sin", "cos", "tan", "arcsin", "arccos", "arctan"
74 class CASFunctionDefinition {
75 virtual bool Expression::Sanitize(void) {
78 virtual void Expression::Simplify(void);
81 bool Expression::SanitizeSingleArgument(void) {
87 class FunctionList : Dictionary {
88 Array<CASFunctionDefinition> f {};
91 for (i=0; i<CASFunction::enumSize; i++)
92 array.Add(StrDup(function_string[i]));
93 f.size = CASFunction::enumSize;
94 f[CASFunction::sum] = {
97 f[CASFunction::product] = {
100 f[CASFunction::polynomial] = {
103 f[CASFunction::exp] = {
104 bool Expression::Sanitize()
113 a = {type=c, mom=this, c=e};
118 f[CASFunction::ln] = {
119 Sanitize = SanitizeSingleArgument;
122 f[CASFunction::sin] = {
123 Sanitize = SanitizeSingleArgument;
126 f[CASFunction::cos] = {
127 Sanitize = SanitizeSingleArgument;
130 f[CASFunction::tan] = {
131 Sanitize = SanitizeSingleArgument;
134 f[CASFunction::arcsin] = {
135 Sanitize = SanitizeSingleArgument;
138 f[CASFunction::arccos] = {
139 Sanitize = SanitizeSingleArgument;
142 f[CASFunction::arctan] = {
143 Sanitize = SanitizeSingleArgument;
147 //these functions check to make sure the input is a function of correct range before executing
148 bool Sanitize(Expression expr) {
150 if (expr.type==function && expr.function.func<f.size)
151 ret = f[expr.function.func].Sanitize(expr);
154 void Simplify(Expression expr) {
155 if (expr.type==function && expr.function.func<f.size)
156 f[expr.function.func].Simplify(expr);
160 class CASDictionary : struct {
165 enum CASConstant {i, e, pi};
167 const char *constant_string[CASConstant] = {
171 class ExprChildWalker {
174 Expression user0, user1, user2;
177 switch (expr.ExprClass()) {
190 for (iter:expr.arg) {
197 //return false to end iteration
198 virtual bool On(Expression *e);
201 class Expression : struct {
204 Expression mom; //easier to type than "parent" :)
207 bool constant:1, approx:1;
208 void ClearTags(void) {
222 ParenthesisType type;
223 } parenthesis; //also used for openparenthesis
236 Expression a; //binary left child or postfix child
237 Expression b; //binary right child, prefix child, or vallary child
239 Array<Expression> arg; //only for nary operators (e.g. functions)
242 void DebugPrint(File out, CASDictionary dict, uint spacelevel, Expression correctMom) {
248 out.Printf("null pointer\n");
251 if (mom != correctMom)
252 out.Printf("Incorrectly parented to %p instead of %p ", mom, correctMom);
253 memset(tagstr, '-', 2);
259 out.Printf("%s ", tagstr);
262 out.Printf("nothing\n");
266 out.Printf("%lld\n", i);
270 out.Printf("%.15Lg\n", (long double)f);
273 out.Printf("%s (constant)\n", c<CASConstant::enumSize ? constant_string[c] : "Invalid constant");
276 out.Printf("%s\n", dict.v.name(v));
279 out.Printf("%s(\n", dict.f.name(function.func));
280 b.DebugPrint(out, dict, spacelevel+2, this);
283 out.Printf("%s()\n", dict.f.name(function.func));
285 iter.DebugPrint(out, dict,spacelevel+2, this);
287 case openparenthesis:
288 out.Printf("%c\n", OpString()[0]);
289 b.DebugPrint(out, dict, spacelevel+2, this);
292 out.Printf("%s\n", OpString());
293 b.DebugPrint(out, dict, spacelevel+2, this);
296 out.Printf("%s\n", OpString());
297 b.DebugPrint(out, dict, spacelevel+2, this);
300 out.Printf("%s\n", OpString());
301 a.DebugPrint(out, dict, spacelevel+2, this);
304 out.Printf("%s\n", OpString());
305 a.DebugPrint(out, dict, spacelevel+2, this);
306 b.DebugPrint(out, dict, spacelevel+2, this);
312 ExprChildWalker w {this;
313 bool On(Expression *e) {
322 if (ExprClass()==nary)
326 void Clear(void) { //keeps parent the same
332 //sort of like delete this, but does not affect anything else but this block
333 //Note: Make sure that arg array is taken care of if this is an nary
347 //Copy() makes a copy of this Expression that makes no references to this expression
348 Expression Copy(Expression newMom) {
349 Expression ret {mom=newMom};
352 memcpy(ret, this, sizeof(class Expression));
354 switch (ExprClass()) {
371 ret.arg[i] = arg[i].Copy(ret);
378 //collect terms in this expression separated by the binary operator op
379 //e.g. separate x^2+3x+1 by plus into [x^2, 3x, 1]
380 //Will separate (x^2+3x+1)/2 by plus into [(x^2+3x+1)/2]
381 //If newMom is not null, all binary operator nodes involved in the array will be freed, and each node will be parented to newMom
382 void CollectTerms(BinaryOperator op, Array<Expression> out, Expression newMom) {
385 if (type==binary && binary.op==op) {
386 a.CollectTerms(op, out, newMom);
387 b.CollectTerms(op, out, newMom);
399 //only reparents the top level
400 void ReparentChildren(void) {
401 ExprChildWalker w {this;
402 bool On(Expression *e)
411 //relinks the parent to this child
412 //Old is the former pointer to the child
413 void Rechild(Expression old) {
414 SwapChild(mom, old, this);
416 //disconnects this from parent without deleting it, and sets in its place newChild
417 void Elope(Expression newChild) {
418 SwapChild(mom, this, newChild);
422 //disconnects this expression from its parent and relinks its children to the parent
423 //Currently, this only works with unary (prefix/postfix/vallary) operators
426 fprintf(stderr, "BUG: Attempted to unlink null node; ignoring.\n");
430 fprintf(stderr, "BUG: Attempted to unlink root node; ignoring.\n");
433 switch (ExprClass()) {
444 fprintf(stderr, "BUG: Attempted to call Unlink on non-unary node.\n");
448 //Note that FromString likes to toy with the string, but anything done to the string will be undone.
449 bool FromString(char *str, CASDictionary dictionary) {
450 ExpressionFromString(this, str, dictionary);
451 return Sanitize(this, dictionary);
453 char *ToString(void) {
456 const char *OpString(void) {
457 if (type==prefix && prefix.op<PrefixOperator::enumSize)
458 return prefix_op_string[prefix.op];
459 else if (type==postfix && postfix.op<PostfixOperator::enumSize)
460 return postfix_op_string[postfix.op];
461 else if (type==binary && binary.op<BinaryOperator::enumSize)
462 return binary_op_string[binary.op];
463 else if ((type==openparenthesis || type==parenthesis) && parenthesis.type<ParenthesisType::enumSize)
464 return parenthesis_type_string[parenthesis.type];
469 if (type==binary && binary.op<BinaryOperator::enumSize)
470 return binary_op_order[binary.op];
471 else if (type==prefix && prefix.op<PrefixOperator::enumSize)
472 return prefix_op_order[prefix.op];
473 else if (type==postfix && postfix.op<PostfixOperator::enumSize)
474 return postfix_op_order[postfix.op];
475 else if (type==openfunction) {
476 if (b && b.type==parenthesis && b.parenthesis.type==parenthesis)
477 return 1000; //arbitrary number larger than any other order
479 return binary_op_order[power];
481 else if (type==openparenthesis)
482 return 0; //yes, this should be the lowest, not highest, operator
486 ExpressionClass ExprClass(void) {
487 if (type<ExpressionType::enumSize)
488 return expression_class[type];
492 void Simplify(CASDictionary dict) {
497 void SwapChild(Expression mom, Expression oldChild, Expression newChild) {
501 w = {mom, user0=oldChild, user1=newChild;
502 bool On(Expression *e) {
514 class ExpressionTree : struct {
517 Expression root {type=prefix, prefix.op=root};
518 Expression p; //pointer to most recently added token
519 File err {output=stderr};
525 if (!closeerrfiles) {
531 // e must be the string terminator, and the string must be zero-terminated as well
532 void PushInteger(const char *s, const char *e) {
534 Expression var {type=i};
535 unsigned long long n = ReadULL_Valid(s, e, NB_Dec, &rerror);
536 var.i = (long long)n;
537 if (rerror==overflow || n>=(((unsigned long long)-1)>>1)+1)
538 err.Printf("Number %s too big; using %lld\n", s, var.i);
541 void PushFloat(const char *s) {
542 Expression var {type=f};
543 long double n = (long double)atof(s); //TODO: Use something that won't truncate the result
547 void PushConstant(CASConstant con) {
548 Expression var {type=c, c=con};
551 void PushVariable(const char *s) {
552 Expression var {type=v, v=dict.v.lookup(s, true)};
555 void PushFunction(CASFunction f) {
556 Expression var {type=openfunction, function.func=f};
559 void PushOperator(const char *s) {
560 Expression op {type=binary};
564 if (s[1]==0) { //single-character operator
569 if (pc==prefix || pc==binary) {
571 op.prefix.op = *s=='+' ? positive : negative;
573 op.binary.op = *s=='+' ? add : subtract;
577 op.binary.op = multiply;
580 op.binary.op = divide;
583 op.binary.op = power;
586 op.binary.op = equals;
589 op.binary.op = comma;
591 case '(': //we will push a "prefix" openparenthesis like any other prefix op
592 op.type = openparenthesis;
593 op.parenthesis.type = parenthesis;
596 op.type = openparenthesis;
597 op.parenthesis.type = brackets;
600 op.type = openparenthesis;
601 op.parenthesis.type = braces;
603 case ')': //we will close the soonest openparenthesis and continue from there
606 ParenthesisType type = (*s==')' ? parenthesis : *s==']' ? brackets : braces);
610 if (w.type==openfunction)
612 else if (w.type==openparenthesis) {
613 if (w.parenthesis.type==type) {
615 p.type = parenthesis;
618 err.Printf("End %c appeared after start %c; disregarding.\n", *s, w.OpString()[1]);
619 } else if (!w.mom || (w.type==prefix && w.prefix.op==root)) {
620 err.Printf("End parenthesis missing beginning parenthesis; ignoring.\n");
629 op.postfix.op = factorial;
637 err.Printf("Operator '%s' unrecognized; ignoring.\n", s);
644 void push(Expression expr, bool implicit) {
645 ExpressionClass c = expr.ExprClass();
646 ExpressionClass pc = p.ExprClass();
647 if (c==nullary || c==prefix) {
648 if (pc==nullary || pc==postfix || pc==vallary || pc==nary)
649 push({type=binary, binary.op=multiply}, true); //implicit multiply (e.g. 7a -> 7*a)
656 if (pc==prefix || pc==binary) {
657 err.Printf("%s operator %s following operator %s; ignoring.\n", c==postfix ? "Postfix" : "Binary", expr.OpString(), p.OpString());
661 order = expr.OpOrder();
663 err.Printf("BUG: %s operator has invalid order; ignoring.\n", c==postfix ? "Postfix" : "Binary");
667 //An AB operator is defined as a postfix or binary operator
668 //For any binary operators that associate right-to-left, increment order so they can overtake sooner
669 //Believe it or not, equals associates right-to-left (at least in C it does)
670 if (expr.type==binary && (expr.binary.op==power || expr.binary.op==equals))
672 //We will walk up the tree with this AB operator until we find a w such that w.mom.order < op.order
673 //If we reach the root node, that's a bug because the root node should have an order of 0
678 err.Printf("BUG: Expression tree ended too soon while handling binary operator; ignoring.\n");
682 worder = w.mom.OpOrder();
684 err.Printf("BUG: Bad parent encountered while handling postfix/binary operator.\n");
688 if (implicit && expr.type==binary && expr.binary.op==multiply && w.mom.type==openfunction) {
689 //this makes it so sin 3x will be sin(3x), while sin 3*x will be (sin 3)*x
690 if (worder == binary_op_order[power])
692 } else if (order > worder)
695 if (w.type==openfunction)
698 //We will take w, make it op's left child, and put op where w was.
706 //closes f (should only be called if f is an openfunction)
707 void closeFunction(Expression f) {
708 Expression child = f.b;
709 if (child && child.type==parenthesis && child.parenthesis.type==parenthesis) {
710 //remove the first set of parenthesis
711 //if more sets of parenthesis surround this, the user really meant to have them there
712 Expression tmp = child.b;
717 //Turn this from an openfunction into a function
722 //note that f.function.func persists because Free() did not clear that
723 //Collect all comma-delimited arguments into the arg array
724 child.CollectTerms(comma, f.arg, f);
726 void finalize(void) {
729 if (w.type==openfunction)
736 void ExpressionFromString(Expression expr, char *str, CASDictionary dictionary) {
737 ExpressionTree tree {dictionary};
738 char *s, *p, *e; //token start and end
740 CASCharFlags cf = cascharflags;
748 for (;;) //for loop is used so multiple character types that merge into one token can be implemented in the future
749 { //grab the next token
750 byte f = cf.flags[(byte)*e++];
751 while (cf.flags[(byte)*e]==f) e++;
752 flag = (CASCharFlag)f;
764 f = dictionary.f.lookup(s, false);
766 tree.PushFunction((CASFunction)f);
770 UTF8GetChar(p, &charlen);
774 for (i=0; i<CASConstant::enumSize; i++) {
775 if (!strcmp(s, constant_string[(CASConstant)i])) {
781 tree.PushConstant((CASConstant)con);
783 tree.PushVariable(s);
796 tree.PushInteger(s, e);
800 //This only lexes one-character operators
804 tree.PushOperator(p);
813 memcpy(expr, tree.root, sizeof(class Expression));
820 //This function walks the tree to make sure it is valid:
821 // no null expressions
822 // no open parenthesis/functions
823 // correct number of arguments for core functions (TODO)
824 //While it's at it, it tags every expression in the tree
825 //It also converts exp(foo) to e^foo (TODO)
826 //returns true if the expression is valid and not empty
827 bool Expression::Sanitize(CASDictionary dict) {
828 if (!this || this.type==null)
831 #define S(x) {if (!Sanitize(x, dict)) return false;}
832 switch (ExprClass()) {
834 if (type==openparenthesis || type==openfunction)
844 if (type==parenthesis && parenthesis.type==parenthesis)
863 if (a.constant && b.constant)
865 if (a.approx || b.approx)
878 if (!dict.f.Sanitize(this))