001: package com.teamkonzept.lib.math;
002:
003: import java.util.*;
004: import java.io.StringReader;
005: import java.io.IOException;
006:
007: public class Eval {
008:
009: public Eval() {
010: }
011:
012: public double eval(String expr)
013: throws MalformedExpressionException, IOException,
014: BadOperandTypeException, UnsupportedOperatorException {
015: /* build token list from input */
016: Vector list = parseExpression(removeWhiteSpaces(expr
017: .toLowerCase()));
018: /* extract operator tokens */
019: Vector operators = extractOperators(list);
020: /* sort tokens in order of their priority
021: * ( higher priorities first )
022: */
023: sortOperators(operators);
024: /* apply operators */
025: return evalTokenList(list, operators);
026: }
027:
028: Vector parseExpression(String expr)
029: throws MalformedExpressionException,
030: UnsupportedOperatorException, IOException {
031: Vector res = new Vector();
032: Object o = null;
033: StringReader in = new StringReader(expr);
034: res = parseExpression(in, res, -1, 0, true, true, 0);
035: return res;
036: }
037:
038: Vector parseExpression(StringReader in, Vector tokens,
039: int lookAhead, int level, boolean newLevel,
040: boolean lastWasBinaryOpOrOpenParen, int position)
041: throws MalformedExpressionException,
042: UnsupportedOperatorException, IOException {
043: boolean lookedAhead = false;
044: int lookedAheadChar = -1;
045: boolean functionFound = false;
046: // read next char if none given
047: int c = lookAhead;
048: if (c < 0) {
049: c = in.read();
050: position++;
051: }
052: // a new Level is started with "("
053: // and might start with "+" or "-"
054: if (newLevel) {
055: if (c == '+') {
056: // ignore it and read next token
057: return parseExpression(in, tokens, -1, level, false,
058: true, position);
059: } else if (c == '-') {
060: // add "0 + "
061: tokens.add(new Double(0.0));
062: tokens.add(new MathBinaryOperator(
063: MathBinaryOperator.MINUS, level, position));
064: // read next token
065: return parseExpression(in, tokens, -1, level, false,
066: true, position);
067: }
068: }
069: // if last token was nota a binary op,
070: // the expression can be so
071: // or now can come any binary operator
072: // or a closing paren ( if there was a matching opening paren)
073: if (!lastWasBinaryOpOrOpenParen) {
074: // end of expression
075: if (c < 0) {
076: // all parens closed?
077: if (level == 0)
078: return tokens;
079: // else
080: throw new MalformedExpressionException(
081: MalformedExpressionException.MISSING_CLOSING_PAREN,
082: position);
083: }
084: // closing paren
085: else if (c == ')') {
086: if (level-- > 0)
087: return parseExpression(in, tokens, -1, level,
088: false, false, position);
089: // else
090: throw new MalformedExpressionException(
091: MalformedExpressionException.MISSING_OPENING_PAREN,
092: position);
093: }
094: // mathematical binary operators
095: else if (c == '+') {
096: tokens.add(new MathBinaryOperator(
097: MathBinaryOperator.PLUS, level, position));
098: return parseExpression(in, tokens, -1, level, false,
099: true, position);
100: } else if (c == '-') {
101: tokens.add(new MathBinaryOperator(
102: MathBinaryOperator.MINUS, level, position));
103: return parseExpression(in, tokens, -1, level, false,
104: true, position);
105: } else if (c == '*') {
106: tokens.add(new MathBinaryOperator(
107: MathBinaryOperator.MULT, level, position));
108: return parseExpression(in, tokens, -1, level, false,
109: true, position);
110: } else if (c == '/') {
111: tokens.add(new MathBinaryOperator(
112: MathBinaryOperator.DIV, level, position));
113: return parseExpression(in, tokens, -1, level, false,
114: true, position);
115: } else if (c == '\\') {
116: tokens.add(new MathBinaryOperator(
117: MathBinaryOperator.INT_DIV, level, position));
118: return parseExpression(in, tokens, -1, level, false,
119: true, position);
120: } else if (c == '%') {
121: tokens.add(new MathBinaryOperator(
122: MathBinaryOperator.MODULO, level, position));
123: return parseExpression(in, tokens, -1, level, false,
124: true, position);
125: } else if (c == '^') {
126: tokens.add(new MathBinaryOperator(
127: MathBinaryOperator.POW, level, position));
128: return parseExpression(in, tokens, -1, level, false,
129: true, position);
130: }
131: // compare operators
132: // "==" and "!="
133: else if (c == '=') {
134: // now must come a "="
135: checkNextChar('=', in, ++position);
136: tokens.add(new CompareOperator(CompareOperator.EQUAL,
137: level, position));
138: } else if (c == '!') {
139: // now must come a "="
140: checkNextChar('=', in, ++position);
141: tokens.add(new CompareOperator(
142: CompareOperator.NOT_EQUAL, level, position));
143: }
144: // "<", "<=", ">" and ">="
145: else if (c == '<') {
146: // now can come a "="
147: lookedAheadChar = in.read();
148: position++;
149: if (lookedAheadChar == '=')
150: tokens.add(new CompareOperator(
151: CompareOperator.LESS_OR_EQUAL, level,
152: position));
153: else {
154: tokens.add(new CompareOperator(
155: CompareOperator.LESS, level, position));
156: lookedAhead = true;
157: }
158: } else if (c == '>') {
159: // now can come a "="
160: lookedAheadChar = in.read();
161: position++;
162: if (lookedAheadChar == '=')
163: tokens.add(new CompareOperator(
164: CompareOperator.GREATER_OR_EQUAL, level,
165: position));
166: else {
167: tokens.add(new CompareOperator(
168: CompareOperator.GREATER, level, position));
169: lookedAhead = true;
170: }
171: }
172: // binary logical operators
173: // "||" and "&&"
174: else if (c == '|') {
175: // now must come the same
176: checkNextChar((char) c, in, ++position);
177: tokens.add(new BinaryLogicalOperator(
178: BinaryLogicalOperator.OR, level, position));
179: } else if (c == '&') {
180: // now must come the same
181: checkNextChar((char) c, in, ++position);
182: tokens.add(new BinaryLogicalOperator(
183: BinaryLogicalOperator.AND, level, position));
184: }
185: // everything else is wrong
186: else
187: throw new MalformedExpressionException(
188: MalformedExpressionException.SYNTAX_ERROR,
189: position);
190: // get next token, can start with - or +
191: if (lookedAhead)
192: return parseExpression(in, tokens, lookedAheadChar,
193: level, true, true, position);
194: else
195: return parseExpression(in, tokens, -1, level, true,
196: true, position);
197: } else { // !lastWasBinaryOpOrOpenParen
198: // if last operator was a binary one
199: // or now can come any unary operator (except -)
200: // a number, a function or a opening paren
201: // parse number
202: StringBuffer sb = new StringBuffer();
203: if (Character.isDigit((char) c) || c == '.') {
204: sb.append((char) c);
205: c = in.read();
206: position++;
207: while (Character.isDigit((char) c) || c == '.') {
208: sb.append((char) c);
209: c = in.read();
210: position++;
211: }
212: Double d = new Double(sb.toString());
213: // test for 2log 3log ..
214: if (c == 'l') {
215: c = in.read();
216: position++;
217: if (c == 'o') {
218: c = in.read();
219: position++;
220: if (c == 'g') {
221: c = in.read();
222: position++;
223: if (c != '(')
224: throw new MalformedExpressionException(
225: MalformedExpressionException.MISSING_OPENING_PAREN,
226: position);
227: else {
228: tokens.add(new MathFunction(
229: MathFunction.LOG, level,
230: position, d));
231: return parseExpression(in, tokens, -1,
232: ++level, true, true, position);
233: }
234: }
235: }
236: // something else than log
237: throw new MalformedExpressionException(
238: MalformedExpressionException.SYNTAX_ERROR,
239: position);
240: }
241: // only a number
242: tokens.add(d);
243: // looked ahead
244: return parseExpression(in, tokens, c, level, false,
245: false, position);
246: }
247: // test for (
248: else if (c == '(') {
249: return parseExpression(in, tokens, -1, ++level, true,
250: true, position);
251: }
252: // !
253: else if (c == '!') {
254: tokens.add(new UnaryLogicalOperator(
255: UnaryLogicalOperator.NOT, level, position));
256: return parseExpression(in, tokens, -1, level, true,
257: true, position);
258: } else { // must be a function
259: functionFound = false;
260: lookedAhead = false;
261: // abs acos asin atan
262: if (c == 'a') {
263: c = in.read();
264: position++;
265: // abs
266: if (c == 'b') {
267: c = in.read();
268: position++;
269: if (c == 's') {
270: tokens.add(new MathFunction(
271: MathFunction.ABS, level, position));
272: functionFound = true;
273: }
274: }
275: // acos
276: else if (c == 'c') {
277: c = in.read();
278: position++;
279: if (c == 'o') {
280: c = in.read();
281: position++;
282: if (c == 's') {
283: tokens.add(new MathFunction(
284: MathFunction.ACOS, level,
285: position));
286: functionFound = true;
287: }
288: }
289: }
290: // asin
291: else if (c == 's') {
292: c = in.read();
293: position++;
294: if (c == 'i') {
295: c = in.read();
296: position++;
297: if (c == 'n') {
298: tokens.add(new MathFunction(
299: MathFunction.ASIN, level,
300: position));
301: functionFound = true;
302: }
303: }
304: }
305: // atan
306: else if (c == 't') {
307: c = in.read();
308: position++;
309: if (c == 'a') {
310: c = in.read();
311: position++;
312: if (c == 'n') {
313: tokens.add(new MathFunction(
314: MathFunction.ATAN, level,
315: position));
316: functionFound = true;
317: }
318: }
319: }
320: }
321: // ceil cos cosh cotan
322: else if (c == 'c') {
323: c = in.read();
324: position++;
325: // ceil
326: if (c == 'e') {
327: c = in.read();
328: position++;
329: if (c == 'i') {
330: c = in.read();
331: position++;
332: if (c == 'l') {
333: tokens.add(new MathFunction(
334: MathFunction.CEIL, level,
335: position));
336: functionFound = true;
337: }
338: }
339: }
340: // cos cosh
341: else if (c == 'o') {
342: c = in.read();
343: position++;
344: // cos cosh
345: if (c == 's') {
346: c = in.read();
347: position++;
348: // cosh
349: if (c == 'h') {
350: tokens.add(new MathFunction(
351: MathFunction.COSH, level,
352: position));
353: functionFound = true;
354: }
355: // cos
356: else {
357: tokens.add(new MathFunction(
358: MathFunction.COS, level,
359: position));
360: functionFound = true;
361: lookedAhead = true;
362: }
363: }
364: // cotan
365: else if (c == 't') {
366: c = in.read();
367: position++;
368: if (c == 'a') {
369: c = in.read();
370: position++;
371: if (c == 'n') {
372: tokens.add(new MathFunction(
373: MathFunction.COTAN, level,
374: position));
375: functionFound = true;
376: }
377: }
378: }
379: }
380: }
381: // exp
382: else if (c == 'e') {
383: c = in.read();
384: position++;
385: if (c == 'x') {
386: c = in.read();
387: position++;
388: if (c == 'p') {
389: tokens.add(new MathFunction(
390: MathFunction.EXP, level, position));
391: functionFound = true;
392: }
393: }
394: }
395: // fac floor fpart
396: else if (c == 'f') {
397: c = in.read();
398: position++;
399: // fac
400: if (c == 'a') {
401: c = in.read();
402: position++;
403: if (c == 'c') {
404: tokens.add(new MathFunction(
405: MathFunction.FAC, level, position));
406: functionFound = true;
407: }
408: }
409: // floor
410: else if (c == 'l') {
411: c = in.read();
412: position++;
413: if (c == 'o') {
414: c = in.read();
415: position++;
416: if (c == 'o') {
417: c = in.read();
418: position++;
419: if (c == 'r') {
420: tokens.add(new MathFunction(
421: MathFunction.FLOOR, level,
422: position));
423: functionFound = true;
424: }
425: }
426: }
427: }
428: // fpart
429: else if (c == 'p') {
430: c = in.read();
431: position++;
432: if (c == 'a') {
433: c = in.read();
434: position++;
435: if (c == 'r') {
436: c = in.read();
437: position++;
438: if (c == 't') {
439: tokens.add(new MathFunction(
440: MathFunction.FPART, level,
441: position));
442: functionFound = true;
443: }
444: }
445: }
446: }
447: }
448: // ln
449: else if (c == 'l') {
450: c = in.read();
451: position++;
452: if (c == 'n') {
453: tokens.add(new MathFunction(MathFunction.LN,
454: level, position));
455: functionFound = true;
456: }
457: }
458: // round
459: else if (c == 'r') {
460: c = in.read();
461: position++;
462: if (c == 'o') {
463: c = in.read();
464: position++;
465: if (c == 'u') {
466: c = in.read();
467: position++;
468: if (c == 'n') {
469: c = in.read();
470: position++;
471: if (c == 'd') {
472: tokens.add(new MathFunction(
473: MathFunction.ROUND, level,
474: position));
475: functionFound = true;
476: }
477: }
478: }
479: }
480: }
481: // sfac sin sinh sqrt
482: else if (c == 's') {
483: c = in.read();
484: position++;
485: // sfac
486: if (c == 'f') {
487: c = in.read();
488: position++;
489: if (c == 'a') {
490: c = in.read();
491: position++;
492: if (c == 'c') {
493: tokens.add(new MathFunction(
494: MathFunction.SFAC, level,
495: position));
496: functionFound = true;
497: }
498: }
499: }
500: // sin sinh
501: else if (c == 'i') {
502: c = in.read();
503: position++;
504: if (c == 'n') {
505: c = in.read();
506: position++;
507: // sinh
508: if (c == 'h') {
509: tokens.add(new MathFunction(
510: MathFunction.SINH, level,
511: position));
512: functionFound = true;
513: }
514: // sin
515: else {
516: tokens.add(new MathFunction(
517: MathFunction.SIN, level,
518: position));
519: functionFound = true;
520: lookedAhead = true;
521: }
522: }
523: }
524: // sqrt
525: else if (c == 'q') {
526: c = in.read();
527: position++;
528: if (c == 'r') {
529: c = in.read();
530: position++;
531: if (c == 't') {
532: tokens.add(new MathFunction(
533: MathFunction.SQRT, level,
534: position));
535: functionFound = true;
536: }
537: }
538: }
539: }
540: // tan tanh
541: else if (c == 't') {
542: c = in.read();
543: position++;
544: if (c == 'a') {
545: c = in.read();
546: position++;
547: if (c == 'n') {
548: c = in.read();
549: position++;
550: // tanh
551: if (c == 'h') {
552: tokens.add(new MathFunction(
553: MathFunction.TANH, level,
554: position));
555: functionFound = true;
556: }
557: // tan
558: else {
559: tokens.add(new MathFunction(
560: MathFunction.TAN, level,
561: position));
562: functionFound = true;
563: lookedAhead = true;
564: }
565: }
566: }
567: }
568: }
569: // test if function was found
570: if (functionFound) {
571: // next must be an open paren
572: if (!lookedAhead)
573: c = in.read();
574: position++;
575: if (c != '(')
576: throw new MalformedExpressionException(
577: MalformedExpressionException.MISSING_OPENING_PAREN,
578: position);
579: // recursive call
580: return parseExpression(in, tokens, -1, ++level, true,
581: true, position);
582: } else { // everything else is an syntax error
583: throw new MalformedExpressionException(
584: MalformedExpressionException.SYNTAX_ERROR,
585: position);
586: }
587: }
588: }
589:
590: /**
591: * extract operators form vector
592: */
593: Vector extractOperators(Vector list) {
594: Vector result = new Vector();
595: int size = list.size();
596: for (int i = 0; i < size; i++) {
597: Object o = list.get(i);
598: if (o instanceof OperatorPriority)
599: result.add(o);
600: }
601: return result;
602: }
603:
604: /* sort tokens in order of their priority
605: * ( higher priorities first )
606: */
607: void sortOperators(Vector list) {
608: Collections.sort(list, new Comparator() {
609: public int compare(Object o1, Object o2) {
610: return -1 * ((OperatorPriority) o1).compareTo(o2);
611: }
612:
613: public boolean equals(Object obj) {
614: return false;
615: }
616: });
617: }
618:
619: /**
620: * evaluate the operators in sorted
621: */
622: double evalTokenList(Vector list, Vector sorted)
623: throws BadOperandTypeException,
624: MalformedExpressionException {
625: int size = sorted.size();
626: for (int i = 0; i < size; i++) {
627: Object o = sorted.get(i);
628: int listpos = list.indexOf(o);
629: int rightpos = listpos + 1;
630: if (o instanceof BinaryOperator) {
631: int leftpos = listpos - 1;
632: BinaryOperator op = (BinaryOperator) o;
633: // get operands
634: Object left = list.get(leftpos);
635: Object right = list.get(rightpos);
636: // evaluate
637: Object res = op.evaluate(left, right);
638: // remove operands and operator, insert result
639: list.setElementAt(res, leftpos);
640: list.removeElementAt(listpos);
641: list.removeElementAt(listpos);
642: } else {
643: UnaryOperator op = (UnaryOperator) o;
644: // get operand
645: Object operand = list.get(rightpos);
646: // evaluate
647: Object res = op.evaluate(operand);
648: // remove operand and operator, insert result
649: list.setElementAt(res, listpos);
650: list.removeElementAt(rightpos);
651: }
652: }
653: // now there is only one element left in list
654: if (list.size() != 1)
655: throw new MalformedExpressionException(
656: MalformedExpressionException.UNKNOWN_RESULT, -1);
657: Object o = list.get(0);
658: if (o instanceof Double)
659: return ((Double) o).doubleValue();
660: else if (o instanceof Boolean) {
661: if (((Boolean) o).booleanValue())
662: return 1.0;
663: else
664: return 0.0;
665: } else
666: throw new MalformedExpressionException(
667: MalformedExpressionException.UNKNOWN_RESULT, -1);
668: }
669:
670: public String removeWhiteSpaces(String s) {
671: StringBuffer sb = new StringBuffer(s);
672: int l = sb.length();
673: for (int i = 0; i < l; i++)
674: if (sb.charAt(i) == ' ') {
675: sb.deleteCharAt(i--);
676: l--;
677: }
678: return sb.toString();
679: }
680:
681: void checkNextChar(char toRead, StringReader in, int position)
682: throws MalformedExpressionException, IOException {
683: int c = in.read();
684: if (c != toRead)
685: throw new MalformedExpressionException(
686: MalformedExpressionException.SYNTAX_ERROR, position);
687: }
688: }
|