/*
* Copyright 2005-2013 Axel Kramer (axelclk@gmail.com)
*
* 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 org.matheclipse.parser.client;
import java.util.ArrayList;
import java.util.List;
import org.matheclipse.parser.client.ast.ASTNode;
import org.matheclipse.parser.client.ast.FunctionNode;
import org.matheclipse.parser.client.ast.IConstantOperators;
import org.matheclipse.parser.client.ast.IParserFactory;
import org.matheclipse.parser.client.ast.NumberNode;
import org.matheclipse.parser.client.ast.SymbolNode;
import org.matheclipse.parser.client.operator.ASTNodeFactory;
import org.matheclipse.parser.client.operator.InfixOperator;
import org.matheclipse.parser.client.operator.Operator;
import org.matheclipse.parser.client.operator.PostfixOperator;
import org.matheclipse.parser.client.operator.PrefixOperator;
/**
* Create an expression of the ASTNode class-hierarchy from a math formulas string representation
*
* See Operator -precedence parser for the idea, how to parse
* the operators depending on their precedence.
*/
public class Parser extends Scanner {
/**
* Use '('...')' as brackets for arguments
*/
private final boolean fRelaxedSyntax;
private final boolean fPackageMode;
private List fNodeList = null;
public final static SymbolNode DERIVATIVE = new SymbolNode("Derivative");
public Parser() {
this(ASTNodeFactory.MMA_STYLE_FACTORY, false, false);
}
/**
*
* @param relaxedSyntax
* if true, use '('...')' as brackets for arguments
* @throws SyntaxError
*/
public Parser(final boolean relaxedSyntax) throws SyntaxError {
this(ASTNodeFactory.MMA_STYLE_FACTORY, relaxedSyntax);
}
public Parser(final boolean relaxedSyntax, boolean packageMode) throws SyntaxError {
this(ASTNodeFactory.MMA_STYLE_FACTORY, relaxedSyntax, packageMode);
}
/**
*
* @param factory
* @param relaxedSyntax
* if true, use '('...')' as brackets for arguments
* @throws SyntaxError
*/
public Parser(IParserFactory factory, final boolean relaxedSyntax) throws SyntaxError {
this(factory, relaxedSyntax, false);
}
public Parser(IParserFactory factory, final boolean relaxedSyntax, boolean packageMode) throws SyntaxError {
super();
this.fRelaxedSyntax = relaxedSyntax;
this.fFactory = factory;
this.fPackageMode = packageMode;
if (this.fPackageMode) {
fNodeList = new ArrayList(256);
}
}
public void setFactory(final IParserFactory factory) {
this.fFactory = factory;
}
public IParserFactory getFactory() {
return fFactory;
}
/**
* construct the arguments for an expression
*
*/
private void getArguments(final FunctionNode function) throws SyntaxError {
do {
function.add(parseOperators(parsePrimary(), 0));
if (fToken != TT_COMMA) {
break;
}
getNextToken();
} while (true);
}
/**
* Determine the current PrefixOperator
*
* @return null if no prefix operator could be determined
*/
private PrefixOperator determinePrefixOperator() {
Operator oper = null;
for (int i = 0; i < fOperList.size(); i++) {
oper = fOperList.get(i);
if (oper instanceof PrefixOperator) {
return (PrefixOperator) oper;
}
}
return null;
}
/**
* Determine the current PostfixOperator
*
* @return null if no postfix operator could be determined
*/
private PostfixOperator determinePostfixOperator() {
Operator oper = null;
for (int i = 0; i < fOperList.size(); i++) {
oper = fOperList.get(i);
if (oper instanceof PostfixOperator) {
return (PostfixOperator) oper;
}
}
return null;
}
/**
* Determine the current BinaryOperator
*
* @return null if no binary operator could be determined
*/
private InfixOperator determineBinaryOperator() {
Operator oper = null;
for (int i = 0; i < fOperList.size(); i++) {
oper = fOperList.get(i);
if (oper instanceof InfixOperator) {
return (InfixOperator) oper;
}
}
return null;
}
private ASTNode parseArguments(ASTNode lhs) {
if (fRelaxedSyntax) {
if (fToken == TT_ARGUMENTS_OPEN) {
lhs = getFunctionArguments(lhs);
} else if (fToken == TT_PRECEDENCE_OPEN) {
lhs = getFunction(lhs);
}
} else {
if (fToken == TT_ARGUMENTS_OPEN) {
lhs = getFunctionArguments(lhs);
}
}
return lhs;
}
private ASTNode parsePrimary() {
if (fToken == TT_OPERATOR) {
if (fOperatorString.equals(".")) {
fCurrentChar = '.';
return getNumber(false);
}
final PrefixOperator prefixOperator = determinePrefixOperator();
if (prefixOperator != null) {
getNextToken();
final ASTNode temp = parseLookaheadOperator(prefixOperator.getPrecedence());
if (prefixOperator.getFunctionName().equals("PreMinus")) {
// special cases for negative numbers
if (temp instanceof NumberNode) {
((NumberNode) temp).toggleSign();
return temp;
}
}
return prefixOperator.createFunction(fFactory, temp);
}
throwSyntaxError("Operator: " + fOperatorString + " is no prefix operator.");
}
return getPart();
}
private ASTNode parseLookaheadOperator(final int min_precedence) {
ASTNode rhs = parsePrimary();
while (true) {
final int lookahead = fToken;
if ((fToken == TT_LIST_OPEN) || (fToken == TT_PRECEDENCE_OPEN) || (fToken == TT_IDENTIFIER) || (fToken == TT_STRING)
|| (fToken == TT_DIGIT)) {
if (fPackageMode && fToken == TT_IDENTIFIER && fLastChar == '\n') {
return rhs;
}
// lazy evaluation of multiplication
InfixOperator timesOperator = (InfixOperator) fFactory.get("Times");
if (timesOperator.getPrecedence() > min_precedence) {
rhs = parseOperators(rhs, timesOperator.getPrecedence());
continue;
} else if ((timesOperator.getPrecedence() == min_precedence)
&& (timesOperator.getGrouping() == InfixOperator.RIGHT_ASSOCIATIVE)) {
rhs = parseOperators(rhs, timesOperator.getPrecedence());
continue;
}
} else {
if (lookahead != TT_OPERATOR) {
break;
}
InfixOperator infixOperator = determineBinaryOperator();
if (infixOperator != null) {
if (infixOperator.getPrecedence() > min_precedence) {
rhs = parseOperators(rhs, infixOperator.getPrecedence());
continue;
} else if ((infixOperator.getPrecedence() == min_precedence)
&& (infixOperator.getGrouping() == InfixOperator.RIGHT_ASSOCIATIVE)) {
rhs = parseOperators(rhs, infixOperator.getPrecedence());
continue;
}
} else {
PostfixOperator postfixOperator = determinePostfixOperator();
if (postfixOperator != null) {
if (postfixOperator.getPrecedence() > min_precedence) {
getNextToken();
rhs = postfixOperator.createFunction(fFactory, rhs);
continue;
}
}
}
}
break;
}
return rhs;
}
/**
* See Operator -precedence parser for the idea, how to
* parse the operators depending on their precedence.
*
* @param lhs
* the already parsed left-hand-side of the operator
* @param min_precedence
* @return
*/
private ASTNode parseOperators(ASTNode lhs, final int min_precedence) {
ASTNode rhs = null;
Operator oper;
InfixOperator infixOperator;
PostfixOperator postfixOperator;
while (true) {
if ((fToken == TT_LIST_OPEN) || (fToken == TT_PRECEDENCE_OPEN) || (fToken == TT_IDENTIFIER) || (fToken == TT_STRING)
|| (fToken == TT_DIGIT)) {
if (fPackageMode && fToken == TT_IDENTIFIER && fLastChar == '\n') {
return lhs;
}
// lazy evaluation of multiplication
oper = fFactory.get("Times");
if (oper.getPrecedence() >= min_precedence) {
rhs = parseLookaheadOperator(oper.getPrecedence());
lhs = fFactory.createFunction(fFactory.createSymbol(oper.getFunctionName()), lhs, rhs);
// lhs = parseArguments(lhs);
continue;
}
} else {
if (fToken != TT_OPERATOR) {
if (fToken == TT_DERIVATIVE) {
getNextToken();
lhs = fFactory.createFunction(DERIVATIVE, lhs);
// lhs = postfixOperator.createFunction(fFactory, lhs);
lhs = parseArguments(lhs);
continue;
}
break;
}
infixOperator = determineBinaryOperator();
if (infixOperator != null) {
if (infixOperator.getPrecedence() >= min_precedence) {
getNextToken();
rhs = parseLookaheadOperator(infixOperator.getPrecedence());
lhs = infixOperator.createFunction(fFactory, lhs, rhs);
// lhs = parseArguments(lhs);
continue;
}
} else {
postfixOperator = determinePostfixOperator();
if (postfixOperator != null) {
if (postfixOperator.getPrecedence() >= min_precedence) {
getNextToken();
lhs = postfixOperator.createFunction(fFactory, lhs);
lhs = parseArguments(lhs);
continue;
}
} else {
throwSyntaxError("Operator: " + fOperatorString + " is no infix or postfix operator.");
}
}
}
break;
}
return lhs;
}
/**
* Parse the given expression String into an ASTNode.
*
* @param expression
* a formula string which should be parsed.
* @return the parsed ASTNode representation of the given formula string
* @throws SyntaxError
*/
public ASTNode parse(final String expression) throws SyntaxError {
initialize(expression);
final ASTNode temp = parseOperators(parsePrimary(), 0);
if (fToken != TT_EOF) {
if (fToken == TT_PRECEDENCE_CLOSE) {
throwSyntaxError("Too many closing ')'; End-of-file not reached.");
}
if (fToken == TT_LIST_CLOSE) {
throwSyntaxError("Too many closing '}'; End-of-file not reached.");
}
if (fToken == TT_ARGUMENTS_CLOSE) {
throwSyntaxError("Too many closing ']'; End-of-file not reached.");
}
throwSyntaxError("End-of-file not reached.");
}
return temp;
}
public List parseList(final String expression) throws SyntaxError {
initialize(expression);
ASTNode temp = parseOperators(parsePrimary(), 0);
fNodeList.add(temp);
while (fToken != TT_EOF) {
if (fToken == TT_PRECEDENCE_CLOSE) {
throwSyntaxError("Too many closing ')'; End-of-file not reached.");
}
if (fToken == TT_LIST_CLOSE) {
throwSyntaxError("Too many closing '}'; End-of-file not reached.");
}
if (fToken == TT_ARGUMENTS_CLOSE) {
throwSyntaxError("Too many closing ']'; End-of-file not reached.");
}
temp = parseOperators(parsePrimary(), 0);
fNodeList.add(temp);
// throwSyntaxError("End-of-file not reached.");
}
return fNodeList;
}
/**
* Method Declaration.
*
* @return
* @see
*/
private ASTNode getNumber(final boolean negative) throws SyntaxError {
ASTNode temp = null;
final Object[] result = getNumberString();
String number = (String) result[0];
final int numFormat = ((Integer) result[1]).intValue();
try {
if (negative) {
number = '-' + number;
}
if (numFormat < 0) {
temp = fFactory.createDouble(number);
} else {
temp = fFactory.createInteger(number, numFormat);
}
} catch (final Throwable e) {
throwSyntaxError("Number format error: " + number, number.length());
}
getNextToken();
// if (fToken == TT_PRECEDENCE_OPEN) {
// return getTimes(temp);
// }
return temp;
}
private int getIntegerNumber() throws SyntaxError {
final Object[] result = getNumberString();
final String number = (String) result[0];
final int numFormat = ((Integer) result[1]).intValue();
int intValue = 0;
try {
intValue = Integer.parseInt(number, numFormat);
} catch (final NumberFormatException e) {
throwSyntaxError("Number format error (not an int type): " + number, number.length());
}
getNextToken();
return intValue;
}
/**
* Read the current identifier from the expression factories table
*
* @return
* @see
*/
private SymbolNode getSymbol() throws SyntaxError {
String identifier = getIdentifier();
if (!fFactory.isValidIdentifier(identifier)) {
throwSyntaxError("Invalid identifier: " + identifier + " detected.");
}
final SymbolNode symbol = fFactory.createSymbol(identifier);
getNextToken();
return symbol;
}
private ASTNode getString() throws SyntaxError {
final StringBuffer ident = getStringBuffer();
getNextToken();
return fFactory.createString(ident);
}
/**
* Get a list {...}
*
*/
private ASTNode getList() throws SyntaxError {
final FunctionNode function = fFactory.createFunction(fFactory.createSymbol(IConstantOperators.List));
getNextToken();
if (fToken == TT_LIST_CLOSE) {
getNextToken();
return function;
}
getArguments(function);
if (fToken == TT_LIST_CLOSE) {
getNextToken();
return function;
}
throwSyntaxError("'}' expected.");
return null;
}
/**
* Get a function f[...][...]
*
*/
FunctionNode getFunction(final ASTNode head) throws SyntaxError {
final FunctionNode function = fFactory.createAST(head);
getNextToken();
if (fRelaxedSyntax) {
if (fToken == TT_PRECEDENCE_CLOSE) {
getNextToken();
if (fToken == TT_PRECEDENCE_OPEN) {
return function;
}
if (fToken == TT_ARGUMENTS_OPEN) {
return getFunctionArguments(function);
}
return function;
}
} else {
if (fToken == TT_ARGUMENTS_CLOSE) {
getNextToken();
if (fToken == TT_ARGUMENTS_OPEN) {
return getFunctionArguments(function);
}
return function;
}
}
getArguments(function);
if (fRelaxedSyntax) {
if (fToken == TT_PRECEDENCE_CLOSE) {
getNextToken();
if (fToken == TT_PRECEDENCE_OPEN) {
return function;
}
if (fToken == TT_ARGUMENTS_OPEN) {
return getFunctionArguments(function);
}
return function;
}
} else {
if (fToken == TT_ARGUMENTS_CLOSE) {
getNextToken();
if (fToken == TT_ARGUMENTS_OPEN) {
return getFunctionArguments(function);
}
return function;
}
}
if (fRelaxedSyntax) {
throwSyntaxError("')' expected.");
} else {
throwSyntaxError("']' expected.");
}
return null;
}
/**
* Get a function f[...][...]
*
*/
FunctionNode getFunctionArguments(final ASTNode head) throws SyntaxError {
final FunctionNode function = fFactory.createAST(head);
getNextToken();
if (fToken == TT_ARGUMENTS_CLOSE) {
getNextToken();
if (fToken == TT_ARGUMENTS_OPEN) {
return getFunctionArguments(function);
}
return function;
}
getArguments(function);
if (fToken == TT_ARGUMENTS_CLOSE) {
getNextToken();
if (fToken == TT_ARGUMENTS_OPEN) {
return getFunctionArguments(function);
}
return function;
}
throwSyntaxError("']' expected.");
return null;
}
private ASTNode getFactor() throws SyntaxError {
ASTNode temp;
if (fToken == TT_IDENTIFIER) {
final SymbolNode symbol = getSymbol();
if (fToken == TT_BLANK) {
// read '_'
getNextToken();
if (fToken == TT_IDENTIFIER) {
final ASTNode check = getSymbol();
temp = fFactory.createPattern(symbol, check);
} else {
temp = fFactory.createPattern(symbol, null);
}
} else if (fToken == TT_BLANK_BLANK) {
// read '__'
getNextToken();
if (fToken == TT_IDENTIFIER) {
final ASTNode check = getSymbol();
temp = fFactory.createPattern2(symbol, check);
} else {
temp = fFactory.createPattern2(symbol, null);
}
} else if (fToken == TT_BLANK_BLANK_BLANK) {
// read '___'
getNextToken();
if (fToken == TT_IDENTIFIER) {
final ASTNode check = getSymbol();
temp = fFactory.createPattern3(symbol, check);
} else {
temp = fFactory.createPattern3(symbol, null);
}
} else if (fToken == TT_BLANK_OPTIONAL) {
// read '_.'
getNextToken();
if (fToken == TT_IDENTIFIER) {
final ASTNode check = getSymbol();
temp = fFactory.createPattern(symbol, check, true);
} else {
temp = fFactory.createPattern(symbol, null, true);
}
} else {
temp = symbol;
}
return parseArguments(temp);
} else if (fToken == TT_BLANK) {
getNextToken();
if (fToken == TT_IDENTIFIER) {
final ASTNode check = getSymbol();
return fFactory.createPattern(null, check);
} else {
return fFactory.createPattern(null, null);
}
} else if (fToken == TT_BLANK_BLANK) {
// read '__'
getNextToken();
if (fToken == TT_IDENTIFIER) {
final ASTNode check = getSymbol();
return fFactory.createPattern2(null, check);
} else {
return fFactory.createPattern2(null, null);
}
} else if (fToken == TT_BLANK_BLANK_BLANK) {
// read '___'
getNextToken();
if (fToken == TT_IDENTIFIER) {
final ASTNode check = getSymbol();
return fFactory.createPattern3(null, check);
} else {
return fFactory.createPattern3(null, null);
}
} else if (fToken == TT_BLANK_OPTIONAL) {
// read '_.'
getNextToken();
if (fToken == TT_IDENTIFIER) {
final ASTNode check = getSymbol();
return fFactory.createPattern(null, check, true);
} else {
return fFactory.createPattern(null, null, true);
}
} else if (fToken == TT_DIGIT) {
return getNumber(false);
} else if (fToken == TT_PRECEDENCE_OPEN) {
getNextToken();
temp = parseOperators(parsePrimary(), 0);
if (fToken != TT_PRECEDENCE_CLOSE) {
throwSyntaxError("\')\' expected.");
}
getNextToken();
if (fToken == TT_PRECEDENCE_OPEN) {
return getTimes(temp);
}
return temp;
} else if (fToken == TT_LIST_OPEN) {
return getList();
} else if (fToken == TT_STRING) {
return getString();
} else if (fToken == TT_PERCENT) {
final FunctionNode out = fFactory.createFunction(fFactory.createSymbol(IConstantOperators.Out));
int countPercent = 1;
getNextToken();
if (fToken == TT_DIGIT) {
countPercent = getIntegerNumber();
out.add(fFactory.createInteger(countPercent));
return out;
}
while (fToken == TT_PERCENT) {
countPercent++;
getNextToken();
}
out.add(fFactory.createInteger(-countPercent));
return out;
} else if (fToken == TT_SLOT) {
getNextToken();
final FunctionNode slot = fFactory.createFunction(fFactory.createSymbol(IConstantOperators.Slot));
if (fToken == TT_DIGIT) {
slot.add(getNumber(false));
} else {
slot.add(fFactory.createInteger(1));
}
return slot;
} else if (fToken == TT_SLOTSEQUENCE) {
getNextToken();
final FunctionNode slotSequencce = fFactory.createFunction(fFactory.createSymbol(IConstantOperators.SlotSequence));
if (fToken == TT_DIGIT) {
slotSequencce.add(getNumber(false));
} else {
slotSequencce.add(fFactory.createInteger(1));
}
return slotSequencce;
}
switch (fToken) {
case TT_PRECEDENCE_CLOSE:
throwSyntaxError("Too much open ) in factor.");
break;
case TT_LIST_CLOSE:
throwSyntaxError("Too much open } in factor.");
break;
case TT_ARGUMENTS_CLOSE:
throwSyntaxError("Too much open ] in factor.");
break;
}
throwSyntaxError("Error in factor at character: '" + fCurrentChar + "' (" + fToken + ")");
return null;
}
private ASTNode getTimes(ASTNode temp) throws SyntaxError {
FunctionNode func = fFactory.createAST(new SymbolNode("Times"));
func.add(temp);
do {
getNextToken();
temp = parseOperators(parsePrimary(), 0);
func.add(temp);
if (fToken != TT_PRECEDENCE_CLOSE) {
throwSyntaxError("\')\' expected.");
}
getNextToken();
} while (fToken == TT_PRECEDENCE_OPEN);
return func;
}
/**
* Get a part [[..]] of an expression {a,b,c}[[2]] → b
*
*/
private ASTNode getPart() throws SyntaxError {
ASTNode temp = getFactor();
// temp = parseArguments(temp);
if (fToken != TT_PARTOPEN) {
return temp;
}
FunctionNode function = null;
do {
if (function == null) {
function = fFactory.createFunction(fFactory.createSymbol(IConstantOperators.Part), temp);
} else {
function = fFactory.createFunction(fFactory.createSymbol(IConstantOperators.Part), function);
}
do {
getNextToken();
if (fToken == TT_PARTCLOSE) {
throwSyntaxError("Statement (i.e. index) expected in [[ ]].");
}
function.add(parseOperators(parsePrimary(), 0));
} while (fToken == TT_COMMA);
if (fToken == TT_ARGUMENTS_CLOSE) {
// scanner-step begin: (instead of getNextToken() call):
if (fInputString.length() > fCurrentPosition) {
if (fInputString.charAt(fCurrentPosition) == ']') {
fCurrentPosition++;
fToken = TT_PARTCLOSE;
}
}
// scanner-step end
}
if (fToken != TT_PARTCLOSE) {
throwSyntaxError("']]' expected.");
}
// }
getNextToken();
} while (fToken == TT_PARTOPEN);
return function;
}
}
Ads help maintain this website.