using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace DevComponents.DotNetBar.SuperGrid
{
///
/// Expression evaluator
///
public class EEval
{
#region Private data
private int _TCount;
private int _PCount;
private ETokens[] _PTokens = new ETokens[10];
private List _PfTokens = new List();
private GridCell _Cell;
private GridPanel _GridPanel;
private List _UsedCells;
private string _Source;
private List _StringPool = new List();
private object _Tag;
#endregion
///
/// Expression evaluator constructor
///
///Associated GridPanel
///'Excel-like' expression (eg. =d4+d5)
public EEval(GridPanel gridPanel, string source)
: this(null, source, new List())
{
_GridPanel = gridPanel;
}
internal EEval(GridCell cell, string source)
: this(cell, source, new List())
{
}
private EEval(GridCell cell, string source, List usedCells)
{
_Cell = cell;
_UsedCells = usedCells;
if (_Cell != null)
_GridPanel = cell.GridPanel;
Source = source;
}
#region Public properties
#region Cell
///
/// Gets the associated Grid Cell, if any
///
public GridCell Cell
{
get { return (_Cell); }
}
#endregion
#region GridPanel
///
/// Gets the associated Grid Panel
///
public GridPanel GridPanel
{
get { return (_GridPanel); }
}
#endregion
#region Source
///
/// Gets or sets the expression source
///
public string Source
{
get { return (_Source); }
set
{
_Source = value;
Tokenize(value);
}
}
#endregion
#region Tag
///
/// Gets or sets user-defined data associated with the object
///
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
///
/// Main expression entry routine
///
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
///
/// Handles the ^ operator
///
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
///
/// Handles the Ampersand operator
///
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
///
/// Handles the shift left and right operators
///
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
///
/// Handles %, *, and / operators
///
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
///
/// Handles %, *, and / operators
///
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
///
/// Handles factor processing
///
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
///
/// Handles function parsing
///
///
///
///
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
///
/// Determines whether the parsed string
/// is a one of our function keywords
///
///
///
///
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
///
/// Handles function parameters
///
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
///
/// Gets the next parsed token
///
///
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
///
/// Saves the given token for future use
///
///
private void PutToken(ETokens t)
{
_PTokens[_PCount++] = t;
}
#endregion
#endregion
#region Evaluate
///
/// Evaluates the previously tokenized code
///
///
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
///
/// Evaluates the current function
///
///
///
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
///
/// Calculates the average of the given set of values
///
///
///
private double Avg(IEnumerable