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