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 public Object evaluate(final String expression) throws RuntimeException
229 {
230 return evaluateExpression(expression);
231 }
232
233
234
235
236
237
238
239 public Boolean evaluateAsBoolean(final String expression) throws RuntimeException
240 {
241 Object result = evaluateExpression(expression);
242 if (!(result instanceof Boolean))
243 {
244 throwException("Result " + result + " can not be cast to a Boolean");
245 }
246 return ((Boolean) result);
247 }
248
249
250
251
252
253
254
255
256 public double evaluateAsDouble(final String expression) throws RuntimeException
257 {
258 Object result = evaluateExpression(expression);
259 if (!(result instanceof DoubleScalar<?, ?>))
260 {
261 throwException("Result " + result + " can not be cast to a double");
262 }
263 return ((DoubleScalar<?, ?>) result).si;
264 }
265
266
267
268
269
270 public Collection<Function> builtInFunctions()
271 {
272 return this.functionData.values();
273 }
274
275
276
277
278
279
280
281 public Eval setUserDefinedFunctions(final Map<String, Function> userDefinedFunctionMap)
282 {
283 this.userDefinedFunctions = userDefinedFunctionMap;
284 return this;
285 }
286
287
288
289
290
291
292
293 public Object evaluateExpression(final String expression) throws RuntimeException
294 {
295 Throw.whenNull(expression, "expression may not be null");
296 Throw.when(expression.length() == 0, IllegalArgumentException.class, "Expression may not be the empty string");
297 String savedExpression = this.expression;
298 int savedPosition = this.position;
299 List<Object> savedStack = new ArrayList<>(this.stack);
300 try
301 {
302 this.expression = expression;
303 this.position = 0;
304 this.stack.clear();
305 eatSpace();
306 evalLhs(0);
307 if (this.position < this.expression.length())
308 {
309 this.throwException("Trailing garbage: \"" + this.expression.substring(this.position) + "\"");
310 }
311 if (this.stack.size() > 1)
312 {
313 this.throwException("Unfinished operations");
314 }
315 if (this.stack.size() <= 0)
316 {
317 this.throwException("No result after evaluation");
318 }
319 return pop();
320 }
321 finally
322 {
323 this.expression = savedExpression;
324 this.position = savedPosition;
325 this.stack = savedStack;
326 }
327 }
328
329
330
331
332 private void eatSpace()
333 {
334 while (this.position < this.expression.length() && Character.isWhitespace(this.expression.charAt(this.position)))
335 {
336 this.position++;
337 }
338 }
339
340
341
342
343
344
345 private void evalLhs(final int bindingStrength) throws RuntimeException
346 {
347 eatSpace();
348 if (this.position >= this.expression.length())
349 {
350 throwException("Missing operand");
351 }
352 char token = this.expression.charAt(this.position);
353 switch (token)
354 {
355 case '-':
356 {
357 this.position++;
358 evalLhs(BIND_UMINUS);
359 Object value = pop();
360 if (value instanceof DoubleScalar<?, ?>)
361 {
362 push(((DoubleScalar<?, ?>) value).neg());
363 break;
364 }
365 throwException("Cannot apply unary minus on " + value);
366 }
367
368 case '!':
369 if (this.position >= this.expression.length() - 1 || '=' != this.expression.charAt(this.position + 1))
370 {
371
372 this.position++;
373 evalLhs(BIND_UMINUS);
374 Object value = pop();
375 if (value instanceof Boolean)
376 {
377 push(!((Boolean) value));
378 break;
379 }
380 throwException("Cannot apply unary not operator on " + value);
381 }
382 break;
383
384 case '(':
385 this.position++;
386 evalLhs(0);
387 if (this.position >= this.expression.length() || ')' != this.expression.charAt(this.position))
388 {
389 throwException("Missing closing parenthesis");
390 }
391 this.position++;
392 break;
393
394 default:
395 {
396 if (Character.isLetter(token) || '#' == token || '@' == token || '_' == token)
397 {
398 push(handleFunctionOrVariableOrNamedConstant());
399 }
400 else if (Character.isDigit(token) || '.' == token)
401 {
402 push(handleNumber());
403 }
404 }
405 }
406 evalRhs(bindingStrength);
407 }
408
409
410
411
412
413
414
415
416 private void evalRhs(final int bindingStrength) throws RuntimeException
417 {
418 eatSpace();
419 while (this.position < this.expression.length())
420 {
421 char token = this.expression.charAt(this.position);
422 switch (token)
423 {
424 case ')':
425 return;
426
427 case '^':
428 if (this.stack.isEmpty())
429 {
430 throwException("Missing left operand");
431 }
432 if (bindingStrength > BIND_POW)
433 {
434 return;
435 }
436 this.position++;
437 evalLhs(BIND_POW);
438 power();
439 break;
440
441 case '*':
442 if (this.stack.isEmpty())
443 {
444 throwException("Missing left operand");
445 }
446 if (bindingStrength >= BIND_MUL)
447 {
448 return;
449 }
450 this.position++;
451 evalLhs(BIND_MUL);
452 multiply();
453 break;
454
455 case '/':
456 if (this.stack.isEmpty())
457 {
458 throwException("Missing left operand");
459 }
460 if (bindingStrength >= BIND_MUL)
461 {
462 return;
463 }
464 this.position++;
465 evalLhs(BIND_MUL);
466 divide();
467 break;
468
469 case '+':
470 if (this.stack.isEmpty())
471 {
472 throwException("Missing left operand");
473 }
474 if (bindingStrength >= BIND_ADD)
475 {
476 return;
477 }
478 this.position++;
479 evalLhs(BIND_ADD);
480 add();
481 break;
482
483 case '-':
484 if (this.stack.isEmpty())
485 {
486 throwException("Missing left operand");
487 }
488 if (bindingStrength >= BIND_ADD)
489 {
490 return;
491 }
492 this.position++;
493 evalLhs(BIND_ADD);
494 subtract();
495 break;
496
497 case '&':
498 if (this.position >= this.expression.length() - 1 || '&' != this.expression.charAt(this.position + 1))
499 {
500 throwException("Single \'&\' is not a valid operator");
501 }
502 if (this.stack.isEmpty())
503 {
504 throwException("Missing left operand");
505 }
506 if (bindingStrength >= BIND_AND)
507 {
508 return;
509 }
510 this.position += 2;
511 evalLhs(BIND_AND);
512 and();
513 break;
514
515 case '|':
516 if (this.position >= this.expression.length() - 1 || '|' != this.expression.charAt(this.position + 1))
517 {
518 throwException("Single \'|\' is not a valid operator");
519 }
520 if (this.stack.isEmpty())
521 {
522 throwException("Missing left operand");
523 }
524 if (bindingStrength >= BIND_OR)
525 {
526 return;
527 }
528 this.position += 2;
529 evalLhs(BIND_OR);
530 or();
531 break;
532
533 case '<':
534 if (this.stack.isEmpty())
535 {
536 throwException("Missing left operand");
537 }
538 this.position++;
539 if (this.position < this.expression.length() && '=' == this.expression.charAt(this.position))
540 {
541 this.position++;
542 evalLhs(BIND_RELATIONAL);
543 compareDoubleScalars((
544 a, b
545 ) -> (a <= b));
546 }
547 else
548 {
549 evalLhs(BIND_RELATIONAL);
550 compareDoubleScalars((
551 a, b
552 ) -> (a < b));
553 }
554 break;
555
556 case '>':
557 if (this.stack.isEmpty())
558 {
559 throwException("Missing left operand");
560 }
561 this.position++;
562 if (this.position < this.expression.length() && '=' == this.expression.charAt(this.position))
563 {
564 this.position++;
565 evalLhs(BIND_RELATIONAL);
566 compareDoubleScalars((
567 a, b
568 ) -> (a >= b));
569 }
570 else
571 {
572 evalLhs(BIND_RELATIONAL);
573 compareDoubleScalars((
574 a, b
575 ) -> (a > b));
576 }
577 break;
578
579 case '=':
580 {
581 if (this.position >= this.expression.length() - 1 || '=' != this.expression.charAt(this.position + 1))
582 {
583 throwException("Single \'=\' is not a valid operator");
584 }
585 if (this.stack.isEmpty())
586 {
587 throwException("Missing left operand");
588 }
589 this.position += 2;
590 evalLhs(BIND_EQUAL);
591 Object right = pop();
592 Object left = pop();
593 push(left.equals(right));
594 break;
595 }
596
597 case '!':
598 {
599 if (this.position >= this.expression.length() - 1 || '=' != this.expression.charAt(this.position + 1))
600 {
601 throwException("Single \'!\' is not a valid operator");
602 }
603 if (this.stack.isEmpty())
604 {
605 throwException("Missing left operand");
606 }
607 this.position += 2;
608 evalLhs(BIND_EQUAL);
609 Object right = pop();
610 Object left = pop();
611 push(!left.equals(right));
612 break;
613 }
614
615 case '?':
616 {
617 if (bindingStrength >= BIND_CONDITIONAL_EXPRESSION)
618 {
619 return;
620 }
621 this.position++;
622
623 Object choice = pop();
624 if (!(choice instanceof Boolean))
625 {
626 throwException("Condition does not evaluate to a logical value");
627 }
628 if (!((Boolean) choice))
629 {
630
631 skip(true);
632 }
633 else
634 {
635 evalLhs(0);
636 }
637 if (this.position >= this.expression.length() || ':' != this.expression.charAt(this.position))
638 {
639 throwException("Missing \':\' of conditional expression");
640 }
641 this.position++;
642 if ((Boolean) choice)
643 {
644
645 skip(false);
646 }
647 else
648 {
649 evalLhs(BIND_CONDITIONAL_EXPRESSION);
650 }
651 break;
652 }
653
654 case ':':
655 return;
656
657 case ',':
658 return;
659
660 default:
661 throwException("Operator expected (got \"" + token + "\")");
662
663 }
664 }
665 }
666
667
668
669
670 interface CompareValues
671 {
672
673
674
675
676
677
678 Object execute(double argument1, double argument2);
679
680 }
681
682
683
684
685
686 private void compareDoubleScalars(final CompareValues comparator)
687 {
688 Object right = pop();
689 Object left = pop();
690 if ((left instanceof DoubleScalar) && (right instanceof DoubleScalar)
691 && getDimensions((DoubleScalar<?, ?>) left).equals(getDimensions((DoubleScalar<?, ?>) right)))
692 {
693 push(comparator.execute(((DoubleScalar<?, ?>) left).si, ((DoubleScalar<?, ?>) right).si));
694 return;
695 }
696 throwException("Cannot compare " + left + " to " + right);
697 }
698
699
700
701
702
703
704 private void throwException(final String description) throws RuntimeException
705 {
706 throw new RuntimeException(description + " at position " + this.position);
707 }
708
709
710
711
712
713
714 private void skip(final boolean thenPart)
715 {
716 while (this.position < this.expression.length())
717 {
718 switch (this.expression.charAt(this.position))
719 {
720 case '(':
721 {
722 int level = 1;
723 this.position++;
724 while (this.position < this.expression.length())
725 {
726 char c = this.expression.charAt(this.position);
727 if ('(' == c)
728 {
729 level++;
730 }
731 else if (')' == c && --level == 0)
732 {
733 break;
734 }
735 this.position++;
736 }
737 if (level > 0)
738 {
739 throwException("Missing closing parenthesis");
740 }
741 this.position++;
742 break;
743 }
744
745 case '?':
746 this.position++;
747 skip(true);
748 if (this.position >= this.expression.length() || ':' != this.expression.charAt(this.position))
749 {
750 throwException("Conditional expression missing \':\'");
751 }
752 this.position++;
753 skip(false);
754 break;
755
756 case ':':
757 return;
758
759 case '^':
760 case '*':
761 case '/':
762 case '+':
763 case '-':
764 case '<':
765 case '=':
766 case '>':
767 case '!':
768 if (!thenPart)
769 {
770 return;
771 }
772 this.position++;
773 break;
774
775 default:
776 this.position++;
777 break;
778 }
779 }
780 if (thenPart)
781 {
782 throwException("Nonterminated conditional expression");
783 }
784
785 }
786
787
788
789
790 private void and()
791 {
792 Object right = pop();
793 Object left = pop();
794 if ((left instanceof Boolean) && (right instanceof Boolean))
795 {
796 push(((Boolean) left) && ((Boolean) right));
797 return;
798 }
799 throwException("Cannot compute logical AND of " + left + " and " + right);
800 }
801
802
803
804
805 private void or()
806 {
807 Object right = pop();
808 Object left = pop();
809 if ((left instanceof Boolean) && (right instanceof Boolean))
810 {
811 push(((Boolean) left) || ((Boolean) right));
812 return;
813 }
814 throwException("Cannot compute logical AND of " + left + " and " + right);
815 }
816
817
818
819
820 private void power()
821 {
822 Object exponent = pop();
823 Object base = pop();
824 push(performPower(base, exponent));
825 }
826
827
828
829
830
831
832
833 private Object performPower(final Object base, final Object exponent)
834 {
835 if ((base instanceof DoubleScalarRel) && (exponent instanceof DoubleScalarRel)
836 && getDimensions((DoubleScalarRel<?, ?>) base).equals(getDimensions(DimensionlessUnit.SI))
837 && getDimensions((DoubleScalarRel<?, ?>) exponent).equals(getDimensions(DimensionlessUnit.SI)))
838 {
839 DoubleScalar<?, ?> result = DoubleScalarRel.instantiate(
840 Math.pow(((DoubleScalarRel<?, ?>) base).si, ((DoubleScalarRel<?, ?>) exponent).si), DimensionlessUnit.SI);
841
842 return result;
843 }
844 throwException("Cannot raise " + base + " to power " + exponent);
845 return null;
846 }
847
848
849
850
851
852
853
854 private Object performAtan2(final Object y, final Object x)
855 {
856 if ((y instanceof DoubleScalarRel) && (x instanceof DoubleScalarRel)
857 && getDimensions((DoubleScalarRel<?, ?>) y).equals(getDimensions((DoubleScalarRel<?, ?>) x)))
858 {
859 DoubleScalar<?, ?> result = DoubleScalarRel.instantiate(
860 Math.atan2(((DoubleScalarRel<?, ?>) y).si, ((DoubleScalarRel<?, ?>) x).si), DimensionlessUnit.SI);
861
862 return result;
863 }
864 throwException("Cannot compute atan2 of " + y + ", " + x + ")");
865 return null;
866
867 }
868
869
870
871
872 private void multiply()
873 {
874 Object right = pop();
875 Object left = pop();
876 if ((right instanceof DoubleScalarRel) && (left instanceof DoubleScalarRel))
877 {
878 push(((DoubleScalarRel<?, ?>) left).times((DoubleScalarRel<?, ?>) right));
879 return;
880 }
881 throwException("Cannot multiply with " + right + " as right hand operand");
882 }
883
884
885
886
887 private void divide()
888 {
889 Object right = pop();
890 Object left = pop();
891 if ((left instanceof DoubleScalarRel) && (right instanceof DoubleScalarRel))
892 {
893 if (0.0 == ((DoubleScalarRel<?, ?>) right).si)
894 {
895 throwException("Division by 0");
896 }
897 push(((DoubleScalarRel<?, ?>) left).divide((DoubleScalarRel<?, ?>) right));
898 return;
899 }
900 throwException("Cannot divide " + left + " by " + right);
901 }
902
903
904
905
906 private void add()
907 {
908 Object right = pop();
909 Object left = pop();
910 if (!(left instanceof DoubleScalar))
911 {
912 throwException("Left operand of addition must be a scalar (got \"" + left + "\")");
913 }
914 if (!(right instanceof DoubleScalar))
915 {
916 throwException("Right operand of addition must be a scalar (got \"" + right + "\")");
917 }
918
919 if (!((DoubleScalar<?, ?>) left).getDisplayUnit().getQuantity().getSiDimensions()
920 .equals(getDimensions((DoubleScalar<?, ?>) right)))
921 {
922
923
924 throwException("Cannot add " + left + " to " + right + " because the types are incompatible");
925 }
926
927 if ((left instanceof DoubleScalarRel) && (right instanceof DoubleScalarRel))
928 {
929
930 DoubleScalar<?, ?> sum =
931 DoubleScalarRel.instantiateAnonymous(((DoubleScalarRel<?, ?>) left).si + ((DoubleScalarRel<?, ?>) right).si,
932 ((DoubleScalarRel<?, ?>) left).getDisplayUnit().getStandardUnit());
933
934
935 push(sum);
936 return;
937 }
938 if (right instanceof DoubleScalarAbs)
939 {
940 throwException("Cannot add an absolute value to some other value");
941 }
942
943 DoubleScalar<?,
944 ?> sum = DoubleScalarAbs.instantiateAnonymous(
945 ((DoubleScalarAbs<?, ?, ?, ?>) left).si + ((DoubleScalarRel<?, ?>) right).si,
946 ((DoubleScalarAbs<?, ?, ?, ?>) left).getDisplayUnit().getStandardUnit());
947
948
949 push(sum);
950 }
951
952
953
954
955 private void subtract()
956 {
957 Object right = pop();
958 Object left = pop();
959 if (!(left instanceof DoubleScalar))
960 {
961 throwException("Left operand of subtraction must be a scalar (got \"" + left + "\")");
962 }
963 if (!(right instanceof DoubleScalar))
964 {
965 throwException("Right operand of subtraction must be a scalar (got \"" + right + "\")");
966 }
967
968 if (!getDimensions((DoubleScalar<?, ?>) left).equals(getDimensions((DoubleScalar<?, ?>) right)))
969 {
970 throwException("Cannot subtract " + right + " from " + left + " because the types are incompatible");
971 }
972 if ((left instanceof DoubleScalarAbs) && (right instanceof DoubleScalarAbs))
973 {
974
975 DoubleScalar<?, ?> difference =
976 DoubleScalarRel.instantiateAnonymous(((DoubleScalar<?, ?>) left).si - ((DoubleScalar<?, ?>) right).si,
977 ((DoubleScalar<?, ?>) left).getDisplayUnit().getStandardUnit());
978
979 push(difference);
980 return;
981 }
982 if ((left instanceof DoubleScalarAbs) && (right instanceof DoubleScalarRel))
983 {
984
985 DoubleScalar<?, ?> difference =
986 DoubleScalarAbs.instantiateAnonymous(((DoubleScalar<?, ?>) left).si - ((DoubleScalar<?, ?>) right).si,
987 ((DoubleScalar<?, ?>) left).getDisplayUnit().getStandardUnit());
988
989 push(difference);
990 return;
991 }
992 if ((left instanceof DoubleScalarRel) && (right instanceof DoubleScalarAbs))
993 {
994
995 throwException("Cannot subtract " + right + " from " + left + " because the right operand is absolute");
996 }
997
998 DoubleScalar<?, ?> difference =
999 DoubleScalarRel.instantiateAnonymous(((DoubleScalar<?, ?>) left).si - ((DoubleScalar<?, ?>) right).si,
1000 ((DoubleScalar<?, ?>) left).getDisplayUnit().getStandardUnit());
1001
1002 push(difference);
1003 }
1004
1005
1006
1007
1008
1009
1010 private DoubleScalar<?, ?> handleNumber()
1011 {
1012
1013 int startPosition = this.position;
1014 boolean seenExp = false;
1015 boolean seenExpSign = false;
1016 boolean seenExpDigit = false;
1017 boolean seenRadix = false;
1018 while (this.position < this.expression.length())
1019 {
1020 char c = this.expression.charAt(this.position);
1021
1022
1023 if (seenExp && (!seenExpDigit) && ('-' == c || '+' == c))
1024 {
1025 if (seenExpSign)
1026 {
1027 throwException("Too many signs in exponent");
1028 }
1029 seenExpSign = true;
1030 }
1031 else if (seenExp && seenExpSign && (!Character.isDigit(c)))
1032 {
1033 break;
1034 }
1035 else if ('e' == c || 'E' == c)
1036 {
1037 if (seenExp)
1038 {
1039 throwException("Too many 'e' or 'E' indicators");
1040 }
1041 seenExp = true;
1042 }
1043 else if ('.' == c)
1044 {
1045 if (seenRadix)
1046 {
1047 throwException("Too many '.'");
1048 }
1049 if (seenExp)
1050 {
1051 throwException("A \'.\' is not allowed after \'e\' or \'E\'");
1052 }
1053 seenRadix = true;
1054 }
1055 else if (Character.isDigit(c))
1056 {
1057 if (seenExp)
1058 {
1059 seenExpDigit = true;
1060 }
1061 }
1062 else
1063 {
1064 break;
1065 }
1066 this.position++;
1067 }
1068 eatSpace();
1069 if (seenExp && !seenExpDigit)
1070 {
1071 throwException("Exponent value missing");
1072 }
1073 String number = this.expression.substring(startPosition, this.position);
1074 if (this.position < this.expression.length() && '[' == this.expression.charAt(this.position))
1075 {
1076 this.position++;
1077 startPosition = this.position;
1078 while (this.position < this.expression.length())
1079 {
1080 char c = this.expression.charAt(this.position);
1081 if (']' == c)
1082 {
1083 String unit = this.expression.substring(startPosition, this.position++);
1084 DoubleScalar<?, ?> result = null;
1085 if (null != this.userSuppliedUnitParser)
1086 {
1087 result = this.userSuppliedUnitParser.parseUnit(Double.parseDouble(number), unit);
1088 }
1089 if (null == result)
1090 {
1091 result = SIScalar.valueOf(number + " " + unit);
1092 }
1093 return result;
1094 }
1095 if ((!Character.isLetterOrDigit(c)) && '-' != c && '^' != c && '/' != c && '.' != c)
1096 {
1097 throwException("Bad symbol in SI unit string");
1098 }
1099 this.position++;
1100 }
1101 if (this.position >= this.expression.length())
1102 {
1103 throwException("Missing closing bracket (\']\')");
1104 }
1105 }
1106 return SIScalar.valueOf(number);
1107 }
1108
1109
1110
1111
1112
1113
1114 private Object handleFunctionOrVariableOrNamedConstant()
1115 {
1116 int startPosition = this.position;
1117 while (this.position < this.expression.length())
1118 {
1119 char c = this.expression.charAt(this.position);
1120 if (Character.isLetterOrDigit(c) || '.' == c || '_' == c || '@' == c || '#' == c)
1121 {
1122 this.position++;
1123 }
1124 else
1125 {
1126 break;
1127 }
1128 }
1129 String name = this.expression.substring(startPosition, this.position);
1130 eatSpace();
1131 if (this.position >= this.expression.length() || this.expression.charAt(this.position) != '(')
1132 {
1133
1134 Object result = null == this.retrieveValue ? null : this.retrieveValue.lookup(name);
1135 if (null == result)
1136 {
1137 throwException("Cannot resolve variable " + name);
1138 }
1139
1140 return result;
1141 }
1142
1143
1144 this.position++;
1145 eatSpace();
1146 int argCount = 0;
1147 while (this.position < this.expression.length() && ')' != this.expression.charAt(this.position))
1148 {
1149 evalLhs(0);
1150 argCount++;
1151 eatSpace();
1152 if (this.position < this.expression.length() && ',' == this.expression.charAt(this.position))
1153 {
1154 this.position++;
1155 }
1156 }
1157 if (this.position >= this.expression.length() || ')' != this.expression.charAt(this.position))
1158 {
1159 throwException("Missing closing parenthesis");
1160 }
1161 this.position++;
1162 Function f = null;
1163 if (null != this.userDefinedFunctions)
1164 {
1165 f = this.userDefinedFunctions.get(name);
1166 }
1167 if (null == f)
1168 {
1169 f = this.functionData.get(name);
1170 }
1171 if (null == f)
1172 {
1173 throwException("Unknown function " + name);
1174 }
1175 Object[] args = new Object[argCount];
1176 for (int i = 0; i < argCount; i++)
1177 {
1178 args[argCount - i - 1] = pop();
1179 }
1180 if (f.getMetaData() != MetaData.NO_META_DATA)
1181 {
1182
1183 int argsNeeded = f.getMetaData().getObjectDescriptors().length;
1184 if (argsNeeded != argCount)
1185 {
1186 throwException(name + " needs " + argsNeeded + " parameter" + (argsNeeded == 1 ? "" : "s") + " (got " + argCount
1187 + ")");
1188 }
1189 for (int i = 0; i < argCount; i++)
1190 {
1191 if ((args[i] instanceof Boolean) && (!Boolean.class.isAssignableFrom(f.getMetaData().getObjectClass(i))))
1192 {
1193 throwException(name + " does not take " + args[i] + " as parameter " + i);
1194 }
1195 else if ((args[i] instanceof DoubleScalar)
1196 && (DoubleScalar.class.isAssignableFrom(f.getMetaData().getObjectClass(i))))
1197 {
1198 DoubleScalar<?, ?> ds = (DoubleScalar<?, ?>) args[i];
1199 Class<?> clazz = f.getMetaData().getObjectClass(i);
1200 if (!clazz.equals(DoubleScalarRel.class))
1201 {
1202 SIDimensions siDimensions = this.siDimensionsMap.get(clazz);
1203 if (null == siDimensions)
1204 {
1205
1206 try
1207 {
1208 Field field = clazz.getDeclaredField("ZERO");
1209 DoubleScalar<?, ?> zero = (DoubleScalar<?, ?>) field.get(clazz);
1210 siDimensions = zero.getDisplayUnit().getQuantity().getSiDimensions();
1211 this.siDimensionsMap.put(clazz, siDimensions);
1212 }
1213 catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException nsfe)
1214 {
1215 throwException("ERROR: Cannot determine quantity for " + clazz.getCanonicalName());
1216 }
1217 }
1218 if (!siDimensions.equals(getDimensions(ds)))
1219 {
1220 throwException("parameter " + i + " of " + name + " has incompatible quantity");
1221 }
1222 }
1223 }
1224 else
1225 {
1226 throwException("Argument " + i + " of function " + name + " is of an unhandled type (" + args[i] + ")");
1227 }
1228 }
1229 }
1230
1231 return (f.function(args));
1232 }
1233
1234
1235
1236
1237
1238 private void push(final Object object)
1239 {
1240 this.stack.add(object);
1241 }
1242
1243
1244
1245
1246
1247
1248 private Object pop() throws RuntimeException
1249 {
1250 if (this.stack.isEmpty())
1251 {
1252 throwException("Stack empty");
1253 }
1254 return (this.stack.remove(this.stack.size() - 1));
1255 }
1256
1257
1258
1259
1260
1261
1262
1263 private Dimensionless checkDimensionless(final Function functionData, final Object object)
1264 {
1265 if (!(object instanceof DoubleScalar))
1266 {
1267 throwException("Function " + functionData.getId() + " cannot be applied to " + object);
1268 }
1269 DoubleScalar<?, ?> ds = (DoubleScalar<?, ?>) object;
1270 if (!getDimensions(ds).equals(getDimensions(DimensionlessUnit.SI)))
1271 {
1272 throwException("Function " + functionData.getId() + " cannot be applied to " + ds);
1273 }
1274 return new Dimensionless(ds.si, DimensionlessUnit.SI);
1275 }
1276
1277
1278
1279
1280
1281
1282 private static SIDimensions getDimensions(final DoubleScalar<?, ?> doubleScalar)
1283 {
1284 return doubleScalar.getDisplayUnit().getQuantity().getSiDimensions();
1285 }
1286
1287
1288
1289
1290
1291
1292 private static SIDimensions getDimensions(final Unit<?> unit)
1293 {
1294 return unit.getQuantity().getSiDimensions();
1295 }
1296
1297 }