Android Open Source - Calculator-for-Android Lexer






From Project

Back to project page Calculator-for-Android.

License

The source code is released under:

Apache License

If you think the Android project Calculator-for-Android listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

//   Copyright 2012 Digipom Inc.
///*from  w  ww.j a v a2  s.  c o m*/
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

package com.digipom.android.library.evaluator.lexer;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

import com.digipom.android.library.evaluator.NumberPrecision;
import com.digipom.android.library.evaluator.exception.ParseException;

public class Lexer {
  private static final char EOF_CHAR = (char) -1;

  private final Map<String, PredefinedFunction> predefinedFunctions = new HashMap<String, PredefinedFunction>();
  private final Map<String, Identifier> previouslyFoundIdentifiers = new HashMap<String, Identifier>();
  private final String input;
  private final NumberPrecision numberPrecision;

  private int currentPosition;
  private int maxPosition;
  private char currentCharacter;
  private Token currentToken;

  public Lexer(String input, NumberPrecision numberPrecision) {
    this.input = input.trim().toLowerCase();
    this.numberPrecision = numberPrecision;

    for (PredefinedFunction predefinedFunction : PredefinedFunction.values()) {
      predefinedFunctions.put(predefinedFunction.name().toLowerCase(), predefinedFunction);
    }

    currentPosition = 0;
    maxPosition = input.length() - 1;

    peek();
  }

  private void peek() {
    currentCharacter = currentPosition <= maxPosition ? input.charAt(currentPosition) : EOF_CHAR;
  }

  public Token nextToken() throws ParseException {
    if (isEOF()) {
      currentToken = Token.EOF;
    } else if (isWhitespace()) {
      skipWhitespace();
      return nextToken();
    } else if (isDigit()) {
      currentToken = tokenizeNumberLiteral();
    } else if (isPartOfIdentifier()) {
      currentToken = tokenizeIdentifierOrPredefinedFunction();
    } else {
      currentToken = tokenizeSymbol();
    }

    return currentToken;
  }

  private boolean isEOF() {
    return currentCharacter == EOF_CHAR;
  }

  private boolean isWhitespace() {
    return Character.isWhitespace(currentCharacter);
  }

  private void skipWhitespace() {
    do {
      consume();
    } while (isWhitespace());
  }

  private void consume() {
    currentPosition++;
    peek();
  }

  private boolean isDigit() {
    return Character.isDigit(currentCharacter);
  }

  private boolean isPartOfNumber() {
    return Character.isDigit(currentCharacter) || currentCharacter == '.';
  }

  private Token tokenizeNumberLiteral() throws ParseException {
    int startPosition = currentPosition;
    boolean isInScientificNotation = false;

    do {
      consume();

      if (currentCharacter == 'E' || currentCharacter == 'e') {
        isInScientificNotation = true;
        consume();

        if (currentCharacter == '+' || currentCharacter == '-') {
          consume();
        } else {
          throw new ParseException("Invalid scientific number around "
              + input.substring(startPosition, currentCharacter) + ": expected '+' or '-'");
        }

        while (isDigit()) {
          consume();
        }
      }
    } while (isPartOfNumber());

    switch (numberPrecision) {
      case BIG_DECIMAL:
        return new BigDecimalNumberLiteral(new BigDecimal(input.substring(startPosition, currentPosition)));
      case DOUBLE:
        if (isInScientificNotation) {
          return new DoubleNumberLiteral(
              new BigDecimal(input.substring(startPosition, currentPosition)).doubleValue());
        } else {
          return new DoubleNumberLiteral(Double.parseDouble(input.substring(startPosition, currentPosition)));
        }
      default:
      case FLOAT:
        if (isInScientificNotation) {
          return new FloatNumberLiteral(
              new BigDecimal(input.substring(startPosition, currentPosition)).floatValue());
        } else {
          return new FloatNumberLiteral(Float.parseFloat(input.substring(startPosition, currentPosition)));
        }
    }
  }

