1374 lines
33 KiB
C#
1374 lines
33 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Text.RegularExpressions;
|
|
|
|
namespace DevComponents.DotNetBar.SuperGrid
|
|
{
|
|
/// <summary>
|
|
/// Expression evaluator
|
|
/// </summary>
|
|
public class EEval
|
|
{
|
|
#region Private data
|
|
|
|
private int _TCount;
|
|
private int _PCount;
|
|
private ETokens[] _PTokens = new ETokens[10];
|
|
private List<ETokens> _PfTokens = new List<ETokens>();
|
|
|
|
private GridCell _Cell;
|
|
private GridPanel _GridPanel;
|
|
private List<GridCell> _UsedCells;
|
|
|
|
private string _Source;
|
|
private List<string> _StringPool = new List<string>();
|
|
|
|
private object _Tag;
|
|
|
|
#endregion
|
|
|
|
///<summary>
|
|
/// Expression evaluator constructor
|
|
///</summary>
|
|
///<param name="gridPanel">Associated GridPanel</param>
|
|
///<param name="source">'Excel-like' expression (eg. =d4+d5)</param>
|
|
public EEval(GridPanel gridPanel, string source)
|
|
: this(null, source, new List<GridCell>())
|
|
{
|
|
_GridPanel = gridPanel;
|
|
}
|
|
|
|
internal EEval(GridCell cell, string source)
|
|
: this(cell, source, new List<GridCell>())
|
|
{
|
|
}
|
|
|
|
private EEval(GridCell cell, string source, List<GridCell> usedCells)
|
|
{
|
|
_Cell = cell;
|
|
_UsedCells = usedCells;
|
|
|
|
if (_Cell != null)
|
|
_GridPanel = cell.GridPanel;
|
|
|
|
Source = source;
|
|
}
|
|
|
|
#region Public properties
|
|
|
|
#region Cell
|
|
|
|
///<summary>
|
|
/// Gets the associated Grid Cell, if any
|
|
///</summary>
|
|
public GridCell Cell
|
|
{
|
|
get { return (_Cell); }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GridPanel
|
|
|
|
///<summary>
|
|
/// Gets the associated Grid Panel
|
|
///</summary>
|
|
public GridPanel GridPanel
|
|
{
|
|
get { return (_GridPanel); }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Source
|
|
|
|
///<summary>
|
|
/// Gets or sets the expression source
|
|
///</summary>
|
|
public string Source
|
|
{
|
|
get { return (_Source); }
|
|
|
|
set
|
|
{
|
|
_Source = value;
|
|
|
|
Tokenize(value);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Tag
|
|
|
|
///<summary>
|
|
/// Gets or sets user-defined data associated with the object
|
|
///</summary>
|
|
public object Tag
|
|
{
|
|
get { return (_Tag); }
|
|
set { _Tag = value; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region Tokenize code
|
|
|
|
private void Tokenize(string source)
|
|
{
|
|
const string sref = @"([\.]|[a-zA-Z_]+|""[^""]+"")\s*([\.]|\d+)";
|
|
|
|
Regex p = new Regex(
|
|
"(" + sref + "\\s*:\\s*" + sref + ")|(" + sref + ")|" +
|
|
"([a-zA-Z0-9.]+)|" +
|
|
"\"([^\"]+)\"|" +
|
|
"([()+\\-*/%,|&\\^])|(<<)|(>>)");
|
|
|
|
MatchCollection mc = p.Matches(source);
|
|
|
|
Expr(mc);
|
|
|
|
ETokens t = GetToken(mc);
|
|
|
|
if (t != ETokens.BadToken)
|
|
throw new Exception("Expression error.");
|
|
}
|
|
|
|
#region Expr
|
|
|
|
/// <summary>
|
|
/// Main expression entry routine
|
|
/// </summary>
|
|
private void Expr(MatchCollection mc)
|
|
{
|
|
Term1(mc);
|
|
|
|
ETokens t = GetToken(mc);
|
|
|
|
while (t == ETokens.BitwiseOr)
|
|
{
|
|
Term1(mc);
|
|
|
|
_PfTokens.Add(t);
|
|
|
|
t = GetToken(mc);
|
|
}
|
|
|
|
PutToken(t);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Term1
|
|
|
|
/// <summary>
|
|
/// Handles the ^ operator
|
|
/// </summary>
|
|
private void Term1(MatchCollection mc)
|
|
{
|
|
Term2(mc);
|
|
|
|
ETokens t = GetToken(mc);
|
|
|
|
while (t == ETokens.BitwiseXor)
|
|
{
|
|
Term2(mc);
|
|
|
|
_PfTokens.Add(t);
|
|
|
|
t = GetToken(mc);
|
|
}
|
|
|
|
PutToken(t);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Term2
|
|
|
|
/// <summary>
|
|
/// Handles the Ampersand operator
|
|
/// </summary>
|
|
private void Term2(MatchCollection mc)
|
|
{
|
|
Term3(mc);
|
|
|
|
ETokens t = GetToken(mc);
|
|
|
|
while (t == ETokens.BitwiseAnd)
|
|
{
|
|
Term3(mc);
|
|
|
|
_PfTokens.Add(t);
|
|
|
|
t = GetToken(mc);
|
|
}
|
|
|
|
PutToken(t);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Term3
|
|
|
|
/// <summary>
|
|
/// Handles the shift left and right operators
|
|
/// </summary>
|
|
private void Term3(MatchCollection mc)
|
|
{
|
|
Term4(mc);
|
|
|
|
ETokens t = GetToken(mc);
|
|
|
|
while (t == ETokens.ShiftLeft || t == ETokens.ShiftRight)
|
|
{
|
|
Term4(mc);
|
|
|
|
_PfTokens.Add(t);
|
|
|
|
t = GetToken(mc);
|
|
}
|
|
|
|
PutToken(t);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Term4
|
|
|
|
/// <summary>
|
|
/// Handles %, *, and / operators
|
|
/// </summary>
|
|
private void Term4(MatchCollection mc)
|
|
{
|
|
Term5(mc);
|
|
|
|
ETokens t = GetToken(mc);
|
|
|
|
while (t == ETokens.Add || t == ETokens.Subtract)
|
|
{
|
|
Term5(mc);
|
|
|
|
_PfTokens.Add(t);
|
|
|
|
t = GetToken(mc);
|
|
}
|
|
|
|
PutToken(t);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Term5
|
|
|
|
/// <summary>
|
|
/// Handles %, *, and / operators
|
|
/// </summary>
|
|
private void Term5(MatchCollection mc)
|
|
{
|
|
Factor(mc);
|
|
|
|
ETokens t = GetToken(mc);
|
|
|
|
while (t == ETokens.Mod ||
|
|
t == ETokens.Multiply || t == ETokens.Divide)
|
|
{
|
|
Factor(mc);
|
|
|
|
_PfTokens.Add(t);
|
|
|
|
t = GetToken(mc);
|
|
}
|
|
|
|
PutToken(t);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Factor
|
|
|
|
/// <summary>
|
|
/// Handles factor processing
|
|
/// </summary>
|
|
private void Factor(MatchCollection mc)
|
|
{
|
|
ETokens t = GetToken(mc);
|
|
|
|
int n = 0;
|
|
|
|
// Unary operators
|
|
|
|
while (t == ETokens.Add || t == ETokens.Subtract)
|
|
{
|
|
if (t == ETokens.Subtract)
|
|
n++;
|
|
|
|
t = GetToken(mc);
|
|
}
|
|
|
|
// Operand processing
|
|
|
|
if (t < ETokens.Operator)
|
|
{
|
|
if (Function(mc, t) == false)
|
|
_PfTokens.Add(t);
|
|
}
|
|
else if (t == ETokens.LParen)
|
|
{
|
|
Expr(mc);
|
|
|
|
t = GetToken(mc);
|
|
|
|
if (t != ETokens.RParen)
|
|
throw new Exception("Expecting right parenthesis.");
|
|
}
|
|
else
|
|
{
|
|
PutToken(t);
|
|
}
|
|
|
|
if (n % 2 == 1)
|
|
{
|
|
if (_PfTokens.Count == 0)
|
|
throw new Exception("Invalid expression.");
|
|
|
|
_PfTokens.Add(ETokens.Negate);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Function
|
|
|
|
/// <summary>
|
|
/// Handles function parsing
|
|
/// </summary>
|
|
/// <param name="mc"></param>
|
|
/// <param name="e"></param>
|
|
/// <returns></returns>
|
|
private bool Function(MatchCollection mc, ETokens e)
|
|
{
|
|
string s = _StringPool[(int)e].ToUpper();
|
|
|
|
ETokens t = GetFunction(mc, s);
|
|
|
|
if (t != ETokens.BadToken)
|
|
{
|
|
_PfTokens.Add(t);
|
|
|
|
t = GetToken(mc);
|
|
|
|
if (t != ETokens.LParen)
|
|
throw new Exception("Expecting left parenthesis.");
|
|
|
|
ParameterList(mc, e);
|
|
|
|
t = GetToken(mc);
|
|
|
|
if (t != ETokens.RParen)
|
|
throw new Exception("Expecting right parenthesis.");
|
|
|
|
return (true);
|
|
}
|
|
|
|
return (false);
|
|
}
|
|
|
|
#region GetFunction
|
|
|
|
/// <summary>
|
|
/// Determines whether the parsed string
|
|
/// is a one of our function keywords
|
|
/// </summary>
|
|
/// <param name="mc"></param>
|
|
/// <param name="s"></param>
|
|
/// <returns></returns>
|
|
private ETokens GetFunction(MatchCollection mc, string s)
|
|
{
|
|
ETokens t = GetToken(mc);
|
|
|
|
if (t == ETokens.LParen)
|
|
{
|
|
PutToken(t);
|
|
|
|
switch (s)
|
|
{
|
|
case "AVG":
|
|
return (ETokens.Avg);
|
|
|
|
case "CEILING":
|
|
return (ETokens.Ceiling);
|
|
|
|
case "FLOOR":
|
|
return (ETokens.Floor);
|
|
|
|
case "MIN":
|
|
return (ETokens.Min);
|
|
|
|
case "MAX":
|
|
return (ETokens.Max);
|
|
|
|
case "ROUND":
|
|
return (ETokens.Round);
|
|
|
|
case "SUM":
|
|
return (ETokens.Sum);
|
|
|
|
default:
|
|
return (ETokens.User);
|
|
}
|
|
}
|
|
|
|
PutToken(t);
|
|
|
|
return (ETokens.BadToken);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region ParameterList
|
|
|
|
/// <summary>
|
|
/// Handles function parameters
|
|
/// </summary>
|
|
private void ParameterList(MatchCollection mc, ETokens e)
|
|
{
|
|
int n = 0;
|
|
|
|
if (_PfTokens[_PfTokens.Count - 1] == ETokens.User)
|
|
{
|
|
_PfTokens.Add(e);
|
|
n++;
|
|
}
|
|
|
|
ETokens t = GetToken(mc);
|
|
|
|
while (t != ETokens.RParen && t != ETokens.BadToken)
|
|
{
|
|
PutToken(t);
|
|
|
|
int count = _PfTokens.Count;
|
|
|
|
Expr(mc);
|
|
|
|
if (_PfTokens.Count > count)
|
|
n++;
|
|
|
|
t = GetToken(mc);
|
|
|
|
if (t != ETokens.Comma)
|
|
break;
|
|
|
|
t = GetToken(mc);
|
|
}
|
|
|
|
PutToken(t);
|
|
|
|
_PfTokens.Add(ETokens.Function);
|
|
_PfTokens.Add((ETokens) n);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetToken
|
|
|
|
/// <summary>
|
|
/// Gets the next parsed token
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private ETokens GetToken(MatchCollection mc)
|
|
{
|
|
ETokens t = ETokens.BadToken;
|
|
|
|
if (_PCount > 0)
|
|
{
|
|
t = _PTokens[--_PCount];
|
|
}
|
|
else
|
|
{
|
|
if (_TCount < mc.Count)
|
|
{
|
|
string s = mc[_TCount].Value;
|
|
|
|
switch (s)
|
|
{
|
|
case "|":
|
|
t = ETokens.BitwiseOr;
|
|
break;
|
|
|
|
case "^":
|
|
t = ETokens.BitwiseXor;
|
|
break;
|
|
|
|
case "&":
|
|
t = ETokens.BitwiseAnd;
|
|
break;
|
|
|
|
case "<<":
|
|
t = ETokens.ShiftLeft;
|
|
break;
|
|
|
|
case ">>":
|
|
t = ETokens.ShiftRight;
|
|
break;
|
|
|
|
case "+":
|
|
t = ETokens.Add;
|
|
break;
|
|
|
|
case "-":
|
|
t = ETokens.Subtract;
|
|
break;
|
|
|
|
case "*":
|
|
t = ETokens.Multiply;
|
|
break;
|
|
|
|
case "/":
|
|
t = ETokens.Divide;
|
|
break;
|
|
|
|
case "%":
|
|
t = ETokens.Mod;
|
|
break;
|
|
|
|
case "(":
|
|
t = ETokens.LParen;
|
|
break;
|
|
|
|
case ")":
|
|
t = ETokens.RParen;
|
|
break;
|
|
|
|
case ",":
|
|
t = ETokens.Comma;
|
|
break;
|
|
|
|
default:
|
|
int index = _StringPool.IndexOf(s);
|
|
|
|
if (index < 0)
|
|
{
|
|
_StringPool.Add(s);
|
|
|
|
index = _StringPool.Count - 1;
|
|
}
|
|
|
|
t = (ETokens)(index);
|
|
break;
|
|
}
|
|
|
|
_TCount++;
|
|
}
|
|
}
|
|
|
|
return (t);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region PutToken
|
|
|
|
/// <summary>
|
|
/// Saves the given token for future use
|
|
/// </summary>
|
|
/// <param name="t"></param>
|
|
private void PutToken(ETokens t)
|
|
{
|
|
_PTokens[_PCount++] = t;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region Evaluate
|
|
|
|
/// <summary>
|
|
/// Evaluates the previously tokenized code
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public object Evaluate()
|
|
{
|
|
_UsedCells.Clear();
|
|
|
|
if (_Cell != null)
|
|
_UsedCells.Add(_Cell);
|
|
|
|
return (EvaluateEx());
|
|
}
|
|
|
|
internal object EvaluateEx()
|
|
{
|
|
Stack myStack = new Stack();
|
|
|
|
for (int i = 0; i < _PfTokens.Count; i++)
|
|
{
|
|
if (_PfTokens[i] < ETokens.Operator)
|
|
{
|
|
myStack.Push(_StringPool[(int)_PfTokens[i]]);
|
|
}
|
|
else if (_PfTokens[i] > ETokens.Functions)
|
|
{
|
|
myStack.Push(_PfTokens[i]);
|
|
}
|
|
else
|
|
{
|
|
if (_PfTokens[i] == ETokens.Negate)
|
|
{
|
|
object value = ProcessValue(myStack.Pop());
|
|
|
|
if (value is double == false)
|
|
throw new Exception("Invalid negation value");
|
|
|
|
myStack.Push(-(double)value);
|
|
}
|
|
else if (_PfTokens[i] == ETokens.Function)
|
|
{
|
|
if (++i == _PfTokens.Count)
|
|
throw new Exception("Expecting function parameter count");
|
|
|
|
EvalFunction(myStack, (int)_PfTokens[i]);
|
|
}
|
|
else
|
|
{
|
|
object value1 = ProcessValue(myStack.Pop());
|
|
object value2 = ProcessValue(myStack.Pop());
|
|
|
|
if (value1 == null || value2 == null)
|
|
{
|
|
if (value1 == null && value2 == null)
|
|
value1 = value2 = 0d;
|
|
|
|
else if (value1 == null)
|
|
{
|
|
if (value2 is string)
|
|
value1 = "";
|
|
else
|
|
value1 = 0d;
|
|
}
|
|
else
|
|
{
|
|
if (value1 is string)
|
|
value2 = "";
|
|
else
|
|
value2 = 0d;
|
|
}
|
|
}
|
|
|
|
if (value1 is string || value2 is string)
|
|
OpStringValue(myStack, _PfTokens[i], value1.ToString(), value2.ToString());
|
|
else
|
|
OpDoubleValue(myStack, _PfTokens[i], (double)value1, (double)value2);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (myStack.Count > 0)
|
|
return (ProcessValue(myStack.Pop()));
|
|
|
|
return (0);
|
|
}
|
|
|
|
#region OpStringValue
|
|
|
|
private void OpStringValue(Stack myStack, ETokens op, string s1, string s2)
|
|
{
|
|
switch (op)
|
|
{
|
|
case ETokens.Add:
|
|
myStack.Push(s2 + s1);
|
|
break;
|
|
|
|
default:
|
|
throw new Exception("Invalid string operation.");
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region OpDoubleValue
|
|
|
|
private void OpDoubleValue(
|
|
Stack myStack, ETokens op, double d1, double d2)
|
|
{
|
|
switch (op)
|
|
{
|
|
case ETokens.BitwiseAnd:
|
|
myStack.Push((double)((int)d2 & (int)d1));
|
|
break;
|
|
|
|
case ETokens.BitwiseOr:
|
|
myStack.Push((double)((int)d2 | (int)d1));
|
|
break;
|
|
|
|
case ETokens.BitwiseXor:
|
|
myStack.Push((double)((int)d2 ^ (int)d1));
|
|
break;
|
|
|
|
case ETokens.Add:
|
|
myStack.Push(d2 + d1);
|
|
break;
|
|
|
|
case ETokens.Subtract:
|
|
myStack.Push(d2 - d1);
|
|
break;
|
|
|
|
case ETokens.Multiply:
|
|
myStack.Push(d2 * d1);
|
|
break;
|
|
|
|
case ETokens.Divide:
|
|
myStack.Push(d2 / d1);
|
|
break;
|
|
|
|
case ETokens.Mod:
|
|
myStack.Push(d2 % d1);
|
|
break;
|
|
|
|
case ETokens.ShiftLeft:
|
|
myStack.Push((double)((int)d2 << (int)d1));
|
|
break;
|
|
|
|
case ETokens.ShiftRight:
|
|
myStack.Push((double)((int)d2 >> (int)d1));
|
|
break;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region EvalFunction
|
|
|
|
/// <summary>
|
|
/// Evaluates the current function
|
|
/// </summary>
|
|
/// <param name="myStack"></param>
|
|
/// <param name="count"></param>
|
|
private void EvalFunction(Stack myStack, int count)
|
|
{
|
|
object[] args = new object[count];
|
|
|
|
for (int i = count - 1; i >= 0; i--)
|
|
args[i] = myStack.Pop();
|
|
|
|
switch ((ETokens)myStack.Pop())
|
|
{
|
|
case ETokens.Avg:
|
|
myStack.Push(Avg(args));
|
|
break;
|
|
|
|
case ETokens.Ceiling:
|
|
myStack.Push(Ceiling(args));
|
|
break;
|
|
|
|
case ETokens.Floor:
|
|
myStack.Push(Floor(args));
|
|
break;
|
|
|
|
case ETokens.Min:
|
|
myStack.Push(Min(args));
|
|
break;
|
|
|
|
case ETokens.Max:
|
|
myStack.Push(Max(args));
|
|
break;
|
|
|
|
case ETokens.Round:
|
|
myStack.Push(Round(args));
|
|
break;
|
|
|
|
case ETokens.Sum:
|
|
myStack.Push(Sum(args));
|
|
break;
|
|
|
|
case ETokens.User:
|
|
myStack.Push(User(args));
|
|
break;
|
|
}
|
|
}
|
|
|
|
#region Average
|
|
|
|
/// <summary>
|
|
/// Calculates the average of the given set of values
|
|
/// </summary>
|
|
/// <param name="args"></param>
|
|
/// <returns></returns>
|
|
private double Avg(IEnumerable<object> args)
|
|
{
|
|
double d = 0;
|
|
int count = 1;
|
|
|
|
foreach (object o in args)
|
|
{
|
|
object value = ProcessValue(o, ref count);
|
|
|
|
if (value is double)
|
|
d += (double) value;
|
|
else
|
|
throw new Exception("Can't AVERAGE non-numeric data.");
|
|
}
|
|
|
|
return (d / count);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Ceiling
|
|
|
|
/// <summary>
|
|
/// Returns the smallest whole value
|
|
/// greater than or equal to the given value.
|
|
/// </summary>
|
|
/// <param name="args"></param>
|
|
/// <returns></returns>
|
|
private double Ceiling(object[] args)
|
|
{
|
|
if (args.Length != 1)
|
|
throw new Exception("Ceiling(): Invalid number of arguments.");
|
|
|
|
object value = ProcessValue(args[0]);
|
|
|
|
if (value is double == false)
|
|
throw new Exception("Ceiling(): Invalid data type.");
|
|
|
|
return (Math.Ceiling((double)value));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Floor
|
|
|
|
/// <summary>
|
|
/// Returns the largest whole value
|
|
/// less than or equal to the given value.
|
|
/// </summary>
|
|
/// <param name="args"></param>
|
|
/// <returns></returns>
|
|
private double Floor(object[] args)
|
|
{
|
|
if (args.Length != 1)
|
|
throw new Exception("Floor(): Invalid number of arguments.");
|
|
|
|
object value = ProcessValue(args[0]);
|
|
|
|
if (value is double == false)
|
|
throw new Exception("Floor(): Invalid data type.");
|
|
|
|
return (Math.Floor((double)value));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Minimum
|
|
|
|
/// <summary>
|
|
/// Calculates the minimum from the given set of values
|
|
/// </summary>
|
|
/// <param name="args"></param>
|
|
/// <returns></returns>
|
|
private double Min(ICollection<object> args)
|
|
{
|
|
if (args.Count == 0)
|
|
return (0);
|
|
|
|
double d = double.PositiveInfinity;
|
|
|
|
foreach (object o in args)
|
|
{
|
|
double n = ProcessMinMaxValue(o, true);
|
|
|
|
if (n < d)
|
|
d = n;
|
|
}
|
|
|
|
return (d);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Maximum
|
|
|
|
/// <summary>
|
|
/// Calculates the maximum from the given set of values
|
|
/// </summary>
|
|
/// <param name="args"></param>
|
|
/// <returns></returns>
|
|
private double Max(ICollection<object> args)
|
|
{
|
|
if (args.Count == 0)
|
|
return (0);
|
|
|
|
double d = double.NegativeInfinity;
|
|
|
|
foreach (object o in args)
|
|
{
|
|
double n = ProcessMinMaxValue(o, false);
|
|
|
|
if (n > d)
|
|
d = n;
|
|
}
|
|
|
|
return (d);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Round
|
|
|
|
/// <summary>
|
|
/// Returns the largest whole value
|
|
/// less than or equal to the given value.
|
|
/// </summary>
|
|
/// <param name="args"></param>
|
|
/// <returns></returns>
|
|
private double Round(object[] args)
|
|
{
|
|
if (args.Length != 1 && args.Length != 2)
|
|
throw new Exception("Round(): Invalid number of arguments.");
|
|
|
|
object value = ProcessValue(args[0]);
|
|
object decimals = args.Length == 2 ? ProcessValue(args[1]) : 0d;
|
|
|
|
if (value is double == false)
|
|
throw new Exception("Round(): Invalid value type.");
|
|
|
|
if (decimals is double == false)
|
|
throw new Exception("Round(): Invalid decimals type.");
|
|
|
|
return (Math.Round((double)value, Convert.ToInt32(decimals)));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Sum
|
|
|
|
/// <summary>
|
|
/// Calculates the sum of the given set of values
|
|
/// </summary>
|
|
/// <param name="args"></param>
|
|
/// <returns></returns>
|
|
private double Sum(IEnumerable<object> args)
|
|
{
|
|
double d = 0;
|
|
int count = 0;
|
|
|
|
foreach (object o in args)
|
|
{
|
|
object value = ProcessValue(o, ref count);
|
|
|
|
if (value is double)
|
|
d += (double)value;
|
|
else
|
|
throw new Exception("Can't SUM non-numeric data.");
|
|
}
|
|
|
|
return (d);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region User
|
|
|
|
/// <summary>
|
|
/// Calculates the sum of the given set of values
|
|
/// </summary>
|
|
/// <param name="args"></param>
|
|
/// <returns></returns>
|
|
private object User(object[] args)
|
|
{
|
|
object d = null;
|
|
int count = 0;
|
|
|
|
object[] uargs = new object[args.Length];
|
|
|
|
for (int i = 0; i < args.Length; i++)
|
|
uargs[i] = ProcessValue(args[i], ref count);
|
|
|
|
_GridPanel.SuperGrid.DoCellUserFunctionEvent(_Cell, uargs, ref d);
|
|
|
|
return (d);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ProcessValue
|
|
|
|
/// <summary>
|
|
/// Process the given object value
|
|
/// </summary>
|
|
/// <param name="o"></param>
|
|
/// <returns></returns>
|
|
private object ProcessValue(object o)
|
|
{
|
|
int count = 0;
|
|
|
|
return (ProcessValue(o, ref count));
|
|
}
|
|
|
|
private object ProcessValue(object o, ref int count)
|
|
{
|
|
count = 1;
|
|
|
|
if (o is double)
|
|
return (double)o;
|
|
|
|
if (o is string)
|
|
{
|
|
string s = (string)o;
|
|
|
|
if (s.Length > 0)
|
|
{
|
|
if (s[0] == '.' || s[0] == '"' || char.IsLetter(s[0]) == true)
|
|
{
|
|
object d = null;
|
|
|
|
List<GridCell> cells = GetCellReferences(s);
|
|
|
|
if (cells != null && cells.Count > 0)
|
|
{
|
|
foreach (GridCell cell in cells)
|
|
{
|
|
object n;
|
|
|
|
if (ProcessCellValue(cell, out n) == true)
|
|
{
|
|
if (d == null)
|
|
{
|
|
d = n;
|
|
}
|
|
else
|
|
{
|
|
if (n is string || d is string)
|
|
d = d + n.ToString();
|
|
else
|
|
d = (double)d + (double)n;
|
|
}
|
|
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return (s);
|
|
}
|
|
|
|
return (d);
|
|
}
|
|
|
|
return (GetValue(s));
|
|
}
|
|
}
|
|
|
|
return (0d);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ProcessMinMaxValue
|
|
|
|
/// <summary>
|
|
/// Processes the given MinMax object value
|
|
/// </summary>
|
|
/// <param name="o"></param>
|
|
/// <param name="min"></param>
|
|
/// <returns></returns>
|
|
private double ProcessMinMaxValue(object o, bool min)
|
|
{
|
|
if (o is double)
|
|
return (double) o;
|
|
|
|
if (o is string)
|
|
{
|
|
string s = (string)o;
|
|
|
|
if (s.Length > 0)
|
|
{
|
|
if (s[0] == '.' || s[0] == '"' || char.IsLetter(s[0]) == true)
|
|
{
|
|
double d = 0;
|
|
|
|
IEnumerable<GridCell> cells = GetCellReferences(s);
|
|
|
|
bool valueSet = false;
|
|
|
|
foreach (GridCell cell in cells)
|
|
{
|
|
object value;
|
|
|
|
if (ProcessCellValue(cell, out value) == true)
|
|
{
|
|
if (value is double == false)
|
|
throw new Exception("Cannot Min/Max non-numeric values.");
|
|
|
|
double n = (double)value;
|
|
|
|
if (valueSet == false || (min ? n < d : n > d))
|
|
{
|
|
d = n;
|
|
|
|
valueSet = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (d);
|
|
}
|
|
|
|
return ((double)GetValue(s));
|
|
}
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ProcessCellValue
|
|
|
|
/// <summary>
|
|
/// Processes the given cell reference value
|
|
/// </summary>
|
|
/// <param name="cell"></param>
|
|
/// <param name="value"></param>
|
|
/// <returns></returns>
|
|
private bool ProcessCellValue(GridCell cell, out object value)
|
|
{
|
|
value = cell.Value ?? "";
|
|
|
|
string s = value.ToString();
|
|
|
|
if (string.IsNullOrEmpty(s) == false)
|
|
{
|
|
if (s[0] == '=')
|
|
{
|
|
if (_UsedCells.Contains(cell) == true)
|
|
throw new Exception("Recursive cell reference");
|
|
|
|
_UsedCells.Add(cell);
|
|
|
|
EEval eval = new EEval(cell, s, _UsedCells);
|
|
|
|
value = eval.EvaluateEx();
|
|
|
|
_UsedCells.Remove(cell);
|
|
|
|
return (true);
|
|
}
|
|
|
|
value = GetValue(s);
|
|
|
|
return (true);
|
|
}
|
|
|
|
return (false);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region GetValue
|
|
|
|
/// <summary>
|
|
/// Gets the double value from the given string
|
|
/// </summary>
|
|
/// <param name="s"></param>
|
|
/// <returns></returns>
|
|
private object GetValue(string s)
|
|
{
|
|
double d;
|
|
|
|
if (double.TryParse(s, out d) == true)
|
|
return (d);
|
|
|
|
return (s);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region GetCellReferences
|
|
|
|
internal List<GridCell> GetCellReferences()
|
|
{
|
|
return (GetCellReferences(_Source));
|
|
}
|
|
|
|
private List<GridCell> GetCellReferences(string item)
|
|
{
|
|
const string sref1 = @"(?<col1>[\.]|[a-zA-Z_]+|""[^""]+"")\s*(?<row1>[\.]|\d+)";
|
|
const string sref2 = @"(?<col2>[\.]|[a-zA-Z_]+|""[^""]+"")\s*(?<row2>[\.]|\d+)";
|
|
|
|
Regex reg = new Regex(
|
|
"(" + sref1 + ":" + sref2 + ")|" +
|
|
"(" + sref1 + ")");
|
|
|
|
MatchCollection mc = reg.Matches(item);
|
|
|
|
if (mc.Count > 0)
|
|
{
|
|
List<GridCell> cells = new List<GridCell>();
|
|
|
|
for (int i = 0; i < mc.Count; i++)
|
|
AddCellRange(_GridPanel, cells, mc[i].Groups);
|
|
|
|
return (cells);
|
|
}
|
|
|
|
return (null);
|
|
}
|
|
|
|
#region AddCellRange
|
|
|
|
private void AddCellRange(
|
|
GridPanel panel, List<GridCell> cells, GroupCollection groups)
|
|
{
|
|
int row1;
|
|
int col1 = GetRowCol(panel, groups["col1"].Value,
|
|
groups["row1"].Value, out row1);
|
|
|
|
int row2 = row1;
|
|
int col2 = col1;
|
|
|
|
if (groups["col2"].Success == true)
|
|
{
|
|
col2 = GetRowCol(panel, groups["col2"].Value,
|
|
groups["row2"].Value, out row2);
|
|
}
|
|
|
|
if (col1 >= 0 && col2 >= 0)
|
|
ProcessRange(panel, cells, col1, row1, col2, row2);
|
|
}
|
|
|
|
#region GetRowCol
|
|
|
|
private int GetRowCol(GridPanel panel,
|
|
string scol, string srow, out int row)
|
|
{
|
|
scol = scol.Replace("\"", "");
|
|
|
|
if (_Cell != null)
|
|
{
|
|
if (srow.Equals(".") == true)
|
|
row = _Cell.GridRow.RowIndex;
|
|
else
|
|
int.TryParse(srow, out row);
|
|
|
|
if (scol.Equals(".") == true)
|
|
return (_Cell.ColumnIndex);
|
|
}
|
|
else
|
|
{
|
|
int.TryParse(srow, out row);
|
|
}
|
|
|
|
int col;
|
|
if (int.TryParse(scol, out col) == true)
|
|
return (col);
|
|
|
|
GridColumn column = panel.Columns[scol];
|
|
|
|
if (column != null)
|
|
return (column.ColumnIndex);
|
|
|
|
return (-1);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ProcessRange
|
|
|
|
private void ProcessRange(GridPanel panel,
|
|
ICollection<GridCell> cells, int col1,
|
|
int row1, int col2, int row2)
|
|
{
|
|
GridContainer cont = _Cell.GridRow.Parent as GridContainer;
|
|
|
|
if (cont != null)
|
|
{
|
|
for (int i = row1; i <= row2; i++)
|
|
{
|
|
for (int j = col1; j <= col2; j++)
|
|
{
|
|
GridCell cell = cont.GetCell(i, j);
|
|
|
|
if (cell != null)
|
|
cells.Add(cell);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
}
|
|
|
|
#region enums
|
|
|
|
#region ETokens
|
|
|
|
///<summary>
|
|
/// ETokens
|
|
///</summary>
|
|
internal enum ETokens
|
|
{
|
|
// Anything before this, is an operand
|
|
|
|
Operator = 512,
|
|
|
|
// Normal operators
|
|
|
|
Negate,
|
|
|
|
LParen,
|
|
RParen,
|
|
|
|
Multiply,
|
|
Divide,
|
|
Mod,
|
|
|
|
Add,
|
|
Subtract,
|
|
|
|
ShiftLeft,
|
|
ShiftRight,
|
|
|
|
BitwiseAnd,
|
|
BitwiseOr,
|
|
BitwiseXor,
|
|
|
|
Function,
|
|
Comma,
|
|
|
|
BadToken,
|
|
|
|
// Anything after this is a function
|
|
|
|
Functions,
|
|
|
|
Avg,
|
|
Ceiling,
|
|
Floor,
|
|
Max,
|
|
Min,
|
|
Round,
|
|
Sum,
|
|
User,
|
|
};
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
} |