2016-03-07 04:37:01 +00:00
|
|
|
package com.bytezone.diskbrowser.visicalc;
|
|
|
|
|
2016-03-09 10:38:53 +00:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.List;
|
2016-03-07 04:37:01 +00:00
|
|
|
|
2016-03-12 22:38:03 +00:00
|
|
|
class Expression implements Value
|
2016-03-07 04:37:01 +00:00
|
|
|
{
|
|
|
|
// Expressions:
|
2016-03-10 09:21:47 +00:00
|
|
|
// number
|
|
|
|
// cell address
|
|
|
|
// function
|
|
|
|
// expression [+-*/^] expression
|
2016-03-11 21:56:02 +00:00
|
|
|
// [+-] expression
|
2016-03-10 09:21:47 +00:00
|
|
|
// ( expression )
|
2016-03-07 04:37:01 +00:00
|
|
|
|
2016-03-09 10:38:53 +00:00
|
|
|
// From the reference card:
|
|
|
|
// Expressions are evaluated strictly from left to right except as modified by
|
|
|
|
// parentheses. You must start an expression with a +, a digit (0-9), or one of
|
|
|
|
// the symbols @-(. or #.
|
|
|
|
|
2016-03-11 02:54:31 +00:00
|
|
|
// [@IF(@ISERROR(BK24),0,BK24)]
|
|
|
|
// [@IF(D4=0,0,1)]
|
|
|
|
// [@IF(D4=0,0,B32+1)]
|
|
|
|
// [@IF(D4=0,0,1+(D3/100/D4)^D4-1*100)]
|
|
|
|
// [@SUM(C4...F4)]
|
|
|
|
// [+C4-@SUM(C5...C12)]
|
|
|
|
// [+D5/100/12]
|
|
|
|
// [.3*(B4+B7+B8+B9)]
|
|
|
|
// [+N12+(P12*(.2*K12+K9-O12))]
|
|
|
|
|
2016-03-09 10:38:53 +00:00
|
|
|
private final List<Value> values = new ArrayList<Value> ();
|
|
|
|
private final List<String> operators = new ArrayList<String> ();
|
2016-03-11 01:52:22 +00:00
|
|
|
private final List<String> signs = new ArrayList<String> ();
|
2016-03-07 04:37:01 +00:00
|
|
|
|
2016-03-16 19:32:25 +00:00
|
|
|
private ValueType valueType;
|
2016-03-16 06:15:39 +00:00
|
|
|
private double value;
|
2016-03-19 05:31:30 +00:00
|
|
|
private String text;
|
2016-03-11 21:56:02 +00:00
|
|
|
|
2016-03-14 08:58:54 +00:00
|
|
|
public Expression (Sheet parent, String text)
|
2016-03-07 04:37:01 +00:00
|
|
|
{
|
2016-03-19 05:31:30 +00:00
|
|
|
this.text = text;
|
2016-03-14 08:58:54 +00:00
|
|
|
String line = checkBrackets (text);
|
|
|
|
// System.out.printf ("Exp[%s]%n", line);
|
2016-03-11 21:56:02 +00:00
|
|
|
|
2016-03-09 10:38:53 +00:00
|
|
|
int ptr = 0;
|
|
|
|
while (ptr < line.length ())
|
2016-03-07 12:16:11 +00:00
|
|
|
{
|
2016-03-11 21:56:02 +00:00
|
|
|
// check for optional leading + or -
|
2016-03-09 10:38:53 +00:00
|
|
|
char ch = line.charAt (ptr);
|
2016-03-11 01:52:22 +00:00
|
|
|
if (ch == '-')
|
|
|
|
{
|
|
|
|
signs.add ("(-)");
|
|
|
|
ch = line.charAt (++ptr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
signs.add ("(+)");
|
|
|
|
if (ch == '+')
|
|
|
|
ch = line.charAt (++ptr);
|
|
|
|
}
|
|
|
|
|
2016-03-11 21:56:02 +00:00
|
|
|
// check for mandatory function/sub-expression/number/cell reference
|
2016-03-09 10:38:53 +00:00
|
|
|
switch (ch)
|
|
|
|
{
|
|
|
|
case '@': // function
|
|
|
|
String functionText = getFunctionText (line.substring (ptr));
|
|
|
|
ptr += functionText.length ();
|
|
|
|
values.add (Function.getInstance (parent, functionText));
|
|
|
|
break;
|
2016-03-07 04:37:01 +00:00
|
|
|
|
2016-03-09 10:38:53 +00:00
|
|
|
case '(': // parentheses block
|
|
|
|
String bracketText = getFunctionText (line.substring (ptr));
|
|
|
|
ptr += bracketText.length ();
|
2016-03-11 21:56:02 +00:00
|
|
|
values.add (new Expression (parent,
|
|
|
|
bracketText.substring (1, bracketText.length () - 1)));
|
2016-03-09 10:38:53 +00:00
|
|
|
break;
|
2016-03-07 04:37:01 +00:00
|
|
|
|
2016-03-11 01:52:22 +00:00
|
|
|
case '#':
|
|
|
|
System.out.printf ("Hash character [%s] in [%s]%n", ch, line);
|
2016-03-11 21:56:02 +00:00
|
|
|
ptr++; // no idea
|
2016-03-11 01:52:22 +00:00
|
|
|
break;
|
|
|
|
|
2016-03-09 10:38:53 +00:00
|
|
|
default:
|
|
|
|
if (ch == '.' || (ch >= '0' && ch <= '9')) // number
|
|
|
|
{
|
|
|
|
String numberText = getNumberText (line.substring (ptr));
|
|
|
|
ptr += numberText.length ();
|
2016-03-11 21:56:02 +00:00
|
|
|
values.add (new Number (numberText));
|
2016-03-09 10:38:53 +00:00
|
|
|
}
|
|
|
|
else if (ch >= 'A' && ch <= 'Z') // cell address
|
|
|
|
{
|
|
|
|
String addressText = getAddressText (line.substring (ptr));
|
|
|
|
ptr += addressText.length ();
|
2016-03-17 04:40:43 +00:00
|
|
|
Cell cell = parent.getCell (addressText);
|
2016-03-19 05:58:52 +00:00
|
|
|
if (cell == null)
|
2016-03-19 05:54:58 +00:00
|
|
|
values.add (Function.getInstance (parent, "@NA"));
|
2016-03-19 05:58:52 +00:00
|
|
|
else
|
|
|
|
values.add (parent.getCell (addressText));
|
2016-03-09 10:38:53 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-03-11 21:56:02 +00:00
|
|
|
System.out.printf ("Unexpected character [%s] in [%s]%n", ch, line);
|
2016-03-09 10:38:53 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-11 21:56:02 +00:00
|
|
|
// check for optional continuation operator
|
2016-03-09 10:38:53 +00:00
|
|
|
if (ptr < line.length ())
|
|
|
|
{
|
|
|
|
ch = line.charAt (ptr);
|
|
|
|
if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '^')
|
|
|
|
operators.add (line.substring (ptr, ++ptr));
|
|
|
|
else
|
|
|
|
{
|
|
|
|
System.out.printf ("Unknown operator [%s] in [%s]%n", ch, line);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2016-03-07 04:37:01 +00:00
|
|
|
}
|
|
|
|
|
2016-03-09 10:38:53 +00:00
|
|
|
assert values.size () > 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2016-03-17 04:40:43 +00:00
|
|
|
public Value calculate ()
|
2016-03-09 10:38:53 +00:00
|
|
|
{
|
2016-07-21 05:29:08 +00:00
|
|
|
// System.out.printf ("Calculating: %s%n", text);
|
2016-03-19 05:31:30 +00:00
|
|
|
// if (text.equals ("@NA"))
|
|
|
|
// Utility.printStackTrace ();
|
|
|
|
|
2016-03-16 19:32:25 +00:00
|
|
|
try
|
2016-03-07 04:37:01 +00:00
|
|
|
{
|
2016-03-16 19:32:25 +00:00
|
|
|
Value thisValue = values.get (0);
|
|
|
|
thisValue.calculate ();
|
|
|
|
if (thisValue.isError ())
|
|
|
|
{
|
2016-03-17 04:40:43 +00:00
|
|
|
valueType = thisValue.getValueType ();
|
2016-07-21 05:29:08 +00:00
|
|
|
// System.out.println ("error");
|
2016-03-17 04:40:43 +00:00
|
|
|
return this;
|
2016-03-16 19:32:25 +00:00
|
|
|
}
|
2016-03-17 04:40:43 +00:00
|
|
|
value = thisValue.isNotAvailable () ? 0 : thisValue.getValue ();
|
2016-03-11 01:52:22 +00:00
|
|
|
|
2016-03-16 19:32:25 +00:00
|
|
|
String sign = signs.get (0);
|
2016-03-11 01:52:22 +00:00
|
|
|
if (sign.equals ("(-)"))
|
2016-03-16 19:32:25 +00:00
|
|
|
value *= -1;
|
|
|
|
|
|
|
|
for (int i = 1; i < values.size (); i++)
|
|
|
|
{
|
|
|
|
thisValue = values.get (i);
|
|
|
|
thisValue.calculate ();
|
|
|
|
if (thisValue.isError ())
|
|
|
|
{
|
2016-03-17 04:40:43 +00:00
|
|
|
valueType = thisValue.getValueType ();
|
2016-07-21 05:29:08 +00:00
|
|
|
// System.out.println ("error");
|
2016-03-17 04:40:43 +00:00
|
|
|
return this;
|
2016-03-16 19:32:25 +00:00
|
|
|
}
|
2016-03-17 04:40:43 +00:00
|
|
|
|
|
|
|
double nextValue = thisValue.isNotAvailable () ? 0 : thisValue.getValue ();
|
2016-03-16 19:32:25 +00:00
|
|
|
|
|
|
|
sign = signs.get (i);
|
|
|
|
if (sign.equals ("(-)"))
|
|
|
|
nextValue *= -1;
|
|
|
|
|
|
|
|
String operator = operators.get (i - 1);
|
|
|
|
if (operator.equals ("+"))
|
|
|
|
value += nextValue;
|
|
|
|
else if (operator.equals ("-"))
|
|
|
|
value -= nextValue;
|
|
|
|
else if (operator.equals ("*"))
|
|
|
|
value *= nextValue;
|
|
|
|
else if (operator.equals ("/"))
|
|
|
|
value /= nextValue;
|
|
|
|
else if (operator.equals ("^"))
|
|
|
|
value = Math.pow (value, nextValue);
|
|
|
|
}
|
2016-07-21 11:28:22 +00:00
|
|
|
|
|
|
|
if (Double.isNaN (value))
|
|
|
|
valueType = ValueType.NAN;
|
|
|
|
else
|
|
|
|
valueType = ValueType.VALUE;
|
2016-03-16 19:32:25 +00:00
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
valueType = ValueType.ERROR;
|
2016-03-07 04:37:01 +00:00
|
|
|
}
|
2016-03-19 05:31:30 +00:00
|
|
|
|
|
|
|
// System.out.printf ("Result: %f%n", value);
|
2016-03-17 04:40:43 +00:00
|
|
|
return this;
|
2016-03-07 04:37:01 +00:00
|
|
|
}
|
|
|
|
|
2016-03-16 19:32:25 +00:00
|
|
|
@Override
|
|
|
|
public ValueType getValueType ()
|
|
|
|
{
|
|
|
|
return valueType;
|
|
|
|
}
|
|
|
|
|
2016-03-11 21:56:02 +00:00
|
|
|
@Override
|
2016-03-17 04:40:43 +00:00
|
|
|
public boolean isValue ()
|
2016-03-16 06:15:39 +00:00
|
|
|
{
|
2016-03-17 04:40:43 +00:00
|
|
|
return valueType == ValueType.VALUE;
|
2016-03-16 06:15:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2016-03-17 04:40:43 +00:00
|
|
|
public boolean isNotAvailable ()
|
2016-03-11 21:56:02 +00:00
|
|
|
{
|
2016-03-17 04:40:43 +00:00
|
|
|
return valueType == ValueType.NA;
|
2016-03-11 21:56:02 +00:00
|
|
|
}
|
|
|
|
|
2016-07-21 11:28:22 +00:00
|
|
|
@Override
|
|
|
|
public boolean isNotANumber ()
|
|
|
|
{
|
|
|
|
return valueType == ValueType.NAN;
|
|
|
|
}
|
|
|
|
|
2016-03-11 21:56:02 +00:00
|
|
|
@Override
|
2016-03-16 06:15:39 +00:00
|
|
|
public boolean isError ()
|
2016-03-11 21:56:02 +00:00
|
|
|
{
|
2016-03-16 19:32:25 +00:00
|
|
|
return valueType == ValueType.ERROR;
|
2016-03-16 06:15:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public double getValue ()
|
|
|
|
{
|
2016-07-21 11:28:22 +00:00
|
|
|
// assert valueType == ValueType.VALUE : "Expression ValueType = " + valueType;
|
2016-03-16 06:15:39 +00:00
|
|
|
return value;
|
2016-03-11 21:56:02 +00:00
|
|
|
}
|
|
|
|
|
2016-03-17 04:40:43 +00:00
|
|
|
@Override
|
|
|
|
public String getText ()
|
|
|
|
{
|
2016-07-21 11:28:22 +00:00
|
|
|
return isNotAvailable () ? "NA" : isError () ? "Error" : isNotANumber () ? "NaN" : "";
|
|
|
|
// return isNotAvailable () ? "NA" : isError () ? "Error" : "";
|
2016-03-17 04:40:43 +00:00
|
|
|
}
|
|
|
|
|
2016-03-11 21:56:02 +00:00
|
|
|
private String checkBrackets (String input)
|
|
|
|
{
|
|
|
|
String line = input.trim ();
|
|
|
|
|
|
|
|
int leftBracket = 0;
|
|
|
|
int rightBracket = 0;
|
|
|
|
|
|
|
|
for (char c : line.toCharArray ())
|
|
|
|
if (c == '(')
|
|
|
|
leftBracket++;
|
|
|
|
else if (c == ')')
|
|
|
|
rightBracket++;
|
|
|
|
|
|
|
|
if (leftBracket != rightBracket)
|
|
|
|
{
|
2016-03-14 20:09:04 +00:00
|
|
|
if (rightBracket > leftBracket)
|
|
|
|
{
|
|
|
|
System.out.printf ("**** Unbalanced brackets: left:%d, right:%d ****%n",
|
|
|
|
leftBracket, rightBracket);
|
|
|
|
System.out.println (input);
|
2016-03-17 04:40:43 +00:00
|
|
|
return "@ERROR";
|
2016-03-14 20:09:04 +00:00
|
|
|
}
|
|
|
|
// System.out.printf ("Old expression:[%s]%n", line);
|
|
|
|
while (rightBracket < leftBracket)
|
|
|
|
{
|
|
|
|
line = line + ")";
|
|
|
|
rightBracket++;
|
|
|
|
}
|
|
|
|
// System.out.printf ("New expression:[%s]%n", line);
|
2016-03-11 21:56:02 +00:00
|
|
|
}
|
|
|
|
return line;
|
|
|
|
}
|
|
|
|
|
2016-03-07 04:37:01 +00:00
|
|
|
private String getFunctionText (String text)
|
|
|
|
{
|
|
|
|
int ptr = text.indexOf ('('); // find first left parenthesis
|
2016-03-11 01:52:22 +00:00
|
|
|
if (ptr < 0)
|
2016-03-14 08:58:54 +00:00
|
|
|
return text;
|
2016-03-07 04:37:01 +00:00
|
|
|
int depth = 1;
|
2016-03-11 21:56:02 +00:00
|
|
|
|
2016-03-07 04:37:01 +00:00
|
|
|
while (++ptr < text.length ()) // find matching right parenthesis
|
|
|
|
{
|
|
|
|
if (text.charAt (ptr) == ')')
|
|
|
|
{
|
|
|
|
--depth;
|
|
|
|
if (depth == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (text.charAt (ptr) == '(')
|
|
|
|
++depth;
|
|
|
|
}
|
2016-07-21 11:08:28 +00:00
|
|
|
|
2016-03-09 10:38:53 +00:00
|
|
|
return text.substring (0, ptr + 1); // include closing parenthesis
|
2016-03-07 04:37:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private String getNumberText (String text)
|
|
|
|
{
|
|
|
|
int ptr = 0;
|
|
|
|
while (++ptr < text.length ())
|
|
|
|
{
|
|
|
|
char c = text.charAt (ptr);
|
|
|
|
if (c != '.' && (c < '0' || c > '9'))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return text.substring (0, ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
private String getAddressText (String text)
|
|
|
|
{
|
|
|
|
int ptr = 0;
|
|
|
|
while (++ptr < text.length ())
|
|
|
|
{
|
|
|
|
char c = text.charAt (ptr);
|
|
|
|
if ((c < '0' || c > '9') && (c < 'A' || c > 'Z'))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return text.substring (0, ptr);
|
|
|
|
}
|
|
|
|
|
2016-03-07 12:16:11 +00:00
|
|
|
@Override
|
|
|
|
public String toString ()
|
2016-03-07 04:37:01 +00:00
|
|
|
{
|
2016-03-19 05:31:30 +00:00
|
|
|
// StringBuilder text = new StringBuilder ();
|
|
|
|
//
|
|
|
|
// int ptr = 0;
|
|
|
|
// for (Value value : values)
|
|
|
|
// {
|
|
|
|
// assert value != null;
|
|
|
|
// text.append (signs.get (ptr));
|
|
|
|
// // value.calculate ();
|
|
|
|
// // if (value.isValue ())
|
|
|
|
// // text.append (value.getValue ());
|
|
|
|
// if (ptr < operators.size ())
|
|
|
|
// {
|
|
|
|
// // System.out.println (operators.get (ptr));
|
|
|
|
// text.append (operators.get (ptr++));
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// // System.out.println ("finished building");
|
|
|
|
//
|
|
|
|
// return text.toString ();
|
|
|
|
return "Expression: " + text;
|
2016-03-07 12:16:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public static void main (String[] args)
|
|
|
|
{
|
2016-03-11 01:52:22 +00:00
|
|
|
Expression ex = new Expression (null, "-5+((-4-(20-(2^3))+6/3))*-2");
|
2016-03-07 12:16:11 +00:00
|
|
|
System.out.println (ex.getValue ());
|
2016-03-11 01:52:22 +00:00
|
|
|
System.out.println (ex);
|
2016-03-07 04:37:01 +00:00
|
|
|
}
|
|
|
|
}
|