1 module calcool.expression; 2 3 import std.math; 4 import std.algorithm; 5 6 import calcool.exceptions : ParseException; 7 8 public: 9 10 interface Expression { 11 real evaluate() const; 12 string toString() @safe const; 13 } 14 15 class FuncExpression : Expression { 16 private: 17 Expression param; 18 string name; 19 20 static immutable { 21 real function(real) pure @safe nothrow @nogc[string] funcs; 22 auto trigonometry = ["sin", "cos", "tan",]; 23 auto other = ["sqrt", "floor", "ceil", "log", "log2", "log10", "exp"]; 24 } 25 26 shared static this() { 27 static foreach (i; trigonometry ~ other) { 28 funcs[i] = mixin("&" ~ i); 29 } 30 funcs["abs"] = &abs!real; 31 } 32 33 public: 34 35 this(string n, Expression p) { 36 param = p; 37 name = n; 38 if (cast(EolExpression) param) { 39 throw new ParseException("Operand needed"); 40 } 41 } 42 43 override real evaluate() const { 44 import std.algorithm : canFind; 45 46 if (name in funcs) { 47 if (trigonometry.canFind(name)) { 48 auto ret = funcs[name](param.evaluate() * PI / 180); 49 if (ret > -1.0e-17 && ret < 1.0e-17) 50 ret = 0; 51 return ret; 52 } 53 return funcs[name](param.evaluate()); 54 } 55 throw new ParseException("Unknown function call"); 56 } 57 58 override string toString() @safe const { 59 return name ~ "(" ~ param.toString() ~ ")"; 60 } 61 } 62 63 class OperatorExpression(string op) : Expression 64 if (["+", "-", "/", "*", "^^"].canFind(op)) { 65 66 private Expression left; 67 private Expression right; 68 69 this(Expression l, Expression r) { 70 left = l; 71 right = r; 72 if (cast(EolExpression) right) { 73 throw new ParseException("Operand needed"); 74 } 75 } 76 77 override real evaluate() const { 78 const rhs = right.evaluate(); 79 static if (op == "/") { 80 if (rhs == 0) { 81 throw new ParseException("Devide by zero"); 82 } 83 } 84 return mixin("left.evaluate()" ~ op ~ "rhs"); 85 } 86 87 override string toString() @safe const { 88 return left.toString() ~ op ~ right.toString(); 89 } 90 } 91 92 class GroupExpression : Expression { 93 private Expression inside; 94 95 this(Expression i) { 96 inside = i; 97 } 98 99 override real evaluate() const { 100 return inside.evaluate(); 101 } 102 103 override string toString() @safe const { 104 return "(" ~ inside.toString() ~ ")"; 105 } 106 } 107 108 class NumberExpression : Expression { 109 private real num; 110 111 this(real n) { 112 num = n; 113 } 114 115 override real evaluate() const { 116 return num; 117 } 118 119 override string toString() @safe const { 120 import std.conv : to; 121 122 return num.to!string; 123 } 124 } 125 126 class NegateExpression : Expression { 127 private Expression right; 128 129 this(Expression r) { 130 right = r; 131 if (cast(EolExpression) right) { 132 throw new ParseException("Operand needed"); 133 } 134 } 135 136 override real evaluate() const { 137 return -right.evaluate(); 138 } 139 140 override string toString() @safe const { 141 return "-" ~ right.toString(); 142 } 143 } 144 145 class EolExpression : Expression { 146 import calcool.exceptions : EolException; 147 148 override real evaluate() const { 149 throw new EolException(); 150 } 151 152 override string toString() @safe const { 153 return ""; 154 } 155 }