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