package parser; import java.util.Map; import valueTypes.DecimalValue; import valueTypes.ErrorValue; import valueTypes.StringValue; import variables.Variable; /** * The class whose purpose is to scan for tokens in the expression string. This * is only meant to be used by the RecursiveDescentParser class. * * @author Curran Kelleher * */ public class TokenScanner { // the current index in the expression string being parsed public int currentCharIndex = 0; // the current expression String being parsed public String expressionStr; // all present binary operators // the type is <String symbol,BinaryOperatorCreator operator> Map binaryOperators; // all present unary operators // the type is <String name,BinaryOperatorCreator operator> Map unaryOperators; /** * Constructs a TokenScanner for the specified expression string, and with * references to the available operators. * * @param binaryOperators * the available binary operators * @param unaryOperators * the available unary operators * @param expression * the expression string */ public TokenScanner(Map binaryOperators, Map unaryOperators, String expression) { this.binaryOperators = binaryOperators; this.unaryOperators = unaryOperators; expressionStr = expression; } /** * Scans for the next token, and returns it's interpreted value. * * @return null if at the end of the string. a Character object if it is a * grouper('(',')','[',']'), absolute value (|x|), function { a = * 5^x }, or factorial (!) a Value for all parsed Values. a Variable * for all parsed Variable names. a BinaryOperatorCreator for all * binary operators a UnaryOperatorCreator for all unary operators * an ErrorValue if there was a syntax error. * */ public Object scan() { if (currentCharIndex >= expressionStr.length()) return null; char c = expressionStr.charAt(currentCharIndex++); while (true) { // check for two-character binary operator if (currentCharIndex < expressionStr.length()) { char d = expressionStr.charAt(currentCharIndex); Object binaryOperator = binaryOperators.get("" + c + d); if (binaryOperator != null) { currentCharIndex++; return binaryOperator; } } // check for single character binary operator Object binaryOperator = binaryOperators.get("" + c); if (binaryOperator != null) return binaryOperator; if (Character.isDigit(c) || c == '.') { boolean decimalFound = false; if (c == '.') decimalFound = true; StringBuffer s = new StringBuffer().append(c); while ((currentCharIndex) < expressionStr.length()) if (Character.isDigit(c = expressionStr .charAt(currentCharIndex++))) s.append(c); else if (c == '.') { if (!decimalFound) decimalFound = true; else return new ErrorValue("malformed decimal: " + s.toString()); ; s.append(c); } else { currentCharIndex--; break; } if (s.toString().equals(".")) return new ErrorValue("there is a dangling decimal point!"); else return new DecimalValue(Double.parseDouble(s.toString())); } else if (Character.isLetter(c)) { StringBuffer s = new StringBuffer().append(c); while (currentCharIndex < expressionStr.length()) { c = expressionStr.charAt(currentCharIndex++); if (Character.isLetterOrDigit(c)||c=='_') { s.append(c); if (currentCharIndex < expressionStr.length()) if (expressionStr.charAt(currentCharIndex) == '(' || expressionStr.charAt(currentCharIndex) == '[')// found // a // unary // operator return getUnaryOperator(s.toString()); } else { if (currentCharIndex < expressionStr.length()) if (c == '(' || c == '[')// found // a one-letter // unary // operator { currentCharIndex--; return getUnaryOperator(s.toString()); } currentCharIndex--; break; } } // if we are here it is a variable name return Variable.getVariable(s.toString()); } switch (c) { case '(': case ')': case '[': case ']': case '{': case '}': case '!': return new Character(c); case ' ': case '\t': if (currentCharIndex < expressionStr.length()) c = expressionStr.charAt(currentCharIndex++); else return new ErrorValue("unfinished statement: " + expressionStr); continue; case '"': StringBuffer b = new StringBuffer(); if (currentCharIndex < expressionStr.length()) { c = expressionStr.charAt(currentCharIndex++); } else { return new ErrorValue("unfinished string literal: " + b.toString()); } while (c != '"') { if (currentCharIndex < expressionStr.length()) { b.append(c); c = expressionStr.charAt(currentCharIndex++); } else return new ErrorValue("unfinished string: " + b.toString()); } return new StringValue(b.toString()); default: return new ErrorValue("invalid symbol " + c); } } } /** * Gets the UnaryOperatorCreator mapped to the specified operator name. * * @param string * the name of the operator (for example "sin") * @return the UnaryOperatorCreator mapped to the name, or an ErrorValue * with a message if no mapping exists. */ private Object getUnaryOperator(String string) { Object unaryOperator = unaryOperators.get(string); if (unaryOperator != null) return unaryOperator; return new ErrorValue(string + " is an invalid unary operator."); } }