/* * Copyright 2008 Reg Whitton * * 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 net.java.dev.eval; import java.math.BigDecimal; final class Tokeniser { static final Character START_NEW_EXPRESSION = new Character('('); private final String string; private int position; private Operator pushedBackOperator = null; Tokeniser(String string) { this.string = string; this.position = 0; } int getPosition() { return this.position; } void setPosition(int position) { this.position = position; } void pushBack(Operator operator) { this.pushedBackOperator = operator; } Operator getOperator(char endOfExpressionChar) { /* Use any pushed back operator. */ if (this.pushedBackOperator != null) { Operator operator = this.pushedBackOperator; this.pushedBackOperator = null; return operator; } /* Skip whitespace */ final int len = this.string.length(); char ch = 0; while (this.position < len && Character.isWhitespace(ch = this.string.charAt(this.position))) { this.position++; } if (this.position == len) { if (endOfExpressionChar == 0) { return Operator.END; } else { throw new RuntimeException("missing " + endOfExpressionChar); } } this.position++; if (ch == endOfExpressionChar) { return Operator.END; } switch (ch) { case '+': { return Operator.ADD; } case '-': { return Operator.SUB; } case '/': { return Operator.DIV; } case '%': { return Operator.REMAINDER; } case '*': { return Operator.MUL; } case '?': { return Operator.TERNARY; } case '>': { if (this.position < len && this.string.charAt(this.position) == '=') { this.position++; return Operator.GE; } return Operator.GT; } case '<': { if (this.position < len) { switch (this.string.charAt(this.position)) { case '=': this.position++; return Operator.LE; case '>': this.position++; return Operator.NE; } } return Operator.LT; } case '=': { if (this.position < len && this.string.charAt(this.position) == '=') { this.position++; return Operator.EQ; } throw new RuntimeException("use == for equality at position " + this.position); } case '!': { if (this.position < len && this.string.charAt(this.position) == '=') { this.position++; return Operator.NE; } throw new RuntimeException("use != or <> for inequality at position " + this.position); } case '&': { if (this.position < len && this.string.charAt(this.position) == '&') { this.position++; return Operator.AND; } throw new RuntimeException("use && for AND at position " + this.position); } case '|': { if (this.position < len && this.string.charAt(this.position) == '|') { this.position++; return Operator.OR; } throw new RuntimeException("use || for OR at position " + this.position); } default: { /* Is this an identifier name for an operator function? */ if (Character.isUnicodeIdentifierStart(ch)) { int start = this.position - 1; while (this.position < len && Character.isUnicodeIdentifierPart(this.string.charAt(this.position))) { this.position++; } String name = this.string.substring(start, this.position); if (name.equals("pow")) { return Operator.POW; } } throw new RuntimeException("operator expected at position " + this.position + " instead of '" + ch + "'"); } } } /** * Called when an operand is expected next. * * @return one of: *