  private boolean isPartOfIdentifier() {
    return Character.isLetter(currentCharacter);
  }

  private Token tokenizeIdentifierOrPredefinedFunction() {
    int startPosition = currentPosition;

    do {
      consume();
    } while (isPartOfIdentifier());

    final String value = input.substring(startPosition, currentPosition);

    final PredefinedFunction predefinedFunction = predefinedFunctions.get(value);

    if (predefinedFunction != null) {
      return predefinedFunction;
    } else {
      final Identifier identifier = previouslyFoundIdentifiers.get(value);

      if (identifier != null) {
        return identifier;
      } else {
        final Identifier newIdentifier = new Identifier(value);
        previouslyFoundIdentifiers.put(value, newIdentifier);
        return newIdentifier;
      }

    }
  }

  private Token tokenizeSymbol() throws ParseException {
    final char symbol = currentCharacter;
    consume();

    if (symbol == '+') {
      return Operator.ADD;
    } else if (symbol == '-') {
      if (currentToken instanceof NumberLiteral || currentToken instanceof Identifier
          || currentToken instanceof PredefinedFunction) {
        return Operator.SUBTRACT;
      } else {
        return Operator.NEGATION;
      }
    } else if (symbol == '*') {
      return Operator.MULTIPLY;
    } else if (symbol == '/') {
      return Operator.DIVIDE;
    } else if (symbol == '^') {
      return Operator.POWER;
    } else if (symbol == '(') {
      return Parenthesis.OPEN;
    } else if (symbol == ')') {
      return Parenthesis.CLOSE;
    } else if (symbol == ',') {
      return Separator.COMMA;
    } else {
      throw new ParseException("tokenizeSymbol(): Could not recognize symbol " + symbol);
    }
  }
}




Java Source Code List

com.digipom.android.library.evaluator.BigDecimalPostfixEvaluator.java
com.digipom.android.library.evaluator.DoublePostfixEvaluator.java
com.digipom.android.library.evaluator.FloatPostfixEvaluator.java
com.digipom.android.library.evaluator.NumberPrecision.java
com.digipom.android.library.evaluator.PostfixEvaluator.java
com.digipom.android.library.evaluator.ShuntingYardParser.java
com.digipom.android.library.evaluator.TestBigDecimalEvaluator.java
com.digipom.android.library.evaluator.TestDoubleEvaluator.java
com.digipom.android.library.evaluator.TestFloatEvaluator.java
com.digipom.android.library.evaluator.TestShuntingYardParser.java
com.digipom.android.library.evaluator.builder.ExpressionBuilder.java
com.digipom.android.library.evaluator.builder.StringNumberLiteral.java
com.digipom.android.library.evaluator.exception.ParseException.java
com.digipom.android.library.evaluator.lexer.BigDecimalNumberLiteral.java
com.digipom.android.library.evaluator.lexer.DoubleNumberLiteral.java
com.digipom.android.library.evaluator.lexer.FloatNumberLiteral.java
com.digipom.android.library.evaluator.lexer.Identifier.java
com.digipom.android.library.evaluator.lexer.Lexer.java
com.digipom.android.library.evaluator.lexer.NumberLiteral.java
com.digipom.android.library.evaluator.lexer.Operator.java
com.digipom.android.library.evaluator.lexer.Parenthesis.java
com.digipom.android.library.evaluator.lexer.PredefinedFunction.java
com.digipom.android.library.evaluator.lexer.Separator.java
com.digipom.android.library.evaluator.lexer.TestLexer.java
com.digipom.android.library.evaluator.lexer.Token.java
com.digipom.android.library.util.ObjectUtils.java
com.digipom.android.library.util.TestObjectUtils.java
com.digipom.calculator.config.LoggerConfig.java
com.digipom.calculator.logic.Calculator.java
com.digipom.calculator.logic.Memory.java
com.digipom.calculator.ui.CalculatorActivity.java