Clover Coverage Report
Coverage timestamp: Sa Aug 2 2008 13:56:27 CEST
../../../../../img/srcFileCovDistChart8.png 47% of files have more coverage
253   591   80   13,32
110   388   0,32   19
19     4,21  
1    
 
  MathParser       Line # 31 253 80 78,5% 0.7853403
 
  (92)
 
1    /* $Id: MathParser.java,v 1.1 2008/07/26 07:58:30 m31 Exp $
2    *
3    * This file is part of the project "Hilbert II" - http://www.qedeq.org
4    *
5    * Copyright 2000-2008, Michael Meyling <mime@qedeq.org>.
6    *
7    * "Hilbert II" is free software; you can redistribute
8    * it and/or modify it under the terms of the GNU General Public
9    * License as published by the Free Software Foundation; either
10    * version 2 of the License, or (at your option) any later version.
11    *
12    * This program is distributed in the hope that it will be useful,
13    * but WITHOUT ANY WARRANTY; without even the implied warranty of
14    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15    * GNU General Public License for more details.
16    */
17   
18    package org.qedeq.kernel.bo.parser;
19   
20    import java.util.ArrayList;
21    import java.util.List;
22   
23    import org.qedeq.base.trace.Trace;
24   
25    /**
26    * Parse term or formula data into {@link org.qedeq.kernel.bo.parser.Term}s.
27    *
28    * @version $Revision: 1.1 $
29    * @author Michael Meyling
30    */
 
