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