dmolony-DiskBrowser/src/com/bytezone/diskbrowser/visicalc/Expression.java

378 lines
11 KiB
Java
Raw Permalink Normal View History

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
2020-02-10 11:05:40 +00:00
// -----------------------------------------------------------------------------------//
2017-03-24 11:02:52 +00:00
class Expression extends AbstractValue
2020-02-10 11:05:40 +00:00
// -----------------------------------------------------------------------------------//
2016-03-07 04:37:01 +00:00
{
2020-02-02 10:17:49 +00:00
private final List<String> operators = new ArrayList<> ();
private final List<String> signs = new ArrayList<> ();
2016-03-07 04:37:01 +00:00
2020-02-10 11:05:40 +00:00
// ---------------------------------------------------------------------------------//
Expression (Cell cell, String text)
// ---------------------------------------------------------------------------------//
2016-03-07 04:37:01 +00:00
{
2017-03-23 13:30:41 +00:00
super (cell, text);
2017-02-25 03:56:22 +00:00
String line = balanceBrackets (text); // add trailing right brackets if necessary
2016-03-11 21:56:02 +00:00
2017-03-23 13:30:41 +00:00
if (Condition.isCondition (text))
{
values.add (new Condition (cell, text));
signs.add ("(+)"); // reduce() needs this
return;
}
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
2017-03-18 04:35:34 +00:00
String functionText = getFunctionCall (line.substring (ptr));
2016-03-09 10:38:53 +00:00
ptr += functionText.length ();
2017-03-19 01:03:57 +00:00
values.add (cell.getFunction (functionText));
2016-03-09 10:38:53 +00:00
break;
2016-03-07 04:37:01 +00:00
2016-03-09 10:38:53 +00:00
case '(': // parentheses block
2017-03-14 11:28:52 +00:00
String bracketText = getBalancedText (line.substring (ptr));
2016-03-09 10:38:53 +00:00
ptr += bracketText.length ();
2017-03-19 01:03:57 +00:00
values.add (new Expression (cell,
2016-03-11 21:56:02 +00:00
bracketText.substring (1, bracketText.length () - 1)));
2016-03-09 10:38:53 +00:00
break;
2016-03-07 04:37:01 +00:00
2017-03-03 10:24:23 +00:00
case '#': // no idea
2016-03-11 01:52:22 +00:00
System.out.printf ("Hash character [%s] in [%s]%n", ch, line);
2017-03-03 10:24:23 +00:00
ptr++;
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 ();
2017-03-23 13:30:41 +00:00
values.add (new Number (cell, 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 ();
values.add (cell.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;
}
}
2017-03-23 13:30:41 +00:00
// check for possible 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
}
2017-03-14 11:28:52 +00:00
assert values.size () > 0;
2017-03-23 13:30:41 +00:00
valueType = values.get (0).getValueType ();
2017-02-25 03:56:22 +00:00
}
2020-02-10 11:05:40 +00:00
// ---------------------------------------------------------------------------------//
2017-02-25 10:30:56 +00:00
Value reduce ()
2020-02-10 11:05:40 +00:00
// ---------------------------------------------------------------------------------//
2017-02-25 10:30:56 +00:00
{
2017-03-23 13:30:41 +00:00
return values.size () == 1 && signs.get (0).equals ("(+)") ? values.get (0) : this;
2017-02-25 10:30:56 +00:00
}
2020-02-10 11:05:40 +00:00
// ---------------------------------------------------------------------------------//
2017-02-25 03:56:22 +00:00
Value get (int index)
2020-02-10 11:05:40 +00:00
// ---------------------------------------------------------------------------------//
2017-02-25 03:56:22 +00:00
{
2017-03-23 13:30:41 +00:00
if (index < 0 || index >= values.size ())
throw new IllegalArgumentException ();
2017-02-25 03:56:22 +00:00
return values.get (index);
2016-03-09 10:38:53 +00:00
}
2020-02-10 11:05:40 +00:00
// ---------------------------------------------------------------------------------//
2017-03-23 13:30:41 +00:00
@Override
public String getType ()
2020-02-10 11:05:40 +00:00
// ---------------------------------------------------------------------------------//
2017-03-23 13:30:41 +00:00
{
return "Expression";
}
2020-02-10 11:05:40 +00:00
// ---------------------------------------------------------------------------------//
2016-03-09 10:38:53 +00:00
@Override
2017-02-28 20:39:26 +00:00
public void calculate ()
2020-02-10 11:05:40 +00:00
// ---------------------------------------------------------------------------------//
2016-03-09 10:38:53 +00:00
{
2017-03-23 13:30:41 +00:00
assert values.size () > 0;
2017-03-15 04:40:18 +00:00
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);
2017-03-03 23:41:08 +00:00
thisValue.calculate ();
2017-02-25 03:56:22 +00:00
2017-03-25 08:02:02 +00:00
if (valueType == ValueType.NUMBER)
value = thisValue.getDouble ();
else
bool = thisValue.getBoolean ();
2017-03-23 13:30:41 +00:00
if (!thisValue.isValid ()) // ERROR / NA
2016-03-16 19:32:25 +00:00
{
2016-03-17 04:40:43 +00:00
valueType = thisValue.getValueType ();
2017-02-28 20:39:26 +00:00
return;
2016-03-16 19:32:25 +00:00
}
2016-08-01 01:22:34 +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;
2017-03-23 13:30:41 +00:00
for (int i = 1; i < values.size (); i++) // only NUMBER will enter here
2016-03-16 19:32:25 +00:00
{
thisValue = values.get (i);
2017-03-03 23:41:08 +00:00
thisValue.calculate ();
2017-02-25 03:56:22 +00:00
2017-03-23 13:30:41 +00:00
if (!thisValue.isValid ())
2016-03-16 19:32:25 +00:00
{
2016-03-17 04:40:43 +00:00
valueType = thisValue.getValueType ();
2017-02-28 20:39:26 +00:00
return;
2016-03-16 19:32:25 +00:00
}
2016-03-17 04:40:43 +00:00
2017-03-23 13:30:41 +00:00
double nextValue = thisValue.getDouble ();
2017-03-03 10:24:23 +00:00
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 ("/"))
2017-03-08 09:18:59 +00:00
{
if (nextValue == 0)
{
2017-03-23 13:30:41 +00:00
valueResult = ValueResult.ERROR;
2017-03-08 09:18:59 +00:00
return;
}
2016-03-16 19:32:25 +00:00
value /= nextValue;
2017-03-08 09:18:59 +00:00
}
2016-03-16 19:32:25 +00:00
else if (operator.equals ("^"))
value = Math.pow (value, nextValue);
2016-07-21 11:28:22 +00:00
2017-03-23 13:30:41 +00:00
if (Double.isNaN (value))
{
valueResult = ValueResult.ERROR;
return;
}
}
2016-03-16 19:32:25 +00:00
}
catch (Exception e)
{
2017-03-23 13:30:41 +00:00
valueResult = ValueResult.ERROR;
2017-02-25 03:56:22 +00:00
e.printStackTrace ();
2017-03-14 11:28:52 +00:00
return;
2016-03-07 04:37:01 +00:00
}
}
2020-02-10 11:05:40 +00:00
// ---------------------------------------------------------------------------------//
2017-03-14 11:28:52 +00:00
private String balanceBrackets (String input)
2020-02-10 11:05:40 +00:00
// ---------------------------------------------------------------------------------//
2016-03-11 21:56:02 +00:00
{
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",
2017-02-25 03:56:22 +00:00
leftBracket, rightBracket);
2016-03-14 20:09:04 +00:00
System.out.println (input);
2016-03-17 04:40:43 +00:00
return "@ERROR";
2016-03-14 20:09:04 +00:00
}
2016-07-22 05:01:17 +00:00
2016-03-14 20:09:04 +00:00
while (rightBracket < leftBracket)
{
line = line + ")";
rightBracket++;
}
2016-03-11 21:56:02 +00:00
}
return line;
}
2017-03-14 11:28:52 +00:00
// called for functions and expressions
2020-02-10 11:05:40 +00:00
// ---------------------------------------------------------------------------------//
2017-03-18 04:35:34 +00:00
private String getBalancedText (String text)
2020-02-10 11:05:40 +00:00
// ---------------------------------------------------------------------------------//
2016-03-07 04:37:01 +00:00
{
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
}
2017-03-17 23:57:48 +00:00
// reads text up to the next comma that is not part of a function
// text does not include the outer brackets or calling function name
2020-02-10 11:05:40 +00:00
// ---------------------------------------------------------------------------------//
2017-03-17 23:57:48 +00:00
static String getParameter (String text)
2020-02-10 11:05:40 +00:00
// ---------------------------------------------------------------------------------//
2017-03-17 11:03:56 +00:00
{
2017-03-17 23:57:48 +00:00
int depth = 0;
int ptr = 0;
while (ptr < text.length ())
{
char c = text.charAt (ptr);
if (c == '(')
++depth;
else if (c == ')')
--depth;
else if (c == ',' && depth == 0)
break;
++ptr;
}
return text.substring (0, ptr);
}
// receives a string starting with the function name
2020-02-10 11:05:40 +00:00
// ---------------------------------------------------------------------------------//
2017-03-18 04:35:34 +00:00
private String getFunctionCall (String text)
2020-02-10 11:05:40 +00:00
// ---------------------------------------------------------------------------------//
2017-03-17 23:57:48 +00:00
{
if (text.charAt (0) != '@')
throw new IllegalArgumentException ("Bad function name: " + text);
for (String functionName : Function.functionList)
if (text.startsWith (functionName))
{
if (functionName.endsWith ("(")) // if function has parameters
return getBalancedText (text); // return full function call
2017-03-17 23:57:48 +00:00
return functionName; // return function name only
}
2017-03-17 11:03:56 +00:00
2017-03-17 23:57:48 +00:00
throw new IllegalArgumentException ("Bad function name: " + text);
2017-03-17 11:03:56 +00:00
}
2020-02-10 11:05:40 +00:00
// ---------------------------------------------------------------------------------//
2016-03-07 04:37:01 +00:00
private String getNumberText (String text)
2020-02-10 11:05:40 +00:00
// ---------------------------------------------------------------------------------//
2016-03-07 04:37:01 +00:00
{
int ptr = 0;
while (++ptr < text.length ())
{
char c = text.charAt (ptr);
if (c != '.' && (c < '0' || c > '9'))
break;
}
return text.substring (0, ptr);
}
2020-02-10 11:05:40 +00:00
// ---------------------------------------------------------------------------------//
2016-03-07 04:37:01 +00:00
private String getAddressText (String text)
2020-02-10 11:05:40 +00:00
// ---------------------------------------------------------------------------------//
2016-03-07 04:37:01 +00:00
{
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);
}
2020-02-10 11:05:40 +00:00
// ---------------------------------------------------------------------------------//
2017-02-25 03:56:22 +00:00
public String fullText ()
2020-02-10 11:05:40 +00:00
// ---------------------------------------------------------------------------------//
2017-02-25 03:56:22 +00:00
{
StringBuilder text = new StringBuilder ();
int ptr = 0;
for (Value value : values)
{
assert value != null;
text.append (signs.get (ptr));
2017-03-23 13:30:41 +00:00
text.append (value.getDouble ());
2017-02-25 03:56:22 +00:00
if (ptr < operators.size ())
text.append (operators.get (ptr++));
}
return text.toString ();
}
2020-02-10 11:05:40 +00:00
// ---------------------------------------------------------------------------------//
2017-02-25 03:56:22 +00:00
@Override
public String toString ()
2020-02-10 11:05:40 +00:00
// ---------------------------------------------------------------------------------//
2017-02-25 03:56:22 +00:00
{
2017-03-23 13:30:41 +00:00
StringBuilder text = new StringBuilder ();
2017-03-24 02:00:11 +00:00
text.append (String.format ("%s%n", LINE));
2017-03-25 21:58:10 +00:00
text.append (String.format (FMT4, "Exprssion", getFullText (), valueType,
getValueText (this)));
2017-03-23 13:30:41 +00:00
int index = 0;
for (Value value : values)
{
String sign = signs.get (index);
if (!"(+)".equals (sign))
2017-03-24 02:00:11 +00:00
text.append (String.format (FMT2, "sign", sign));
text.append (String.format (FMT4, value.getType (), value.getFullText (),
value.getValueType (), getValueText (value)));
2017-03-23 13:30:41 +00:00
if (index < operators.size ())
2017-03-24 02:00:11 +00:00
text.append (String.format (FMT2, "operator", operators.get (index)));
2017-03-23 13:30:41 +00:00
++index;
}
return text.toString ();
2017-02-25 03:56:22 +00:00
}
2016-03-07 04:37:01 +00:00
}