1 package org.djutils.eval;
2
3 import java.lang.reflect.Field;
4 import java.util.ArrayList;
5 import java.util.Collection;
6 import java.util.HashMap;
7 import java.util.List;
8 import java.util.Map;
9
10 import org.djunits.unit.DimensionlessUnit;
11 import org.djunits.unit.TimeUnit;
12 import org.djunits.unit.Unit;
13 import org.djunits.unit.si.SIDimensions;
14 import org.djunits.value.vdouble.scalar.Dimensionless;
15 import org.djunits.value.vdouble.scalar.SIScalar;
16 import org.djunits.value.vdouble.scalar.Time;
17 import org.djunits.value.vdouble.scalar.base.Constants;
18 import org.djunits.value.vdouble.scalar.base.DoubleScalar;
19 import org.djunits.value.vdouble.scalar.base.DoubleScalarAbs;
20 import org.djunits.value.vdouble.scalar.base.DoubleScalarRel;
21 import org.djutils.exceptions.Throw;
22 import org.djutils.metadata.MetaData;
23 import org.djutils.metadata.ObjectDescriptor;
24
25
26
27
28
29
30
31
32
33
34
35
36 public class Eval
37 {
38
39 private List<Object> stack = new ArrayList<>();
40
41
42 private String expression;
43
44
45 private int position = 0;
46
47
48 private RetrieveValue retrieveValue = null;
49
50
51 private static final int BIND_CONDITIONAL_EXPRESSION = 1;
52
53
54 private static final int BIND_OR = 2;
55
56
57 private static final int BIND_AND = 3;
58
59
60 private static final int BIND_EQUAL = 4;
61
62
63 private static final int BIND_RELATIONAL = 5;
64
65
66 private static final int BIND_ADD = 6;
67
68
69 private static final int BIND_MUL = 7;
70
71
72 private static final int BIND_POW = 8;
73
74
75 private static final int BIND_UMINUS = 9;
76
77
78 private static final ObjectDescriptor[] noArguments = new ObjectDescriptor[] {};
79
80
81
82 private final Function[] builtinFunctions = new Function[] {
83 new F0("AVOGADRO", Constants.AVOGADRO, new MetaData("Avogadro constant", "Avogadro constant in 1/mol", noArguments)),
84 new F0("BOLTZMANN", Constants.BOLTZMANN, new MetaData("Boltzmann constant",
85 "The exact value of the Boltzmann constant in Joule per Kelvin", noArguments)),
86 new F0("CESIUM133_FREQUENCY", Constants.CESIUM133_FREQUENCY, new MetaData("Cesium 133 frequency",
87 "The exact value of the Cesium 133 ground state hyperfine structure transition frequency", noArguments)),
88 new F0("CURRENTTIME", Time.ZERO.getClass(),
89 new MetaData("The current time in seconds since 1970 UTC",
90 "The current time in seconds since 1970 UTC to the nearest ms as reported by the operating system", noArguments),
91 (f) -> new Time(System.currentTimeMillis() / 1000d, TimeUnit.BASE_SECOND)),
92 new F0("E", Constants.E, new MetaData("Euler\'s constant e", "Euler\'s constant e; the base of the natural logarithm")),
93 new F0("ELECTRONCHARGE", Constants.ELECTRONCHARGE, new MetaData("Electrical charge of one electron",
94 "The exact electrical charge of one electron", noArguments)),
95 new F0("ELECTRONMASS", Constants.ELECTRONMASS, new MetaData("Mass of an electron",
96 "Mass of an electron, the value of this physical constant has an uncertainty of 2.8e-40 kg", noArguments)),
97 new F0("G", Constants.G, new MetaData("Gravitational constant",
98 "Gravitational constant, a.k.a. Newtonian constant of gravitation. This is the 2018 best known approximation, which has an "
99 + "uncertainty 1.5e-15 m^3/kgs^2", noArguments)),
100 new F0("LIGHTSPEED", Constants.LIGHTSPEED, new MetaData("Speed of light in vacuum", "The exact speed of light in vacuum", noArguments)),
101 new F0("LUMINOUS_EFFICACY_540THZ", Constants.LUMINOUS_EFFICACY_540THZ, new MetaData(
102 "The luminous efficacy Kcd of monochromatic radiation of frequency 540×10^12 Hz (540 THz)",
103 "The exact luminous efficacy Kcd of monochromatic radiation of frequency 540×10^12 Hz (540 THz)", noArguments)),
104 new F0("NEUTRONMASS", Constants.NEUTRONMASS, new MetaData("Mass of a neutron",
105 "Mass of a neutron. The value of this physical constant has an uncertainty of 9.5e-37 kg.", noArguments)),
106 new F0("PI", Constants.PI, new MetaData("Ratio of a half circumference of a circle and its radius",
107 "Ratio of a half circumference of a circle and its radius", noArguments)),
108 new F0("PHI", Constants.PHI, new MetaData("Golden ratio", "Golden ratio", noArguments)),
109 new F0("PLANCK", Constants.PLANCK, new MetaData("Planck constant; ratio of a photon's energy and its frequency",
110 "Planck constant; the exact ratio of a photon's energy and its frequency", noArguments)),
111 new F0("PLANCKREDUCED", Constants.PLANCKREDUCED, new MetaData("Reduced Planck constant",
112 "Reduced Planck constant, a.k.a. angular Planck constant; Planck constant divided by 2 pi" ,noArguments)),
113 new F0("PROTONCHARGE", Constants.PROTONCHARGE, new MetaData("ElectricalCharge of one proton", "ElectricalCharge of one proton", noArguments)),
114 new F0("PROTONMASS", Constants.PROTONMASS, new MetaData("Mass of a proton",
115 "Mass of a proton. The value of this physical constant has an uncertainty of 5.1e-37", noArguments)),
116 new F0("TAU", Constants.TAU, new MetaData("Ratio of circumference of circle and its radius",
117 "Ratio of circumference of circle and its radius", noArguments)),
118 new F0("VACUUMIMPEDANCE", Constants.VACUUMIMPEDANCE, new MetaData("Impedance of vacuum", "Impedance of vacuum", noArguments)),
119 new F0("VACUUMPERMEABILITY", Constants.VACUUMPERMEABILITY, new MetaData("Permeability of vacuum",
120 "Permeability of vacuum. The uncertainty of this value is 1.9e-16N/A^2", noArguments)),
121 new F0("VACUUMPERMITTIVITY", Constants.VACUUMPERMITTIVITY, new MetaData("Permittivity of vacuum.",
122 "Permittivity of vacuum. The uncertainty of this value is 1.3e-21 F/m.", noArguments)),
123 new F0("TRUE", Boolean.TRUE, new MetaData("The logical value TRUE", "The logical value TRUE", noArguments)),
124 new F0("FALSE", Boolean.FALSE, new MetaData("The logical value FALSE", "The logical value FALSE", noArguments)),
125 new F1("acos", Dimensionless.class, new MetaData("acos", "returns the angle of which the cosine equals the value of the argument",
126 new ObjectDescriptor("angle", "angle", Dimensionless.class)), (i, a) -> checkDimensionless(i, a).acos()),
127 new F1("asin", Dimensionless.class, new MetaData("asin", "returns the angle of which the sine equals the value of the argument",
128 new ObjectDescriptor("angle", "angle", Dimensionless.class)), (i, a) -> checkDimensionless(i, a).asin()),
129 new F1("atan", Dimensionless.class, new MetaData("atan", "returns the angle of which the tangent equals the value of the argument",
130 new ObjectDescriptor("angle", "angle", Dimensionless.class)), (i, a) -> checkDimensionless(i, a).atan()),
131 new F1("cbrt", Dimensionless.class, new MetaData("cbrt", "returns the cubic root of the value of the argument",
132 new ObjectDescriptor("value", "value", Dimensionless.class)), (i, a) -> checkDimensionless(i, a).cbrt()),
133 new F1("cos", Dimensionless.class, new MetaData("cos", "returns the cosine of the value of the argument",
134 new ObjectDescriptor("value", "value", Dimensionless.class)), (i, a) -> checkDimensionless(i, a).cos()),
135 new F1("cosh", Dimensionless.class, new MetaData("cosh", "returns the hyperbolic cosine of the value of the argument",
136 new ObjectDescriptor("value", "value", Dimensionless.class)), (i, a) -> checkDimensionless(i, a).cosh()),
137 new F1("exp", Dimensionless.class, new MetaData("exp", "returns e to the power of the argument",
138 new ObjectDescriptor("value", "value", Dimensionless.class)), (i, a) -> checkDimensionless(i, a).exp()),
139 new F1("expm1", Dimensionless.class, new MetaData("expm1", "returns e to the power of the argument minus 1",
140 new ObjectDescriptor("value", "value", Dimensionless.class)), (i, a) -> checkDimensionless(i, a).expm1()),
141 new F1("log", Dimensionless.class, new MetaData("log", "returns natural logarithm (logarithm base e) of the argument",
142 new ObjectDescriptor("value", "value", Dimensionless.class)), (i, a) -> checkDimensionless(i, a).log()),
143 new F1("log10", Dimensionless.class, new MetaData("log10", "returns logarithm base 10 of the argument",
144 new ObjectDescriptor("value", "value", Dimensionless.class)), (i, a) -> checkDimensionless(i, a).log10()),
145 new F1("log1p", Dimensionless.class, new MetaData("log1p", "returns natural logarithm (logarithm base e) of the argument plus 1",
146 new ObjectDescriptor("value", "value", Dimensionless.class)), (i, a) -> checkDimensionless(i, a).log1p()),
147 new F1("signum", Dimensionless.class, new MetaData("signum", "returns sign of the argument (1 if positive, -1 if negative, 0 if zero)",
148 new ObjectDescriptor("value", "value", Dimensionless.class)), (i, a) -> checkDimensionless(i, a).signum()),
149 new F1("sin", Dimensionless.class, new MetaData("cos", "returns the sine of the value of the argument",
150 new ObjectDescriptor("value", "value", Dimensionless.class)), (i, a) -> checkDimensionless(i, a).sin()),
151 new F1("sinh", Dimensionless.class, new MetaData("cosh", "returns the hyperbolic sine of the value of the argument",
152 new ObjectDescriptor("value", "value", Dimensionless.class)), (i, a) -> checkDimensionless(i, a).sinh()),
153 new F1("sqrt", Dimensionless.class, new MetaData("cos", "returns the square root of the value of the argument",
154 new ObjectDescriptor("value", "value", Dimensionless.class)), (i, a) -> checkDimensionless(i, a).sqrt()),
155 new F1("tan", Dimensionless.class, new MetaData("cos", "returns the tangent of the value of the argument",
156 new ObjectDescriptor("value", "value", Dimensionless.class)), (i, a) -> checkDimensionless(i, a).tan()),
157 new F1("tanh", Dimensionless.class, new MetaData("cosh", "returns the hyperbolic tangent of the value of the argument",
158 new ObjectDescriptor("value", "value", Dimensionless.class)), (i, a) -> checkDimensionless(i, a).tanh()),
159 new F2("pow", new MetaData("pow", "raises the first argument to the power of the second argument",
160 new ObjectDescriptor("base", "base", Dimensionless.class),
161 new ObjectDescriptor("exponent", "exponent", Dimensionless.class)),
162 (i, b, p)-> performPower(b, p) ),
163 new F2("atan2", new MetaData("atan2",
164 "atan2 function (needs two DoubleScalarRel parameters that have the same SI dimensions)",
165 new ObjectDescriptor("y", "y", DoubleScalarRel.class),
166 new ObjectDescriptor("x", "x", DoubleScalarRel.class)), (i, y, x) -> performAtan2(y, x)),
167 };
168
169
170
171 private Map<String, Function> functionData;
172
173 {
174 this.functionData = new HashMap<>();
175 for (Function function : this.builtinFunctions)
176 {
177 this.functionData.put(function.getId(), function);
178 }
179 }
180
181
182 private Map<String, Function> userDefinedFunctions = null;
183
184
185 private UnitParser userSuppliedUnitParser = null;
186
187
188 private Map<Class<?>, SIDimensions> siDimensionsMap = new HashMap<>();
189
190
191
192
193 public Eval()
194 {
195
196 }
197
198
199
200
201
202
203
204 public Eval setRetrieveValue(final RetrieveValue retrieveValue)
205 {
206 this.retrieveValue = retrieveValue;
207 return this;
208 }
209
210
211
212
213
214
215
216 public Eval setUnitParser(final UnitParser unitParser)
217 {
218 this.userSuppliedUnitParser = unitParser;
219 return this;
220 }
221
222
223
224
225
226
227
228
229
230 public Object evaluate(final String expression) throws RuntimeException
231 {
232 return evaluateExpression(expression);
233 }
234
235
236
237
238
239
240
241 public Boolean evaluateAsBoolean(final String expression) throws RuntimeException
242 {
243 Object result = evaluateExpression(expression);
244 if (!(result instanceof Boolean))
245 {
246 throwException("Result " + result + " can not be cast to a Boolean");
247 }
248 return ((Boolean) result);
249 }
250
251
252
253
254
255
256
257
258 public double evaluateAsDouble(final String expression) throws RuntimeException
259 {
260 Object result = evaluateExpression(expression);
261 if (!(result instanceof DoubleScalar<?, ?>))
262 {
263 throwException("Result " + result + " can not be cast to a double");
264 }
265 return ((DoubleScalar<?, ?>) result).si;
266 }
267
268
269
270
271
272 public Collection<Function> builtInFunctions()
273 {
274 return this.functionData.values();
275 }
276
277
278
279
280
281
282
283 public Eval setUserDefinedFunctions(final Map<String, Function> userDefinedFunctionMap)
284 {
285 this.userDefinedFunctions = userDefinedFunctionMap;
286 return this;
287 }
288
289
290
291
292
293
294
295 public Object evaluateExpression(final String expression) throws RuntimeException
296 {
297 Throw.whenNull(expression, "expression may not be null");
298 Throw.when(expression.length() == 0, IllegalArgumentException.class, "Expression may not be the empty string");
299 String savedExpression = this.expression;
300 int savedPosition = this.position;
301 List<Object> savedStack = new ArrayList<>(this.stack);
302 try
303 {
304 this.expression = expression;
305 this.position = 0;
306 this.stack.clear();
307 eatSpace();
308 evalLhs(0);
309 if (this.position < this.expression.length())
310 {
311 this.throwException("Trailing garbage: \"" + this.expression.substring(this.position) + "\"");
312 }
313 if (this.stack.size() > 1)
314 {
315 this.throwException("Unfinished operations");
316 }
317 if (this.stack.size() <= 0)
318 {
319 this.throwException("No result after evaluation");
320 }
321 return pop();
322 }
323 finally
324 {
325 this.expression = savedExpression;
326 this.position = savedPosition;
327 this.stack = savedStack;
328 }
329 }
330
331
332
333
334 private void eatSpace()
335 {
336 while (this.position < this.expression.length() && Character.isWhitespace(this.expression.charAt(this.position)))
337 {
338 this.position++;
339 }
340 }
341
342
343
344
345
346
347 private void evalLhs(final int bindingStrength) throws RuntimeException
348 {
349 eatSpace();
350 if (this.position >= this.expression.length())
351 {
352 throwException("Missing operand");
353 }
354 char token = this.expression.charAt(this.position);
355 switch (token)
356 {
357 case '-':
358 {
359 this.position++;
360 evalLhs(BIND_UMINUS);
361 Object value = pop();
362 if (value instanceof DoubleScalar<?, ?>)
363 {
364 push(((DoubleScalar<?, ?>) value).neg());
365 break;
366 }
367 throwException("Cannot apply unary minus on " + value);
368 }
369
370 case '!':
371 if (this.position >= this.expression.length() - 1 || '=' != this.expression.charAt(this.position + 1))
372 {
373
374 this.position++;
375 evalLhs(BIND_UMINUS);
376 Object value = pop();
377 if (value instanceof Boolean)
378 {
379 push(!((Boolean) value));
380 break;
381 }
382 throwException("Cannot apply unary not operator on " + value);
383 }
384 break;
385
386 case '(':
387 this.position++;
388 evalLhs(0);
389 if (this.position >= this.expression.length() || ')' != this.expression.charAt(this.position))
390 {
391 throwException("Missing closing parenthesis");
392 }
393 this.position++;
394 break;
395
396 default:
397 {
398 if (Character.isLetter(token) || '#' == token || '@' == token || '_' == token)
399 {
400 push(handleFunctionOrVariableOrNamedConstant());
401 }
402 else if (Character.isDigit(token) || '.' == token)
403 {
404 push(handleNumber());
405 }
406 }
407 }
408 evalRhs(bindingStrength);
409 }
410
411
412
413
414
415
416
417
418 private void evalRhs(final int bindingStrength) throws RuntimeException
419 {
420 eatSpace();
421 while (this.position < this.expression.length())
422 {
423 char token = this.expression.charAt(this.position);
424 switch (token)
425 {
426 case ')':
427 return;
428
429 case '^':
430 if (this.stack.isEmpty())
431 {
432 throwException("Missing left operand");
433 }
434 if (bindingStrength > BIND_POW)
435 {
436 return;
437 }
438 this.position++;
439 evalLhs(BIND_POW);
440 power();
441 break;
442
443 case '*':
444 if (this.stack.isEmpty())
445 {
446 throwException("Missing left operand");
447 }
448 if (bindingStrength >= BIND_MUL)
449 {
450 return;
451 }
452 this.position++;
453 evalLhs(BIND_MUL);
454 multiply();
455 break;
456
457 case '/':
458 if (this.stack.isEmpty())
459 {
460 throwException("Missing left operand");
461 }
462 if (bindingStrength >= BIND_MUL)
463 {
464 return;
465 }
466 this.position++;
467 evalLhs(BIND_MUL);
468 divide();
469 break;
470
471 case '+':
472 if (this.stack.isEmpty())
473 {
474 throwException("Missing left operand");
475 }
476 if (bindingStrength >= BIND_ADD)
477 {
478 return;
479 }
480 this.position++;
481 evalLhs(BIND_ADD);
482 add();
483 break;
484
485 case '-':
486 if (this.stack.isEmpty())
487 {
488 throwException("Missing left operand");
489 }
490 if (bindingStrength >= BIND_ADD)
491 {
492 return;
493 }
494 this.position++;
495 evalLhs(BIND_ADD);
496 subtract();
497 break;
498
499 case '&':
500 if (this.position >= this.expression.length() - 1 || '&' != this.expression.charAt(this.position + 1))
501 {
502 throwException("Single \'&\' is not a valid operator");
503 }
504 if (this.stack.isEmpty())
505 {
506 throwException("Missing left operand");
507 }
508 if (bindingStrength >= BIND_AND)
509 {
510 return;
511 }
512 this.position += 2;
513 evalLhs(BIND_AND);
514 and();
515 break;
516
517 case '|':
518 if (this.position >= this.expression.length() - 1 || '|' != this.expression.charAt(this.position + 1))
519 {
520 throwException("Single \'|\' is not a valid operator");
521 }
522 if (this.stack.isEmpty())
523 {
524 throwException("Missing left operand");
525 }
526 if (bindingStrength >= BIND_OR)
527 {
528 return;
529 }
530 this.position += 2;
531 evalLhs(BIND_OR);
532 or();
533 break;
534
535 case '<':
536 if (this.stack.isEmpty())
537 {
538 throwException("Missing left operand");
539 }
540 this.position++;
541 if (this.position < this.expression.length() && '=' == this.expression.charAt(this.position))
542 {
543 this.position++;
544 evalLhs(BIND_RELATIONAL);
545 compareDoubleScalars((
546 a, b
547 ) -> (a <= b));
548 }
549 else
550 {
551 evalLhs(BIND_RELATIONAL);
552 compareDoubleScalars((
553 a, b
554 ) -> (a < b));
555 }
556 break;
557
558 case '>':
559 if (this.stack.isEmpty())
560 {
561 throwException("Missing left operand");
562 }
563 this.position++;
564 if (this.position < this.expression.length() && '=' == this.expression.charAt(this.position))
565 {
566 this.position++;
567 evalLhs(BIND_RELATIONAL);
568 compareDoubleScalars((
569 a, b
570 ) -> (a >= b));
571 }
572 else
573 {
574 evalLhs(BIND_RELATIONAL);
575 compareDoubleScalars((
576 a, b
577 ) -> (a > b));
578 }
579 break;
580
581 case '=':
582 {
583 if (this.position >= this.expression.length() - 1 || '=' != this.expression.charAt(this.position + 1))
584 {
585 throwException("Single \'=\' is not a valid operator");
586 }
587 if (this.stack.isEmpty())
588 {
589 throwException("Missing left operand");
590 }
591 this.position += 2;
592 evalLhs(BIND_EQUAL);
593 Object right = pop();
594 Object left = pop();
595 push(left.equals(right));
596 break;
597 }
598
599 case '!':
600 {
601 if (this.position >= this.expression.length() - 1 || '=' != this.expression.charAt(this.position + 1))
602 {
603 throwException("Single \'!\' is not a valid operator");
604 }
605 if (this.stack.isEmpty())
606 {
607 throwException("Missing left operand");
608 }
609 this.position += 2;
610 evalLhs(BIND_EQUAL);
611 Object right = pop();
612 Object left = pop();
613 push(!left.equals(right));
614 break;
615 }
616
617 case '?':
618 {
619 if (bindingStrength >= BIND_CONDITIONAL_EXPRESSION)
620 {
621 return;
622 }
623 this.position++;
624
625 Object choice = pop();
626 if (!(choice instanceof Boolean))
627 {
628 throwException("Condition does not evaluate to a logical value");
629 }
630 if (!((Boolean) choice))
631 {
632
633 skip(true);
634 }
635 else
636 {
637 evalLhs(0);
638 }
639 if (this.position >= this.expression.length() || ':' != this.expression.charAt(this.position))
640 {
641 throwException("Missing \':\' of conditional expression");
642 }
643 this.position++;
644 if ((Boolean) choice)
645 {
646
647 skip(false);
648 }
649 else
650 {
651 evalLhs(BIND_CONDITIONAL_EXPRESSION);
652 }
653 break;
654 }
655
656 case ':':
657 return;
658
659 case ',':
660 return;
661
662 default:
663 throwException("Operator expected (got \"" + token + "\")");
664
665 }
666 }
667 }
668
669
670
671
672 interface CompareValues
673 {
674
675
676
677
678
679
680 Object execute(double argument1, double argument2);
681
682 }
683
684
685
686
687
688 private void compareDoubleScalars(final CompareValues comparator)
689 {
690 Object right = pop();
691 Object left = pop();
692 if ((left instanceof DoubleScalar) && (right instanceof DoubleScalar)
693 && getDimensions((DoubleScalar<?, ?>) left).equals(getDimensions((DoubleScalar<?, ?>) right)))
694 {
695 push(comparator.execute(((DoubleScalar<?, ?>) left).si, ((DoubleScalar<?, ?>) right).si));
696 return;
697 }
698 throwException("Cannot compare " + left + " to " + right);
699 }
700
701
702
703
704
705
706 private void throwException(final String description) throws RuntimeException
707 {
708 throw new RuntimeException(description + " at position " + this.position);
709 }
710
711
712
713
714
715
716 private void skip(final boolean thenPart)
717 {
718 while (this.position < this.expression.length())
719 {
720 switch (this.expression.charAt(this.position))
721 {
722 case '(':
723 {
724 int level = 1;
725 this.position++;
726 while (this.position < this.expression.length())
727 {
728 char c = this.expression.charAt(this.position);
729 if ('(' == c)
730 {
731 level++;
732 }
733 else if (')' == c && --level == 0)
734 {
735 break;
736 }
737 this.position++;
738 }
739 if (level > 0)
740 {
741 throwException("Missing closing parenthesis");
742 }
743 this.position++;
744 break;
745 }
746
747 case '?':
748 this.position++;
749 skip(true);
750 if (this.position >= this.expression.length() || ':' != this.expression.charAt(this.position))
751 {
752 throwException("Conditional expression missing \':\'");
753 }
754 this.position++;
755 skip(false);
756 break;
757
758 case ':':
759 return;
760
761 case '^':
762 case '*':
763 case '/':
764 case '+':
765 case '-':
766 case '<':
767 case '=':
768 case '>':
769 case '!':
770 if (!thenPart)
771 {
772 return;
773 }
774 this.position++;
775 break;
776
777 default:
778 this.position++;
779 break;
780 }
781 }
782 if (thenPart)
783 {
784 throwException("Nonterminated conditional expression");
785 }
786
787 }
788
789
790
791
792 private void and()
793 {
794 Object right = pop();
795 Object left = pop();
796 if ((left instanceof Boolean) && (right instanceof Boolean))
797 {
798 push(((Boolean) left) && ((Boolean) right));
799 return;
800 }
801 throwException("Cannot compute logical AND of " + left + " and " + right);
802 }
803
804
805
806
807 private void or()
808 {
809 Object right = pop();
810 Object left = pop();
811 if ((left instanceof Boolean) && (right instanceof Boolean))
812 {
813 push(((Boolean) left) || ((Boolean) right));
814 return;
815 }
816 throwException("Cannot compute logical AND of " + left + " and " + right);
817 }
818
819
820
821
822 private void power()
823 {
824 Object exponent = pop();
825 Object base = pop();
826 push(performPower(base, exponent));
827 }
828
829
830
831
832
833
834
835 private Object performPower(final Object base, final Object exponent)
836 {
837 if ((base instanceof DoubleScalarRel) && (exponent instanceof DoubleScalarRel)
838 && getDimensions((DoubleScalarRel<?, ?>) base).equals(getDimensions(DimensionlessUnit.SI))
839 && getDimensions((DoubleScalarRel<?, ?>) exponent).equals(getDimensions(DimensionlessUnit.SI)))
840 {
841 DoubleScalar<?, ?> result = DoubleScalarRel.instantiate(
842 Math.pow(((DoubleScalarRel<?, ?>) base).si, ((DoubleScalarRel<?, ?>) exponent).si), DimensionlessUnit.SI);
843
844 return result;
845 }
846 throwException("Cannot raise " + base + " to power " + exponent);
847 return null;
848 }
849
850
851
852
853
854
855
856 private Object performAtan2(final Object y, final Object x)
857 {
858 if ((y instanceof DoubleScalarRel) && (x instanceof DoubleScalarRel)
859 && getDimensions((DoubleScalarRel<?, ?>) y).equals(getDimensions((DoubleScalarRel<?, ?>) x)))
860 {
861 DoubleScalar<?, ?> result = DoubleScalarRel.instantiate(
862 Math.atan2(((DoubleScalarRel<?, ?>) y).si, ((DoubleScalarRel<?, ?>) x).si), DimensionlessUnit.SI);
863
864 return result;
865 }
866 throwException("Cannot compute atan2 of " + y + ", " + x + ")");
867 return null;
868
869 }
870
871
872
873
874 private void multiply()
875 {
876 Object right = pop();
877 Object left = pop();
878 if ((right instanceof DoubleScalarRel) && (left instanceof DoubleScalarRel))
879 {
880 push(((DoubleScalarRel<?, ?>) left).times((DoubleScalarRel<?, ?>) right));
881 return;
882 }
883 throwException("Cannot multiply with " + right + " as right hand operand");
884 }
885
886
887
888
889 private void divide()
890 {
891 Object right = pop();
892 Object left = pop();
893 if ((left instanceof DoubleScalarRel) && (right instanceof DoubleScalarRel))
894 {
895 if (0.0 == ((DoubleScalarRel<?, ?>) right).si)
896 {
897 throwException("Division by 0");
898 }
899 push(((DoubleScalarRel<?, ?>) left).divide((DoubleScalarRel<?, ?>) right));
900 return;
901 }
902 throwException("Cannot divide " + left + " by " + right);
903 }
904
905
906
907
908 private void add()
909 {
910 Object right = pop();
911 Object left = pop();
912 if (!(left instanceof DoubleScalar))
913 {
914 throwException("Left operand of addition must be a scalar (got \"" + left + "\")");
915 }
916 if (!(right instanceof DoubleScalar))
917 {
918 throwException("Right operand of addition must be a scalar (got \"" + right + "\")");
919 }
920
921 if (!((DoubleScalar<?, ?>) left).getDisplayUnit().getQuantity().getSiDimensions()
922 .equals(getDimensions((DoubleScalar<?, ?>) right)))
923 {
924
925
926 throwException("Cannot add " + left + " to " + right + " because the types are incompatible");
927 }
928
929 if ((left instanceof DoubleScalarRel) && (right instanceof DoubleScalarRel))
930 {
931
932 DoubleScalar<?, ?> sum =
933 DoubleScalarRel.instantiateAnonymous(((DoubleScalarRel<?, ?>) left).si + ((DoubleScalarRel<?, ?>) right).si,
934 ((DoubleScalarRel<?, ?>) left).getDisplayUnit().getStandardUnit());
935
936
937 push(sum);
938 return;
939 }
940 if (right instanceof DoubleScalarAbs)
941 {
942 throwException("Cannot add an absolute value to some other value");
943 }
944
945 DoubleScalar<?,
946 ?> sum = DoubleScalarAbs.instantiateAnonymous(
947 ((DoubleScalarAbs<?, ?, ?, ?>) left).si + ((DoubleScalarRel<?, ?>) right).si,
948 ((DoubleScalarAbs<?, ?, ?, ?>) left).getDisplayUnit().getStandardUnit());
949
950
951 push(sum);
952 }
953
954
955
956
957 private void subtract()
958 {
959 Object right = pop();
960 Object left = pop();
961 if (!(left instanceof DoubleScalar))
962 {
963 throwException("Left operand of subtraction must be a scalar (got \"" + left + "\")");
964 }
965 if (!(right instanceof DoubleScalar))
966 {
967 throwException("Right operand of subtraction must be a scalar (got \"" + right + "\")");
968 }
969
970 if (!getDimensions((DoubleScalar<?, ?>) left).equals(getDimensions((DoubleScalar<?, ?>) right)))
971 {
972 throwException("Cannot subtract " + right + " from " + left + " because the types are incompatible");
973 }
974 if ((left instanceof DoubleScalarAbs) && (right instanceof DoubleScalarAbs))
975 {
976
977 DoubleScalar<?, ?> difference =
978 DoubleScalarRel.instantiateAnonymous(((DoubleScalar<?, ?>) left).si - ((DoubleScalar<?, ?>) right).si,
979 ((DoubleScalar<?, ?>) left).getDisplayUnit().getStandardUnit());
980
981 push(difference);
982 return;
983 }
984 if ((left instanceof DoubleScalarAbs) && (right instanceof DoubleScalarRel))
985 {
986
987 DoubleScalar<?, ?> difference =
988 DoubleScalarAbs.instantiateAnonymous(((DoubleScalar<?, ?>) left).si - ((DoubleScalar<?, ?>) right).si,
989 ((DoubleScalar<?, ?>) left).getDisplayUnit().getStandardUnit());
990
991 push(difference);
992 return;
993 }
994 if ((left instanceof DoubleScalarRel) && (right instanceof DoubleScalarAbs))
995 {
996
997 throwException("Cannot subtract " + right + " from " + left + " because the right operand is absolute");
998 }
999
1000 DoubleScalar<?, ?> difference =
1001 DoubleScalarRel.instantiateAnonymous(((DoubleScalar<?, ?>) left).si - ((DoubleScalar<?, ?>) right).si,
1002 ((DoubleScalar<?, ?>) left).getDisplayUnit().getStandardUnit());
1003
1004 push(difference);
1005 }
1006
1007
1008
1009
1010
1011
1012 private DoubleScalar<?, ?> handleNumber()
1013 {
1014
1015 int startPosition = this.position;
1016 boolean seenExp = false;
1017 boolean seenExpSign = false;
1018 boolean seenExpDigit = false;
1019 boolean seenRadix = false;
1020 while (this.position < this.expression.length())
1021 {
1022 char c = this.expression.charAt(this.position);
1023
1024
1025 if (seenExp && (!seenExpDigit) && ('-' == c || '+' == c))
1026 {
1027 if (seenExpSign)
1028 {
1029 throwException("Too many signs in exponent");
1030 }
1031 seenExpSign = true;
1032 }
1033 else if (seenExp && seenExpSign && (!Character.isDigit(c)))
1034 {
1035 break;
1036 }
1037 else if ('e' == c || 'E' == c)
1038 {
1039 if (seenExp)
1040 {
1041 throwException("Too many 'e' or 'E' indicators");
1042 }
1043 seenExp = true;
1044 }
1045 else if ('.' == c)
1046 {
1047 if (seenRadix)
1048 {
1049 throwException("Too many '.'");
1050 }
1051 if (seenExp)
1052 {
1053 throwException("A \'.\' is not allowed after \'e\' or \'E\'");
1054 }
1055 seenRadix = true;
1056 }
1057 else if (Character.isDigit(c))
1058 {
1059 if (seenExp)
1060 {
1061 seenExpDigit = true;
1062 }
1063 }
1064 else
1065 {
1066 break;
1067 }
1068 this.position++;
1069 }
1070 eatSpace();
1071 if (seenExp && !seenExpDigit)
1072 {
1073 throwException("Exponent value missing");
1074 }
1075 String number = this.expression.substring(startPosition, this.position);
1076 if (this.position < this.expression.length() && '[' == this.expression.charAt(this.position))
1077 {
1078 this.position++;
1079 startPosition = this.position;
1080 while (this.position < this.expression.length())
1081 {
1082 char c = this.expression.charAt(this.position);
1083 if (']' == c)
1084 {
1085 String unit = this.expression.substring(startPosition, this.position++);
1086 DoubleScalar<?, ?> result = null;
1087 if (null != this.userSuppliedUnitParser)
1088 {
1089 result = this.userSuppliedUnitParser.parseUnit(Double.parseDouble(number), unit);
1090 }
1091 if (null == result)
1092 {
1093 result = SIScalar.valueOf(number + " " + unit);
1094 }
1095 return result;
1096 }
1097 if ((!Character.isLetterOrDigit(c)) && '-' != c && '^' != c && '/' != c && '.' != c)
1098 {
1099 throwException("Bad symbol in SI unit string");
1100 }
1101 this.position++;
1102 }
1103 if (this.position >= this.expression.length())
1104 {
1105 throwException("Missing closing bracket (\']\')");
1106 }
1107 }
1108 return SIScalar.valueOf(number);
1109 }
1110
1111
1112
1113
1114
1115
1116 private Object handleFunctionOrVariableOrNamedConstant()
1117 {
1118 int startPosition = this.position;
1119 while (this.position < this.expression.length())
1120 {
1121 char c = this.expression.charAt(this.position);
1122 if (Character.isLetterOrDigit(c) || '.' == c || '_' == c || '@' == c || '#' == c)
1123 {
1124 this.position++;
1125 }
1126 else
1127 {
1128 break;
1129 }
1130 }
1131 String name = this.expression.substring(startPosition, this.position);
1132 eatSpace();
1133 if (this.position >= this.expression.length() || this.expression.charAt(this.position) != '(')
1134 {
1135
1136 Object result = null == this.retrieveValue ? null : this.retrieveValue.lookup(name);
1137 if (null == result)
1138 {
1139 throwException("Cannot resolve variable " + name);
1140 }
1141
1142 return result;
1143 }
1144
1145
1146 this.position++;
1147 eatSpace();
1148 int argCount = 0;
1149 while (this.position < this.expression.length() && ')' != this.expression.charAt(this.position))
1150 {
1151 evalLhs(0);
1152 argCount++;
1153 eatSpace();
1154 if (this.position < this.expression.length() && ',' == this.expression.charAt(this.position))
1155 {
1156 this.position++;
1157 }
1158 }
1159 if (this.position >= this.expression.length() || ')' != this.expression.charAt(this.position))
1160 {
1161 throwException("Missing closing parenthesis");
1162 }
1163 this.position++;
1164 Function f = null;
1165 if (null != this.userDefinedFunctions)
1166 {
1167 f = this.userDefinedFunctions.get(name);
1168 }
1169 if (null == f)
1170 {
1171 f = this.functionData.get(name);
1172 }
1173 if (null == f)
1174 {
1175 throwException("Unknown function " + name);
1176 }
1177 Object[] args = new Object[argCount];
1178 for (int i = 0; i < argCount; i++)
1179 {
1180 args[argCount - i - 1] = pop();
1181 }
1182 if (f.getMetaData() != MetaData.NO_META_DATA)
1183 {
1184
1185 int argsNeeded = f.getMetaData().getObjectDescriptors().length;
1186 if (argsNeeded != argCount)
1187 {
1188 throwException(name + " needs " + argsNeeded + " parameter" + (argsNeeded == 1 ? "" : "s") + " (got " + argCount
1189 + ")");
1190 }
1191 for (int i = 0; i < argCount; i++)
1192 {
1193 if ((args[i] instanceof Boolean) && (!Boolean.class.isAssignableFrom(f.getMetaData().getObjectClass(i))))
1194 {
1195 throwException(name + " does not take " + args[i] + " as parameter " + i);
1196 }
1197 else if ((args[i] instanceof DoubleScalar)
1198 && (DoubleScalar.class.isAssignableFrom(f.getMetaData().getObjectClass(i))))
1199 {
1200 DoubleScalar<?, ?> ds = (DoubleScalar<?, ?>) args[i];
1201 Class<?> clazz = f.getMetaData().getObjectClass(i);
1202 if (!clazz.equals(DoubleScalarRel.class))
1203 {
1204 SIDimensions siDimensions = this.siDimensionsMap.get(clazz);
1205 if (null == siDimensions)
1206 {
1207
1208 try
1209 {
1210 Field field = clazz.getDeclaredField("ZERO");
1211 DoubleScalar<?, ?> zero = (DoubleScalar<?, ?>) field.get(clazz);
1212 siDimensions = zero.getDisplayUnit().getQuantity().getSiDimensions();
1213 this.siDimensionsMap.put(clazz, siDimensions);
1214 }
1215 catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException nsfe)
1216 {
1217 throwException("ERROR: Cannot determine quantity for " + clazz.getCanonicalName());
1218 }
1219 }
1220 if (!siDimensions.equals(getDimensions(ds)))
1221 {
1222 throwException("parameter " + i + " of " + name + " has incompatible quantity");
1223 }
1224 }
1225 }
1226 else
1227 {
1228 throwException("Argument " + i + " of function " + name + " is of an unhandled type (" + args[i] + ")");
1229 }
1230 }
1231 }
1232
1233 return (f.function(args));
1234 }
1235
1236
1237
1238
1239
1240 private void push(final Object object)
1241 {
1242 this.stack.add(object);
1243 }
1244
1245
1246
1247
1248
1249
1250 private Object pop() throws RuntimeException
1251 {
1252 if (this.stack.isEmpty())
1253 {
1254 throwException("Stack empty");
1255 }
1256 return (this.stack.remove(this.stack.size() - 1));
1257 }
1258
1259
1260
1261
1262
1263
1264
1265 private Dimensionless checkDimensionless(final Function functionData, final Object object)
1266 {
1267 if (!(object instanceof DoubleScalar))
1268 {
1269 throwException("Function " + functionData.getId() + " cannot be applied to " + object);
1270 }
1271 DoubleScalar<?, ?> ds = (DoubleScalar<?, ?>) object;
1272 if (!getDimensions(ds).equals(getDimensions(DimensionlessUnit.SI)))
1273 {
1274 throwException("Function " + functionData.getId() + " cannot be applied to " + ds);
1275 }
1276 return new Dimensionless(ds.si, DimensionlessUnit.SI);
1277 }
1278
1279
1280
1281
1282
1283
1284 private static SIDimensions getDimensions(final DoubleScalar<?, ?> doubleScalar)
1285 {
1286 return doubleScalar.getDisplayUnit().getQuantity().getSiDimensions();
1287 }
1288
1289
1290
1291
1292
1293
1294 private static SIDimensions getDimensions(final Unit<?> unit)
1295 {
1296 return unit.getQuantity().getSiDimensions();
1297 }
1298
1299 }