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