Clover coverage report - QedeqKernelSe Coverage Report
Coverage timestamp: Sa Jan 26 2008 14:11:34 CET
file stats: LOC: 591   Methods: 19
NCLOC: 388   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
MathParser.java 69,1% 82,6% 100% 79,6%
coverage coverage
 1    /* $Id: MathParser.java,v 1.7 2008/01/26 12:39:09 m31 Exp $
 2    *
 3    * This file is part of the project "Hilbert II" - http://www.qedeq.org
 4    *
 5    * Copyright 2000-2007, 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.parser;
 19   
 20    import java.util.ArrayList;
 21    import java.util.List;
 22   
 23    import org.qedeq.kernel.trace.Trace;
 24   
 25    /**
 26    * Parse term or formula data into {@link org.qedeq.kernel.parser.Term}s.
 27    *
 28    * @version $Revision: 1.7 $
 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  97 public MathParser(final MementoTextInput input, final List operators) {
 49  97 this.input = input;
 50  97 this.operators = operators;
 51    }
 52   
 53  360979 protected final List getOperators() {
 54  360979 return operators;
 55    }
 56   
 57    /**
 58    * Reads (maximal possible) Term from input.
 59    *
 60    * @return Read term.
 61    * @throws ParserException Parsing failed.
 62    */
 63  515 public final Term readTerm() throws ParserException {
 64  515 final String method = "Term readTerm()";
 65  515 Trace.begin(CLASS, this, method);
 66  515 try {
 67  515 final Term term = readMaximalTerm(0);
 68  514 if (eot(getToken())) {
 69  108 readToken();
 70    }
 71  514 return term;
 72    } finally {
 73  515 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  2047 private final Term readMaximalTerm(final int priority) throws ParserException {
 88  2047 final String method = "readMaximalTerm(int)";
 89  2047 Trace.begin(CLASS, this, method);
 90  2047 Term term = null;
 91  2047 try {
 92  2047 if (eot(getToken())) {
 93  11 Trace.param(CLASS, this, method, "return term", "null");
 94  11 return null;
 95    }
 96  2036 term = readPrefixTerm();
 97  2033 term = addNextInfixTerms(priority, term);
 98  2033 Trace.param(CLASS, this, method,
 99  2033 "return term", (term != null ? term.getQedeq() : "null"));
 100  2033 return term;
 101    } finally {
 102  2047 Trace.end(CLASS, this, method);
 103    }
 104    }
 105   
 106  2033 private Term addNextInfixTerms(final int priority, final Term initialTerm)
 107    throws ParserException {
 108  2033 final String method = "Term addNextInfixTerms(int, Term)";
 109  2033 Trace.begin(CLASS, this, method);
 110  2033 Term term = initialTerm;
 111  2033 try {
 112  2033 Operator newOperator = null;
 113  2033 Operator oldOperator = null;
 114   
 115  2033 do {
 116  2671 markPosition();
 117  2671 newOperator = readOperator(); // we expect an unique infix operator
 118  2671 Trace.param(CLASS, this, method,
 119  2671 "newOperator", (newOperator != null ? newOperator.getQedeq() : "null"));
 120  2671 if (newOperator == null || newOperator.getPriority() <= priority) {
 121  1912 Trace.trace(CLASS, this, method, "newOperator is null or of less priority");
 122  1912 rewindPosition();
 123  1912 Trace.param(CLASS, this, method,
 124  1912 "read term", (term != null ? term.getQedeq() : "null"));
 125  1912 return term;
 126    }
 127  759 if (newOperator.isPrefix()) {
 128  121 Trace.trace(CLASS, this, method, "newOperator is prefix");
 129    // TODO mime 20060313: try to read further arguments
 130  121 rewindPosition();
 131  121 Trace.param(CLASS, this, method,
 132  121 "read term", (term != null ? term.getQedeq() : "null"));
 133  121 return term;
 134    }
 135  638 if (newOperator.isPostfix()) {
 136  0 rewindPosition();
 137  0 throw new IllegalArgumentException("Postfix Operators not yet supported");
 138    }
 139  638 clearMark();
 140  638 if (oldOperator == null || oldOperator.getPriority() >= newOperator.getPriority()) {
 141  638 Trace.trace(CLASS, this, method,
 142    "oldOperator is null or has more priority than new");
 143  638 Term term2 = readMaximalTerm(newOperator.getPriority());
 144  638 if (term2 == null) {
 145    // TODO mime 20060313: 2 could be false if newOperator == oldOperator
 146  0 throw new TooFewArgumentsException(getPosition(), 2);
 147    }
 148  638 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  610 Trace.trace(CLASS, this, method,
 158    "old term is first argument of new operator");
 159  610 term = new Term(newOperator, term);
 160  610 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  638 oldOperator = newOperator;
 174    } while (true);
 175    } finally {
 176  2033 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  2036 private final Term readPrefixTerm() throws ParserException {
 188  2036 final String method = "readPrefixTerm()";
 189  2036 Trace.begin(CLASS, this, method);
 190  2036 Term term = null;
 191  2036 try {
 192  2036 final List readOperators = readOperators(); // there might be several prefix operators
 193  2036 if (readOperators != null && readOperators.size() > 0) {
 194  417 Trace.trace(CLASS, this, method, "operators found");
 195  417 term = readPrefixOperator(readOperators);
 196    } else { // no operator found
 197  1619 Trace.trace(CLASS, this, method, "no operators found");
 198  1619 final String token;
 199  1619 token = getToken();
 200  1619 if (token == null) {
 201  0 Trace.param(CLASS, this, method, "read term", "null");
 202  0 return null;
 203    }
 204  1619 if ("(".equals(token)) {
 205  384 readToken();
 206  384 Trace.trace(CLASS, this, method, "start bracket found: " + token);
 207  384 term = readMaximalTerm(0);
 208  382 final String lastToken = readToken();
 209  382 if (!")".equals(lastToken)) {
 210  1 throw new ClosingBracketMissingException(getPosition(), "(", lastToken);
 211    }
 212   
 213  1235 } 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  1235 readToken();
 223  1235 Trace.param(CLASS, this, method, "atom", token);
 224  1235 term = new Term(new TermAtom(token));
 225    }
 226    }
 227  2033 Trace.param(CLASS, this, method,
 228  2033 "read term", (term != null ? term.getQedeq() : "null"));
 229  2033 return term;
 230    } finally {
 231  2036 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  417 private Term readPrefixOperator(final List operators) throws ParserException {
 244  417 Term term = null;
 245  417 markPosition();
 246  461 for (int number = 0; number < operators.size(); number++) {
 247  461 rewindPosition();
 248  461 markPosition();
 249  461 Operator operator = (Operator) operators.get(number);
 250   
 251  461 if (!operator.isPrefix()) {
 252  0 clearMark();
 253  0 throw new UnexpectedOperatorException(getPosition(), operator);
 254    }
 255   
 256  461 term = new Term(operator);
 257   
 258  461 if (operator.isFunction()) {
 259    // constants should have no argument list
 260  101 if (operator.getMax() == 0) {
 261  28 break;
 262    }
 263  73 List list = readTupel();
 264  73 if (list == null) { // here we don't distinguish between "a()" and "a"
 265  71 list = new ArrayList();
 266    }
 267    // doesn't have enough arguments
 268  73 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  73 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  73 for (int i = 0; i < list.size(); i++) {
 286  2 term.addArgument((Term) list.get(i));
 287    }
 288  73 break;
 289    }
 290   
 291  360 int i = 0;
 292  360 while (i < operator.getMin()) {
 293  446 if (i > 0 && operator.getSeparatorSymbol() != null) {
 294  44 final String separator = getToken();
 295  44 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  44 readToken();
 304    }
 305  446 final Term add = readMaximalTerm(operator.getPriority());
 306  446 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  446 term.addArgument(add);
 314  446 i++;
 315    }
 316  360 while (operator.getMax() == -1 || i < operator.getMax()) {
 317  114 if (i > 0 && operator.getSeparatorSymbol() != null) {
 318  56 final String separator = getToken();
 319  56 if (!operator.getSeparatorSymbol().equals(separator)) {
 320  50 break;
 321    }
 322  6 readToken();
 323    }
 324  64 if (operator.getEndSymbol() != null) {
 325  58 final String end = getToken();
 326  58 if (operator.getEndSymbol().equals(end)) {
 327  2 break;
 328    }
 329    }
 330  62 Term add = null;
 331  62 markPosition();
 332  62 try {
 333  62 add = readMaximalTerm(operator.getPriority());
 334  62 clearMark();
 335    } catch (Exception e) {
 336  0 rewindPosition();
 337    }
 338  62 if (add == null) {
 339  4 break;
 340    }
 341  58 term.addArgument(add);
 342  58 i++;
 343    }
 344  360 if (operator.getEndSymbol() != null) {
 345  96 final String end = getToken();
 346  96 if (!operator.getEndSymbol().equals(end)) {
 347  44 if (number + 1 < operators.size()) {
 348  44 continue;
 349    }
 350  0 clearMark();
 351  0 throw new EndSymbolNotFoundException(getPosition(),
 352    operator.getEndSymbol());
 353    }
 354  52 readToken();
 355    }
 356  316 break;
 357    }
 358  417 clearMark();
 359  417 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  73 private final List readTupel() throws ParserException {
 370  73 final String method = "List readTupel()";
 371  73 Trace.begin(CLASS, this, method);
 372  73 try {
 373  73 final String firstToken;
 374  73 firstToken = getToken();
 375  73 if (!"(".equals(firstToken)) {
 376  71 Trace.trace(CLASS, this, method, "no start bracket found");
 377  71 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  73 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  2671 private final Operator readOperator() {
 406  2671 final String method = "Operator readOperator()";
 407  2671 Trace.begin(CLASS, this, method);
 408   
 409  2671 try {
 410  2671 markPosition();
 411  2671 final String token;
 412  2671 token = readToken();
 413  2671 if (token == null) {
 414  213 rewindPosition();
 415  213 Trace.trace(CLASS, this, method, "no operator found");
 416  213 return null;
 417    }
 418  2458 final Operator operator = getOperator(token);
 419  2458 if (operator == null) {
 420  1416 rewindPosition();
 421  1416 Trace.trace(CLASS, this, method, "no operator found");
 422  1416 return null;
 423    }
 424  1042 clearMark();
 425  1042 Trace.param(CLASS, this, method, "operator", operator.getQedeq());
 426  1042 return operator;
 427    } finally {
 428  2671 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  2036 private final List readOperators() {
 440  2036 final String method = "List readOperators()";
 441  2036 Trace.begin(CLASS, this, method);
 442   
 443  2036 try {
 444  2036 markPosition();
 445  2036 final String token;
 446  2036 token = readToken();
 447  2036 if (token == null) {
 448  0 rewindPosition();
 449  0 Trace.trace(CLASS, this, method, "no operators found");
 450  0 return null;
 451    }
 452  2036 final List ops = getOperators(token);
 453  2036 if (ops == null || ops.size() == 0) {
 454  1619 rewindPosition();
 455  1619 Trace.trace(CLASS, this, method, "no operators found");
 456  1619 return null;
 457    }
 458  417 clearMark();
 459  417 for (int i = 0; i < ops.size(); i++) {
 460  469 Trace.param(CLASS, this, method, "operator[" + i + "]", ops.get(i));
 461    }
 462  417 return ops;
 463    } finally {
 464  2036 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  4509 public final String getToken() {
 505  4509 markPosition();
 506  4509 final String result = readToken();
 507  4509 rewindPosition();
 508  4509 return result;
 509    }
 510   
 511    /**
 512    * Remember current position.
 513    */
 514  18232 protected final void markPosition() {
 515  18232 input.markPosition();
 516    }
 517   
 518    /**
 519    * Rewind to previous marked position. Also clears the mark.
 520    *
 521    * @return Current position before pop.
 522    */
 523  11386 protected final long rewindPosition() {
 524  11386 return input.rewindPosition();
 525    }
 526   
 527    /**
 528    * Forget last remembered position.
 529    */
 530  6846 protected final void clearMark() {
 531  6846 input.clearMark();
 532    }
 533   
 534    /**
 535    * Get byte position.
 536    *
 537    * @return Position.
 538    */
 539  1 private long getPosition() {
 540  1 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  80548 protected final int getChar() {
 551  80548 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  18717 protected final int readChar() {
 562  18717 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  24708 public final boolean eof() {
 579  24708 return input.eof();
 580    }
 581   
 582    /**
 583    * Get rewind stack size.
 584    *
 585    * @return Rewind stack size.
 586    */
 587  89 public final int getRewindStackSize() {
 588  89 return input.getRewindStackSize();
 589    }
 590   
 591    }