X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=core%2Fsrc%2Fde%2Fcongrace%2Fexp4j%2FTokenizer.java;fp=core%2Fsrc%2Fde%2Fcongrace%2Fexp4j%2FTokenizer.java;h=c2c99d974d233d6bb63c21568dd6404b3d8062f0;hb=9349577cdfdff682b2aabd6daa24fdc3a7449b58;hp=0000000000000000000000000000000000000000;hpb=30ba0a882f0c061176ba14dbf86d3d6fad096c02;p=debian%2Fopenrocket diff --git a/core/src/de/congrace/exp4j/Tokenizer.java b/core/src/de/congrace/exp4j/Tokenizer.java new file mode 100644 index 00000000..c2c99d97 --- /dev/null +++ b/core/src/de/congrace/exp4j/Tokenizer.java @@ -0,0 +1,223 @@ +/* + Copyright 2011 frank asseg + + 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 de.congrace.exp4j; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import de.congrace.exp4j.FunctionToken.Function; + +/** + * Class for tokenizing mathematical expressions by breaking an expression up + * into multiple different {@link Token}s + * + * @author fas@congrace.de + */ +class Tokenizer { + private String[] variableNames; + private final Set functionNames = new HashSet(); + private final Set customFunctions; + + { + functionNames.add("abs"); + functionNames.add("acos"); + functionNames.add("asin"); + functionNames.add("atan"); + functionNames.add("cbrt"); + functionNames.add("ceil"); + functionNames.add("cos"); + functionNames.add("cosh"); + functionNames.add("exp"); + functionNames.add("expm1"); + functionNames.add("floor"); + functionNames.add("log"); + functionNames.add("sin"); + functionNames.add("sinh"); + functionNames.add("sqrt"); + functionNames.add("tan"); + functionNames.add("tanh"); + } + + Tokenizer() { + super(); + customFunctions = null; + } + + /** + * construct a new Tokenizer that recognizes variable names + * + * @param variableNames + * the variable names in the expression + * @throws IllegalArgumentException + * if a variable has the name as a function + * @param customFunctions + * the CustomFunction implementations used if the variableNames + * are not valid + */ + Tokenizer(String[] variableNames, Set customFunctions) throws IllegalArgumentException { + super(); + this.variableNames = variableNames; + + if (variableNames != null) { + for (String varName : variableNames) { + if (functionNames.contains(varName.toLowerCase())) { + throw new IllegalArgumentException("Variable '" + varName + "' can not have the same name as a function"); + } + } + } + this.customFunctions = customFunctions; + } + + private Token getCustomFunctionToken(String name) throws UnknownFunctionException { + for (CustomFunction func : customFunctions) { + if (func.getValue().equals(name)) { + return func; + } + } + throw new UnknownFunctionException(name); + } + + private boolean isCustomFunction(String name) { + if (customFunctions == null) { + return false; + } + for (CustomFunction func : customFunctions) { + if (func.getValue().equals(name)) { + return true; + } + } + return false; + } + + /** + * check if a char is part of a number + * + * @param c + * the char to be checked + * @return true if the char is part of a number + */ + private boolean isDigit(char c) { + return Character.isDigit(c) || c == '.'; + } + + private boolean isFunction(String name) { + for (Function fn : Function.values()) { + if (fn.name().equals(name.toUpperCase())) { + return true; + } + } + return false; + } + + /** + * check if a String is a variable name + * + * @param name + * the variable name which is checked to be valid the char to be + * checked + * @return true if the char is a variable name (e.g. x) + */ + private boolean isVariable(String name) { + //String[] variableNames = variables.getVariableNames(); + + if (variableNames != null) { + for (String var : variableNames) { + if (name.equals(var)) { + return true; + } + } + } + return false; + } + + /** + * tokenize an infix expression by breaking it up into different + * {@link Token} that can represent operations,functions,numbers, + * parenthesis or variables + * + * @param infix + * the infix expression to be tokenized + * @return the {@link Token}s representing the expression + * @throws UnparsableExpressionException + * when the expression is invalid + * @throws UnknownFunctionException + * when an unknown function name has been used. + */ + Token[] tokenize(String infix) throws UnparsableExpressionException, UnknownFunctionException { + final List tokens = new ArrayList(); + final char[] chars = infix.toCharArray(); + // iterate over the chars and fork on different types of input + Token lastToken; + for (int i = 0; i < chars.length; i++) { + char c = chars[i]; + + if (c == ' ') + continue; + if (isDigit(c)) { + final StringBuilder valueBuilder = new StringBuilder(1); + // handle the numbers of the expression + valueBuilder.append(c); + int numberLen = 1; + while (chars.length > i + numberLen && isDigit(chars[i + numberLen])) { + valueBuilder.append(chars[i + numberLen]); + numberLen++; + } + i += numberLen - 1; + lastToken = new NumberToken(valueBuilder.toString()); + } else if (Character.isLetter(c) || c == '_' || c == '$') { + // can be a variable or function + final StringBuilder nameBuilder = new StringBuilder(); + nameBuilder.append(c); + int offset = 1; + while (chars.length > i + offset && (Character.isLetter(chars[i + offset]) || Character.isDigit(chars[i + offset]) || chars[i + offset] == '_' || chars[i + offset] == '$')) { + nameBuilder.append(chars[i + offset++]); + } + String name = nameBuilder.toString(); + if (this.isVariable(name)) { + // a variable + i += offset - 1; + lastToken = new VariableToken(name); + } else if (this.isFunction(name)) { + // might be a function + i += offset - 1; + lastToken = new FunctionToken(name); + } else if (this.isCustomFunction(name)) { + // a custom function + i += offset - 1; + lastToken = getCustomFunctionToken(name); + } else { + // an unknown symbol was encountered + throw new UnparsableExpressionException(c, i); + } + }else if (c == ',') { + // a function separator, hopefully + lastToken=new FunctionSeparatorToken(); + } else if (OperatorToken.isOperator(c)) { + lastToken = new OperatorToken(String.valueOf(c), OperatorToken.getOperation(c)); + } else if (c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}') { + lastToken = new ParenthesisToken(String.valueOf(c)); + } else { + // an unknown symbol was encountered + throw new UnparsableExpressionException(c, i); + } + tokens.add(lastToken); + } + return tokens.toArray(new Token[tokens.size()]); + } +}