/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.statcato.calculator;
import java.util.Vector;
import org.statcato.spreadsheet.*;
import org.statcato.utils.HelperFunctions;
/**
* A parser for mathematical expressions using the recurive descent
* algorithm. Acceptable expressions are defined by the following grammar:
*
* E -> T { ("+" | "-") T}
* T -> F { ("*" | "/") F}
* F -> P {"^" F}
* P -> v | "(" E ")" | "-" T | func_name"(" E ")" | Cx | "'" L "'"
* L -> column_label
*
* where expressions enclosed by { } can appear zero or more times.
*
* @author Margaret Yau
* @version %I%, %G%
* @since 1.0
*/
public class MathExpParserComplete {
private TokenScanner scanner;
private final boolean debug = false;
private Spreadsheet spreadsheet;
private boolean isRadian = true;
public MathExpParserComplete(Spreadsheet sp) {
spreadsheet = sp;
}
/**
* Parses the given expression.
*
* @param exp expression in string
* @param isRadian boolean value of whether radians are used
*/
public CompUnit parse(String exp, boolean isRadian) {
scanner = new TokenScanner(exp, spreadsheet);
this.isRadian = isRadian;
print("\nparsing " + exp);
CompUnit result = S();
print("result = " + result);
return result;
}
/**
* Returns error message from the scanner.
*
* @return error string
*/
public String getError() {
return scanner.error;
}
/**
* Parses a statement. S->E
*
* @return the value of a statement
*/
public CompUnit S() {
print("S:");
CompUnit value = E();
if (scanner.getToken() != Token.LAST) {
print("token=" + scanner.getToken());
scanner.error("Expression syntax error: unexpected " +
Token.toString(scanner.getToken()) + " found");
return null;
}
print("last token = " + Token.toString(scanner.getToken()));
print("S = " + value);
return value;
}
/**
* Parses an expression. E -> T { ("+" | "-") T}
*
* @return value of an expression
*/
private CompUnit E() {
print(" E:");
CompUnit left = T();
if (left == null)
return null;
print(" Left = " + left);
CompUnit right;
int op = scanner.getToken();
while (op == Token.ADD || op == Token.SUBTRACT) {
scanner.consumeToken(); // move pass the operator
right = T();
if (right == null)
return null;
print(" op = " + op + ", right = " + right);
switch (op) {
case Token.ADD:
left = MathOperations.add(left, right);
break;
case Token.SUBTRACT:
left = MathOperations.subtract(left, right);
break;
}
op = scanner.getToken();
}
print(" E = " + left);
return left;
}
/**
* Parses a term T -> F { ("*" | "/") F}
*
* @return value of a term
*/
private CompUnit T() {
print(" T:");
CompUnit left = F();
if (left == null)
return null;
print(" left = " + left);
CompUnit right;
int op = scanner.getToken();
while (op == Token.MULTIPLY || op == Token.DIVIDE) {
scanner.consumeToken(); // move pass the operator
right = F();
if (right == null)
return null;
print(" op = " + op + ", right = " + right);
switch (op) {
case Token.MULTIPLY:
left = MathOperations.multiply(left, right);
break;
case Token.DIVIDE:
left = MathOperations.divide(left, right);
break;
}
op = scanner.getToken();
}
print(" T = " + left);
return left;
}
/**
* Parses a powered term F -> P {"^" F}
*
* @return value of a powered term
*/
private CompUnit F() {
print(" F:");
CompUnit left = P();
if (left == null)
return null;
print(" left = " + left);
while (scanner.getToken() == Token.POWER) {
scanner.consumeToken(); // move pass ^
CompUnit right = F();
print(" right = " + right);
if (right == null)
return null;
left = MathOperations.power(left, right);
}
print(" F = " + left);
return left;
}
/**
* Parses a terminal unit P -> v | "(" E ")" | "-" T | func_name"(" E ") | Cx | "'" column label "'"
*
* @return value of a terminal
*/
private CompUnit P() {
print(" P:");
CompUnit unit = null;
double num;
int token = scanner.getToken();
print("token type = " + Token.toString(token));
switch (token) {
case Token.NUMBER:
num = scanner.consumeNumber();
unit = new CompUnit(Token.NUMBER, num);
print(" P is number");
break;
case Token.COLUMN:
int col = scanner.consumeColumn();
// get column
Vector column = spreadsheet.getColumn(col);
Vector value = HelperFunctions.ConvertInputVectorToDoubles(column);
if (value != null) {
value = HelperFunctions.removeEndingNullValues(value);
unit = new CompUnit(Token.COLUMN, value);
print(" P is column");
}
else
scanner.error("invalid column input");
break;
case Token.LEFTPAREN:
scanner.consumeToken();
print(" P is (E)");
unit = E();
if (scanner.getToken() != Token.RIGHTPAREN) {
scanner.error(" Missing )");
}
else {
scanner.consumeToken(); // move pass )
}
break;
case Token.SUBTRACT:
scanner.consumeToken();
print(" P is -T");
unit = MathOperations.negate(T());
break;
case Token.CONSTANT:
num = scanner.consumeConstant();
unit = new CompUnit(Token.NUMBER, num);
print(" P is a constant");
break;
case Token.FUNCTION:
int function = scanner.consumeFunction();
if (scanner.getToken() != Token.LEFTPAREN) {
scanner.error(" function name must be followed by (");
}
else {
scanner.consumeToken(); // move pass (
CompUnit argument = E(); // function argument
if (argument == null)
break;
print(" P is f(E)");
if (scanner.getToken() != Token.RIGHTPAREN) {
scanner.error(" missing ) after function argument");
}
else {
// perform function call
switch (function) {
case MathFunctions.SIN:
print("sin of " + argument);
unit = MathOperations.functionCall(argument,
new MathFunction(MathFunctions.SIN,
isRadian));
break;
case MathFunctions.COS:
print("cos of " + argument);
unit = MathOperations.functionCall(argument,
new MathFunction(MathFunctions.COS,
isRadian));
break;
case MathFunctions.EXP:
print("exp of " + argument);
unit = MathOperations.functionCall(argument,
new MathFunction(MathFunctions.EXP,
isRadian));
break;
case MathFunctions.ABS:
print("abs of " + argument);
unit = MathOperations.functionCall(argument,
new MathFunction(MathFunctions.ABS,
isRadian));
break;
case MathFunctions.TAN:
print("tan of " + argument);
unit = MathOperations.functionCall(argument,
new MathFunction(MathFunctions.TAN,
isRadian));
break;
case MathFunctions.ACOS:
print("acos of " + argument);
unit = MathOperations.functionCall(argument,
new MathFunction(MathFunctions.ACOS,
isRadian));
break;
case MathFunctions.ASIN:
print("asin of " + argument);
unit = MathOperations.functionCall(argument,
new MathFunction(MathFunctions.ASIN,
isRadian));
break;
case MathFunctions.ATAN:
print("atan of " + argument);
unit = MathOperations.functionCall(argument,
new MathFunction(MathFunctions.ATAN,
isRadian));
break;
case MathFunctions.CEIL:
print("ceil of " + argument);
unit = MathOperations.functionCall(argument,
new MathFunction(MathFunctions.CEIL,
isRadian));
break;
case MathFunctions.FLOOR:
print("floor of " + argument);
unit = MathOperations.functionCall(argument,
new MathFunction(MathFunctions.FLOOR,
isRadian));
break;
case MathFunctions.LOG:
print("log of " + argument);
unit = MathOperations.functionCall(argument,
new MathFunction(MathFunctions.LOG,
isRadian));
break;
case MathFunctions.ROUND:
print("round of " + argument);
unit = MathOperations.functionCall(argument,
new MathFunction(MathFunctions.ROUND,
isRadian));
break;
case MathFunctions.SQRT:
print("sqrt of " + argument);
unit = MathOperations.functionCall(argument,
new MathFunction(MathFunctions.SQRT,
isRadian));
break;
case MathFunctions.LN:
print("ln of " + argument);
unit = MathOperations.functionCall(argument,
new MathFunction(MathFunctions.LN,
isRadian));
break;
case MathFunctions.FACTORIAL:
print("factorial of " + argument);
unit = MathOperations.functionCall(argument,
new MathFunction(MathFunctions.FACTORIAL,
isRadian));
break;
}
scanner.consumeToken(); // move pass )
}
}
break;
default:
break;
}
print(" P = " + unit);
if (unit == null)
scanner.error("Unexpected token");
return unit;
}
/**
* Print a debug message.
*
* @param msg debug string
*/
private void print(String msg) {
if (debug)
System.out.println(msg);
}
}
|
Ads help maintain this website.