31    public abstract class MathParser {
32   
33    /** This class. */
34    private static final Class CLASS = MathParser.class;
35   
36    /** Input source to parse. */
37    private final MementoTextInput input;
38   
39    /** List of operators. */
40    private List operators;
41   
42    /**
43    * Constructor.
44    *
45    * @param input Input source to parse.
46    * @param operators Operator list.
47    */
 
48  92 toggle public MathParser(final MementoTextInput input, final List operators) {
49  92 this.input = input;
50  92 this.operators = operators;
51    }
52   
 
53  300002 toggle protected final List getOperators() {
54  300002 return operators;
55    }
56   
57    /**
58    * Reads (maximal possible) Term from input.
59    *
60    * @return Read term.
61    * @throws ParserException Parsing failed.
62    */
 
63  182 toggle public final Term readTerm() throws ParserException {
64  182 final String method = "Term readTerm()";
65  182 Trace.begin(CLASS, this, method);
66  182 try {
67  182 final Term term = readMaximalTerm(0);
68  182 if (eot(getToken())) {
69  98 readToken();
70    }
71  182 return term;
72    } finally {
73  182 Trace.end(CLASS, this, method);
74    }
75    }
76   
77    /**
78    * Reads next "maximal" term from input. The following input doesn't make the the term more
79    * complete. Respects the priority of coming operators by comparing it
80    * to the given value.
81    *
82    * @param priority next formula will be the last, if
83    * connecting operator has higher priority
84    * @return Read term.
85    * @throws ParserException Parsing failed.
86    */
 
87  1671 toggle private final Term readMaximalTerm(final int priority) throws ParserException {
88  1671 final String method = "readMaximalTerm(int)";
89  1671 Trace.begin(CLASS, this, method);
90  1671 Term term = null;
91  1671 try {
92  1671 if (eot(getToken())) {
93  7 Trace.param(CLASS, this, method, "return term", "null");
94  7 return null;
95    }
96  1664 term = readPrefixTerm();
97  1664 term = addNextInfixTerms(priority, term);
98  1664 Trace.param(CLASS, this, method,
99  1664 "return term", (term != null ? term.getQedeq() : "null"));
100  1664 return term;
101    } finally {
102  1671 Trace.end(CLASS, this, method);
103    }
104    }
105   
 
106  1664 toggle private Term addNextInfixTerms(final int priority, final Term initialTerm)
107    throws ParserException {
108  1664 final String method = "Term addNextInfixTerms(int, Term)";
109  1664 Trace.begin(CLASS, this, method);
110  1664 Term term = initialTerm;
111  1664 try {
112  1664 Operator newOperator = null;
113  1664 Operator oldOperator = null;
114   
115  1664 do {
116  2277 markPosition();
117  2277 newOperator = readOperator(); // we expect an unique infix operator
118  2277 Trace.param(CLASS, this, method,
119  2277 "newOperator", (newOperator != null ? newOperator.getQedeq() : "null"));
120  2277 if (newOperator == null || newOperator.getPriority() <= priority) {
121  1608 Trace.trace(CLASS, this, method, "newOperator is null or of less priority");
122  1608 rewindPosition();
123  1608 Trace.param(CLASS, this, method,
124  1608 "read term", (term != null ? term.getQedeq() : "null"));
125  1608 return term;
126    }
127  669 if (newOperator.isPrefix()) {
128  56 Trace.trace(CLASS, this, method, "newOperator is prefix");
129    // TODO mime 20060313: try to read further arguments
130  56 rewindPosition();
131  56 Trace.param(CLASS, this, method,
132  56 "read term", (term != null ? term.getQedeq() : "null"));
133  56 return term;
134    }
135  613 if (newOperator.isPostfix()) {
136  0 rewindPosition();
137  0 throw new IllegalArgumentException("Postfix Operators not yet supported");
138    }
139  613 clearMark();
140  613 if (oldOperator == null || oldOperator.getPriority() >= newOperator.getPriority()) {
141  613 Trace.trace(CLASS, this, method,
142    "oldOperator is null or has more priority than new");
143  613 Term term2 = readMaximalTerm(newOperator.getPriority());
144  613 if (term2 == null) {
145    // TODO mime 20060313: 2 could be false if newOperator == oldOperator
146  0 throw new TooFewArgumentsException(getPosition(), 2);
147    }
148  613 if (oldOperator == newOperator) {
149  28 if (oldOperator.getMax() != -1 && term.size() + 1 >= oldOperator.getMax()) {
150  0 throw new TooMuchArgumentsException(getPosition(), oldOperator,
151    oldOperator.getMax());
152    }
153  28 Trace.trace(CLASS, this, method, "new term is added to old term");
154  28 term.addArgument(term2);
155    } else {
156    // old term is first argument of new operator
157  585 Trace.trace(CLASS, this, method,
158    "old term is first argument of new operator");
159  585 term = new Term(newOperator, term);
160  585 term.addArgument(term2);
161    }
162    } else {
163  0 Trace.trace(CLASS, this,
164    method, "oldOperator is not null or has less priority than new");
165  0 Term term2 = readMaximalTerm(newOperator.getPriority());
166  0 if (term2 == null) {
167    // TODO mime 20060313: 2 could be false if newOperator == oldOperator
168  0 throw new TooFewArgumentsException(getPosition(), 2);
169    }
170  0 term = new Term(newOperator, term);
171  0 term.addArgument(term2);
172    }
173  613 oldOperator = newOperator;
174    } while (true);
175    } finally {
176  1664 Trace.end(CLASS, this, method);
177    }
178    }
179   
180    /**
181    * Read next following term. This is a complete term but some infix operator
182    * or some terms for an infix operator might follow.
183    *
184    * @return Read term.
185    * @throws ParserException Parsing failed.
186    */
 
187  1664 toggle private final Term readPrefixTerm() throws ParserException {
188  1664 final String method = "readPrefixTerm()";
189  1664 Trace.begin(CLASS, this, method);
190  1664 Term term = null;
191  1664 try {
192  1664 final List readOperators = readOperators(); // there might be several prefix operators
193  1664 if (readOperators != null && readOperators.size() > 0) {
194  394 Trace.trace(CLASS, this, method, "operators found");
195  394 term = readPrefixOperator(readOperators);
196    } else { // no operator found
197  1270 Trace.trace(CLASS, this, method, "no operators found");
198  1270 final String token;
199  1270 token = getToken();
200  1270 if (token == null) {
201  0 Trace.param(CLASS, this, method, "read term", "null");
202  0 return null;
203    }
204  1270 if ("(".equals(token)) {
205  372 readToken();
206  372 Trace.trace(CLASS, this, method, "start bracket found: " + token);
207  372 term = readMaximalTerm(0);
208  372 final String lastToken = readToken();
209  372 if (!")".equals(lastToken)) {
210  0 throw new ClosingBracketMissingException(getPosition(), "(", lastToken);
211    }
212   
213  898 } else if ("[".equals(token)) {
214  0 readToken();
215  0 Trace.trace(CLASS, this, method, "start bracket found: " + token);
216  0 term = readMaximalTerm(0);
217  0 final String lastToken = readToken();
218  0 if (!"]".equals(lastToken)) {
219  0 throw new ClosingBracketMissingException(getPosition(), "[", lastToken);
220    }
221    } else {
222  898 readToken();
223  898 Trace.param(CLASS, this, method, "atom", token);
224  898 term = new Term(new TermAtom(token));
225    }
226    }
227  1664 Trace.param(CLASS, this, method,
228  1664 "read term", (term != null ? term.getQedeq() : "null"));
229  1664 return term;
230    } finally {
231  1664 Trace.end(CLASS, this, method);
232    }
233    }
234   
235    /**
236    * Try to parse an prefix operator and its operands from the input. Tries first operator,
237    * second operator and so on. If the last one fails an appropriate exception is thrown.
238    *
239    * @param operators Prefix operator list.
240    * @return Resulting term.
241    * @throws ParserException Parsing failed.
242    */
 
243  394 toggle private Term readPrefixOperator(final List operators) throws ParserException {
244  394 Term term = null;
245  394 markPosition();
246  436 for (int number = 0; number < operators.size(); number++) {
247  436 rewindPosition();
248  436 markPosition();
249  436 Operator operator = (Operator) operators.get(number);
250   
251  436 if (!operator.isPrefix()) {
252  0 clearMark();
253  0 throw new UnexpectedOperatorException(getPosition(), operator);
254    }
255   
256  436 term = new Term(operator);
257   
258  436 if (operator.isFunction()) {
259    // constants should have no argument list
260  80 if (operator.getMax() == 0) {
261  48 break;
262    }
263  32 List list = readTupel();
264  32 if (list == null) { // here we don't distinguish between "a()" and "a"
265  30 list = new ArrayList();
266    }
267    // doesn't have enough arguments
268  32 if (list.size() < operator.getMin()) {
269  0 if (number + 1 < operators.size()) {
270  0 continue; // try again
271    }
272  0 clearMark();
273  0 throw new TooFewArgumentsException(getPosition(),
274    operator.getMin());
275    }
276    // has to much arguments
277  32 if (operator.getMax() != -1 && list.size() > operator.getMax()) {
278  0 if (number + 1 < operators.size()) {
279  0 continue; // try again
280    }
281  0 clearMark();
282  0 throw new TooMuchArgumentsException(getPosition(), operator,
283    operator.getMax());
284    }
285  34 for (int i = 0; i < list.size(); i++) {
286  2 term.addArgument((Term) list.get(i));
287    }
288  32 break;
289    }
290   
291  356 int i = 0;
292  798 while (i < operator.getMin()) {
293  442 if (i > 0 && operator.getSeparatorSymbol() != null) {
294  42 final String separator = getToken();
295  42 if (!operator.getSeparatorSymbol().equals(separator)) {
296  0 if (number + 1 < operators.size()) {
297  0 continue;
298    }
299  0 clearMark();
300  0 throw new SeparatorNotFoundException(getPosition(),
301    operator.getSeparatorSymbol());
302    }
303  42 readToken();
304    }
305  442 final Term add = readMaximalTerm(operator.getPriority());
306  442 if (add == null) {
307  0 if (number + 1 < operators.size()) {
308  0 continue;
309    }
310  0 clearMark();
311  0 throw new TooFewArgumentsException(getPosition(), operator.getMin());
312    }
313  442 term.addArgument(add);
314  442 i++;
315    }
316  412 while (operator.getMax() == -1 || i < operator.getMax()) {
317  110 if (i > 0 && operator.getSeparatorSymbol() != null) {
318  54 final String separator = getToken();
319  54 if (!operator.getSeparatorSymbol().equals(separator)) {
320  48 break;
321    }
322  6 readToken();
323    }
324  62 if (operator.getEndSymbol() != null) {
325  56 final String end = getToken();
326  56 if (operator.getEndSymbol().equals(end)) {
327  2 break;
328    }
329    }
330  60 Term add = null;
331  60 markPosition();
332  60 try {
333  60 add = readMaximalTerm(operator.getPriority());
334  60 clearMark();
335    } catch (Exception e) {
336  0 rewindPosition();
337    }
338  60 if (add == null) {
339  4 break;
340    }
341  56 term.addArgument(add);
342  56 i++;
343    }
344  356 if (operator.getEndSymbol() != null) {
345  92 final String end = getToken();
346  92 if (!operator.getEndSymbol().equals(end)) {
347  42 if (number + 1 < operators.size()) {
348  42 continue;
349    }
350  0 clearMark();
351  0 throw new EndSymbolNotFoundException(getPosition(),
352    operator.getEndSymbol());
353    }
354  50 readToken();
355    }
356  314 break;
357    }
358  394 clearMark();
359  394 return term;
360    }
361   
362    /**
363    * Read n-tupel. This is a list of terms encapsulated by "(" and ")" and the terms are separated
364    * by ",".
365    *
366    * @return List of terms. <code>null</code> if no bracket followed.
367    * @throws ParserException Parsing failed.
368    */
 
369  32 toggle private final List readTupel() throws ParserException {
370  32 final String method = "List readTupel()";
371  32 Trace.begin(CLASS, this, method);
372  32 try {
373  32 final String firstToken;
374  32 firstToken = getToken();
375  32 if (!"(".equals(firstToken)) {
376  30 Trace.trace(CLASS, this, method, "no start bracket found");
377  30 return null;
378    }
379  2 readToken(); // read "("
380  2 List list = new ArrayList();
381  2 Term term = null;
382  ? while (null != (term = readMaximalTerm(0))) {
383  2 list.add(term);
384  2 final String separatorToken = getToken();
385  2 if (!",".equals(separatorToken)) {
386  2 break;
387    }
388  0 readToken(); // read ","
389    }
390  2 final String lastToken = readToken();
391  2 if (!")".equals(lastToken)) {
392  0 throw new ClosingBracketMissingException(getPosition(), ")", lastToken);
393    }
394  2 return list;
395    } finally {
396  32 Trace.end(CLASS, this, method);
397    }
398    }
399   
400    /**
401    * Read next operator from input.
402    *
403    * @return Found operator, maybe <code>null</code>.
404    */
 
405  2277 toggle private final Operator readOperator() {
406  2277 final String method = "Operator readOperator()";
407  2277 Trace.begin(CLASS, this, method);
408   
409  2277 try {
410  2277 markPosition();
411  2277 final String token;
412  2277 token = readToken();
413  2277 if (token == null) {
414  209 rewindPosition();
415  209 Trace.trace(CLASS, this, method, "no operator found");
416  209 return null;
417    }
418  2068 final Operator operator = getOperator(token);
419  2068 if (operator == null) {
420  1117 rewindPosition();
421  1117 Trace.trace(CLASS, this, method, "no operator found");
422  1117 return null;
423    }
424  951 clearMark();
425  951 Trace.param(CLASS, this, method, "operator", operator.getQedeq());
426  951 return operator;
427    } finally {
428  2277 Trace.end(CLASS, this, method);
429    }
430    }
431   
432    /**
433    * Read next operators from input. Because the token that identifies an operator might be not
434    * unique we could get multiple operators that start with that token. So this method reads
435    * the operator token (if any) and returns a list of operators that start with that token.
436    *
437    * @return Found operators, maybe <code>null</code> if no matching found..
438    */
 
439  1664 toggle private final List readOperators() {
440  1664 final String method = "List readOperators()";
441  1664 Trace.begin(CLASS, this, method);
442   
443  1664 try {
444  1664 markPosition();
445  1664 final String token;
446  1664 token = readToken();
447  1664 if (token == null) {
448  0 rewindPosition();
449  0 Trace.trace(CLASS, this, method, "no operators found");
450  0 return null;
451    }
452  1664 final List ops = getOperators(token);
453  1664 if (ops == null || ops.size() == 0) {
454  1270 rewindPosition();
455  1270 Trace.trace(CLASS, this, method, "no operators found");
456  1270 return null;
457    }
458  394 clearMark();
459  838 for (int i = 0; i < ops.size(); i++) {
460  444 Trace.param(CLASS, this, method, "operator[" + i + "]", ops.get(i));
461    }
462  394 return ops;
463    } finally {
464  1664 Trace.end(CLASS, this, method);
465    }
466    }
467   
468    /**
469    * Get an operator for that token. If there are more than one possibilities the first matching
470    * is returned.
471    *
472    * @param token Get an operator for this token.
473    * @return Operator. Might be <code>null</code>.
474    */
475    protected abstract Operator getOperator(String token);
476   
477    /**
478    * Get operators for that token. If there are more than one possibilities all matching are
479    * returned.
480    *
481    * @param token Get operators for this token.
482    * @return Operators. Might be <code>null</code>.
483    */
484    protected abstract List getOperators(String token);
485   
486    /**
487    * Read next token from input and move reading position.
488    * A token is a recognized character sequence. A token is no
489    * elementary whitespace. Also a dosn't start or end with
490    * elementary whitespace.
491    *
492    * @return Token read, is <code>null</code> if end of data reached.
493    */
494    protected abstract String readToken();
495   
496    /**
497    * Read next token from input but don't move reading position.
498    * A token is a recognised character sequence. A token is no
499    * elementary whitespace. Also a dosn't start or end with
500    * elementary whitespace.
501    *
502    * @return Token read, is <code>null</code> if end of data reached.
503    */
 
504  3401 toggle public final String getToken() {
505  3401 markPosition();
506  3401 final String result = readToken();
507  3401 rewindPosition();
508  3401 return result;
509    }
510   
511    /**
512    * Remember current position.
513    */
 
514  15914 toggle protected final void markPosition() {
515  15914 input.markPosition();
516    }
517   
518    /**
519    * Rewind to previous marked position. Also clears the mark.
520    *
521    * @return Current position before pop.
522    */
 
523  9232 toggle protected final long rewindPosition() {
524  9232 return input.rewindPosition();
525    }
526   
527    /**
528    * Forget last remembered position.
529    */
 
530  6682 toggle protected final void clearMark() {
531  6682 input.clearMark();
532    }
533   
534    /**
535    * Get byte position.
536    *
537    * @return Position.
538    */
 
539  0 toggle private long getPosition() {
540  0 return input.getPosition();
541    }
542   
543    /**
544    * Reads a single character and does not change the reading
545    * position.
546    *
547    * @return character read, if there are no more chars
548    * <code>Character.MAX_VALUE</code> is returned
549    */
 
550  65216 toggle protected final int getChar() {
551  65216 return input.getChar();
552    }
553   
554    /**
555    * Reads a single character and increments the reading position
556    * by one.
557    *
558    * @return character read, if there are no more chars
559    * <code>Character.MAX_VALUE</code> is returned
560    */
 
561  15149 toggle protected final int readChar() {
562  15149 return input.readChar();
563    }
564   
565    /**
566    * Is this an end of term token?
567    *
568    * @param token Check this token.
569    * @return Is this an end of term token.
570    */
571    protected abstract boolean eot(String token);
572   
573    /**
574    * Are there still any characters to read?
575    *
576    * @return Anything left for reading further?
577    */
 
578  22381 toggle public final boolean eof() {
579  22381 return input.eof();
580    }
581   
582    /**
583    * Get rewind stack size.
584    *
585    * @return Rewind stack size.
586    */
 
587  89 toggle public final int getRewindStackSize() {
588  89 return input.getRewindStackSize();
589    }
590   
591    }