1 module appbase.utils.expression;
2 
3 import std.conv;
4 import std.variant;
5 import std.uni;
6 import std.container.dlist;
7 
8 import appbase.utils.utility;
9 import appbase.utils.container.stack;
10 
11 class Expression
12 {
13     double calc(string express)
14     {
15         Stack!string stackOperators;
16         Stack!double stackNumerials;
17 
18         Token[] tokens = scanner(express);
19 
20         string operator = string.init, topOperator  = string.init;
21         double numerial = 0.0, top1Numerial = 0.0, top2Numerial = 0.0;
22 
23         for (size_t i = 0; i < tokens.length; i++)
24         {
25             Token token = tokens[i];
26 
27             if (token.type == 2)
28             {
29                 stackNumerials.push(token.value.get!double);
30 
31                 continue;
32             }
33 
34             operator = token.value.get!string;
35 
36             if (operator == "[")
37             {
38                 stackOperators.push(operator);
39 
40                 continue;
41             }
42 
43             if (isCalcOperator(operator))
44             {
45                 if (i >= tokens.length - 1)
46                 {
47                     assert(0, "Syntax error in expression: " ~ express);
48                 }
49 
50                 Token nextToken = tokens[i + 1];
51 
52                 if ((nextToken.type != 2) && (nextToken.value.get!string != "["))
53                 {
54                     assert(0, "Syntax error in expression: " ~ express);
55                 }
56 
57                 if ((stackOperators.length == 0) || (!isCalcOperator(stackOperators.back())))
58                 {
59                     stackOperators.push(operator);
60 
61                     continue;
62                 }
63 
64                 topOperator = stackOperators.back();
65 
66                 if (getPriority(operator) > getPriority(topOperator))
67                 {
68                     stackOperators.push(operator);
69 
70                     continue;
71                 }
72 
73                 topOperator  = stackOperators.pop();
74                 top1Numerial = stackNumerials.pop();
75                 top2Numerial = stackNumerials.pop();
76                 numerial = calc(top2Numerial, top1Numerial, topOperator);
77                 stackNumerials.push(numerial);
78 
79                 stackOperators.push(operator);
80 
81                 continue;
82             }
83 
84             if (operator == "]")
85             {
86                 if ((stackOperators.length == 0) || (!isCalcOperator(stackOperators.back())))
87                 {
88                     assert(0, "Syntax error in expression: " ~ express);
89                 }
90 
91                 while (stackOperators.length > 0)
92                 {
93                     topOperator  = stackOperators.pop();
94                     top1Numerial = stackNumerials.pop();
95                     top2Numerial = stackNumerials.pop();
96                     numerial = calc(top2Numerial, top1Numerial, topOperator);
97                     stackNumerials.push(numerial);
98 
99                     if (stackOperators.length == 0)
100                     {
101                         assert(0, "Syntax error in expression: " ~ express);
102                     }
103 
104                     topOperator = stackOperators.back();
105 
106                     if (topOperator == "[")
107                     {
108                         stackOperators.pop();
109 
110                         break;
111                     }
112                 }
113             }
114         }
115 
116         while (stackOperators.length > 0)
117         {
118             topOperator = stackOperators.pop();
119 
120             if (!isCalcOperator(topOperator))
121             {
122                 assert(0, "Syntax error in expression: " ~ express);
123             }
124 
125             top1Numerial = stackNumerials.pop();
126             top2Numerial = stackNumerials.pop();
127             numerial = calc(top2Numerial, top1Numerial, topOperator);
128             stackNumerials.push(numerial);
129         }
130 
131         numerial = stackNumerials.pop();
132         return numerial;
133     }
134 
135 private:
136 
137     class Token
138     {
139         int     type;  // 1: operator, 2: numerical
140         Variant value;
141 
142         this(int type, string value)
143         {
144             this.type  = type;
145             this.value = Variant(value);
146         }
147 
148         this(int type, double value)
149         {
150             this.type  = type;
151             this.value = Variant(value);
152         }
153     }
154 
155     Token[] scanner(string express)
156     {
157         Token[] result;
158         string  token = "";
159 
160         for (size_t i = 0; i < express.length; i++)
161         {
162             char ch = express[i];
163 
164             if (isWhite(ch))
165             {
166                 continue;
167             }
168 
169             if (isOperator(ch))
170             {
171                 if (token != string.init)
172                 {
173                     result ~= new Token(2, token.to!double);
174                     token   = "";
175                 }
176 
177                 result ~= new Token(1, ch.to!string());
178             }
179             else
180             {
181                 token ~= ch;
182             }
183         }
184 
185         if (token != string.init)
186         {
187             result ~= new Token(2, token.to!double);
188         }
189 
190         return result;
191     }
192 
193     bool isOperator(char ch)
194     {
195         return inArray!char("[]+-*/^", ch);
196     }
197 
198     bool isCalcOperator(string ch)
199     {
200         return ((ch == "+") || (ch == "-") || (ch == "*") || (ch == "/") || (ch == "^"));
201     }
202 
203     int getPriority(string operator)
204     {
205         if ((operator == "[") || (operator == "]"))
206         {
207             return 4;
208         }
209 
210         if (operator == "^")
211         {
212             return 3;
213         }
214 
215         if ((operator == "*") || (operator == "/"))
216         {
217             return 2;
218         }
219 
220         if ((operator == "+") || (operator == "-"))
221         {
222             return 1;
223         }
224 
225         return -1;
226     }
227 
228     double calc(double numerial1, double numerial2, string operator)
229     {
230         if (operator == "+")
231         {
232             return numerial1 + numerial2;
233         }
234         else if (operator == "-")
235         {
236             return numerial1 - numerial2;
237         }
238         else if (operator == "*")
239         {
240             return numerial1 * numerial2;
241         }
242         else if (operator == "/")
243         {
244             return numerial1 / numerial2;
245         }
246         else if (operator == "^")
247         {
248             return numerial1^^numerial2;
249         }
250 
251         assert(0, "Syntax error in expression, Unknown operator: " ~ operator);
252     }
253 }