Parser: Variables and functions.
This commit is contained in:
@@ -14,7 +14,7 @@ namespace VAR.ExpressionEvaluator.Tests
|
|||||||
operation: (a, b) => (decimal)a + (decimal)b
|
operation: (a, b) => (decimal)a + (decimal)b
|
||||||
);
|
);
|
||||||
|
|
||||||
var result = expr.Eval();
|
var result = expr.Eval(null);
|
||||||
|
|
||||||
Assert.AreEqual(30m, result);
|
Assert.AreEqual(30m, result);
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,7 @@ namespace VAR.ExpressionEvaluator.Tests
|
|||||||
operation: (a, b) => (decimal)a - (decimal)b
|
operation: (a, b) => (decimal)a - (decimal)b
|
||||||
);
|
);
|
||||||
|
|
||||||
var result = expr.Eval();
|
var result = expr.Eval(null);
|
||||||
|
|
||||||
Assert.AreEqual(-10m, result);
|
Assert.AreEqual(-10m, result);
|
||||||
}
|
}
|
||||||
@@ -42,7 +42,7 @@ namespace VAR.ExpressionEvaluator.Tests
|
|||||||
operation: (a, b) => (decimal)a * (decimal)b
|
operation: (a, b) => (decimal)a * (decimal)b
|
||||||
);
|
);
|
||||||
|
|
||||||
var result = expr.Eval();
|
var result = expr.Eval(null);
|
||||||
|
|
||||||
Assert.AreEqual(200m, result);
|
Assert.AreEqual(200m, result);
|
||||||
}
|
}
|
||||||
@@ -56,7 +56,7 @@ namespace VAR.ExpressionEvaluator.Tests
|
|||||||
operation: (a, b) => (decimal)a / (decimal)b
|
operation: (a, b) => (decimal)a / (decimal)b
|
||||||
);
|
);
|
||||||
|
|
||||||
var result = expr.Eval();
|
var result = expr.Eval(null);
|
||||||
|
|
||||||
Assert.AreEqual(2m, result);
|
Assert.AreEqual(2m, result);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,21 +9,21 @@ namespace VAR.ExpressionEvaluator.Tests
|
|||||||
public void ExpressionNumberNode__One()
|
public void ExpressionNumberNode__One()
|
||||||
{
|
{
|
||||||
IExpressionNode node = new ExpressionNumberNode(1);
|
IExpressionNode node = new ExpressionNumberNode(1);
|
||||||
Assert.AreEqual(1m, node.Eval());
|
Assert.AreEqual(1m, node.Eval(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod()]
|
[TestMethod()]
|
||||||
public void ExpressionNumberNode__Two()
|
public void ExpressionNumberNode__Two()
|
||||||
{
|
{
|
||||||
IExpressionNode node = new ExpressionNumberNode(2);
|
IExpressionNode node = new ExpressionNumberNode(2);
|
||||||
Assert.AreEqual(2m, node.Eval());
|
Assert.AreEqual(2m, node.Eval(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod()]
|
[TestMethod()]
|
||||||
public void ExpressionNumberNode__OneHundredDotFortyFive()
|
public void ExpressionNumberNode__OneHundredDotFortyFive()
|
||||||
{
|
{
|
||||||
IExpressionNode node = new ExpressionNumberNode(100.45m);
|
IExpressionNode node = new ExpressionNumberNode(100.45m);
|
||||||
Assert.AreEqual(100.45m, node.Eval());
|
Assert.AreEqual(100.45m, node.Eval(null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,22 +9,21 @@ namespace VAR.ExpressionEvaluator.Tests
|
|||||||
public void ExpressionNumberNode__Hello()
|
public void ExpressionNumberNode__Hello()
|
||||||
{
|
{
|
||||||
IExpressionNode node = new ExpressionStringNode("Hello");
|
IExpressionNode node = new ExpressionStringNode("Hello");
|
||||||
Assert.AreEqual("Hello", node.Eval());
|
Assert.AreEqual("Hello", node.Eval(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod()]
|
[TestMethod()]
|
||||||
public void ExpressionNumberNode__World()
|
public void ExpressionNumberNode__World()
|
||||||
{
|
{
|
||||||
IExpressionNode node = new ExpressionStringNode("World");
|
IExpressionNode node = new ExpressionStringNode("World");
|
||||||
Assert.AreEqual("World", node.Eval());
|
Assert.AreEqual("World", node.Eval(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod()]
|
[TestMethod()]
|
||||||
public void ExpressionNumberNode__Hello_World()
|
public void ExpressionNumberNode__Hello_World()
|
||||||
{
|
{
|
||||||
IExpressionNode node = new ExpressionStringNode("Hello World");
|
IExpressionNode node = new ExpressionStringNode("Hello World");
|
||||||
Assert.AreEqual("Hello World", node.Eval());
|
Assert.AreEqual("Hello World", node.Eval(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace VAR.ExpressionEvaluator.Tests
|
namespace VAR.ExpressionEvaluator.Tests
|
||||||
{
|
{
|
||||||
@@ -145,5 +146,48 @@ namespace VAR.ExpressionEvaluator.Tests
|
|||||||
|
|
||||||
#endregion Multiplication and division
|
#endregion Multiplication and division
|
||||||
|
|
||||||
|
#region Variables
|
||||||
|
|
||||||
|
[TestMethod()]
|
||||||
|
public void ParseString__Var1PlusVar2()
|
||||||
|
{
|
||||||
|
EvaluationContext evaluationContex = new EvaluationContext();
|
||||||
|
evaluationContex.SetVariable("v1", 1m);
|
||||||
|
evaluationContex.SetVariable("v2", 1m);
|
||||||
|
string expression = "v1 + v2";
|
||||||
|
object result = Parser.EvaluateString(expression, evaluationContex);
|
||||||
|
Assert.AreEqual(2m, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod()]
|
||||||
|
public void ParseString__Var1MultiplyVar2()
|
||||||
|
{
|
||||||
|
EvaluationContext evaluationContex = new EvaluationContext();
|
||||||
|
evaluationContex.SetVariable("v1", 10m);
|
||||||
|
evaluationContex.SetVariable("v2", 5m);
|
||||||
|
string expression = "v1 * v2";
|
||||||
|
object result = Parser.EvaluateString(expression, evaluationContex);
|
||||||
|
Assert.AreEqual(50m, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Variables
|
||||||
|
|
||||||
|
#region Funcitions
|
||||||
|
|
||||||
|
[TestMethod()]
|
||||||
|
public void ParseString__MaxFunction()
|
||||||
|
{
|
||||||
|
EvaluationContext evaluationContex = new EvaluationContext();
|
||||||
|
evaluationContex.SetFunction("max", (parameters) =>
|
||||||
|
{
|
||||||
|
return parameters.Max(p => (decimal)p);
|
||||||
|
});
|
||||||
|
string expression = "max(1,2,10,5)";
|
||||||
|
object result = Parser.EvaluateString(expression, evaluationContex);
|
||||||
|
Assert.AreEqual(10m, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Functions
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,17 +96,17 @@ namespace VAR.ExpressionEvaluator.Tests
|
|||||||
var t = new Tokenizer(new StringReader(testString));
|
var t = new Tokenizer(new StringReader(testString));
|
||||||
|
|
||||||
// "null"
|
// "null"
|
||||||
Assert.AreEqual(t.Token, Token.Keyword);
|
Assert.AreEqual(t.Token, Token.Identifier);
|
||||||
Assert.AreEqual(t.Text, "null");
|
Assert.AreEqual(t.Text, "null");
|
||||||
t.NextToken();
|
t.NextToken();
|
||||||
|
|
||||||
// "true"
|
// "true"
|
||||||
Assert.AreEqual(t.Token, Token.Keyword);
|
Assert.AreEqual(t.Token, Token.Identifier);
|
||||||
Assert.AreEqual(t.Text, "true");
|
Assert.AreEqual(t.Text, "true");
|
||||||
t.NextToken();
|
t.NextToken();
|
||||||
|
|
||||||
// "false"
|
// "false"
|
||||||
Assert.AreEqual(t.Token, Token.Keyword);
|
Assert.AreEqual(t.Token, Token.Identifier);
|
||||||
Assert.AreEqual(t.Text, "false");
|
Assert.AreEqual(t.Text, "false");
|
||||||
t.NextToken();
|
t.NextToken();
|
||||||
|
|
||||||
@@ -168,7 +168,7 @@ namespace VAR.ExpressionEvaluator.Tests
|
|||||||
t.NextToken();
|
t.NextToken();
|
||||||
|
|
||||||
// "false"
|
// "false"
|
||||||
Assert.AreEqual(t.Token, Token.Keyword);
|
Assert.AreEqual(t.Token, Token.Identifier);
|
||||||
Assert.AreEqual(t.Text, "false");
|
Assert.AreEqual(t.Text, "false");
|
||||||
t.NextToken();
|
t.NextToken();
|
||||||
|
|
||||||
|
|||||||
47
VAR.ExpressionEvaluator/EvaluationContext.cs
Normal file
47
VAR.ExpressionEvaluator/EvaluationContext.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace VAR.ExpressionEvaluator
|
||||||
|
{
|
||||||
|
public class EvaluationContext : IEvaluationContext
|
||||||
|
{
|
||||||
|
private Dictionary<string, Func<object[], object>> _functions = new Dictionary<string, Func<object[], object>>();
|
||||||
|
private Dictionary<string, object> _variables = new Dictionary<string, object>();
|
||||||
|
|
||||||
|
public void CleanFunctions()
|
||||||
|
{
|
||||||
|
_functions.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetFunction(string name, Func<object[], object> function)
|
||||||
|
{
|
||||||
|
_functions.Add(name, function);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Func<object[], object> GetFunction(string name)
|
||||||
|
{
|
||||||
|
return _functions[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CleanVariables()
|
||||||
|
{
|
||||||
|
_variables.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetVariable(string name, object value)
|
||||||
|
{
|
||||||
|
_variables.Add(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public object GetVariable(string name)
|
||||||
|
{
|
||||||
|
return _variables[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clean()
|
||||||
|
{
|
||||||
|
CleanFunctions();
|
||||||
|
CleanVariables();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,10 +15,10 @@ namespace VAR.ExpressionEvaluator
|
|||||||
_operation = operation;
|
_operation = operation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Eval()
|
public object Eval(IEvaluationContext evaluationContext)
|
||||||
{
|
{
|
||||||
object leftValue = _leftNode.Eval();
|
object leftValue = _leftNode.Eval(evaluationContext);
|
||||||
object rightValue = _rightNode.Eval();
|
object rightValue = _rightNode.Eval(evaluationContext);
|
||||||
|
|
||||||
object result = _operation(leftValue, rightValue);
|
object result = _operation(leftValue, rightValue);
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
28
VAR.ExpressionEvaluator/ExpressionFunctionNode.cs
Normal file
28
VAR.ExpressionEvaluator/ExpressionFunctionNode.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace VAR.ExpressionEvaluator
|
||||||
|
{
|
||||||
|
public class ExpressionFunctionNode : IExpressionNode
|
||||||
|
{
|
||||||
|
private readonly string _name;
|
||||||
|
private IExpressionNode[] _paramNodes;
|
||||||
|
|
||||||
|
public ExpressionFunctionNode(string name, IExpressionNode[] paramNodes)
|
||||||
|
{
|
||||||
|
_name = name;
|
||||||
|
_paramNodes = paramNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object Eval(IEvaluationContext evaluationContext)
|
||||||
|
{
|
||||||
|
object[] paramValues = _paramNodes.Select(p => p.Eval(evaluationContext)).ToArray();
|
||||||
|
|
||||||
|
Func<object[], object> func = evaluationContext.GetFunction(_name);
|
||||||
|
|
||||||
|
object result = func(paramValues);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
_number = number;
|
_number = number;
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Eval()
|
public object Eval(IEvaluationContext evaluationContext)
|
||||||
{
|
{
|
||||||
return _number;
|
return _number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
_string = str;
|
_string = str;
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Eval()
|
public object Eval(IEvaluationContext evaluationContext)
|
||||||
{
|
{
|
||||||
return _string;
|
return _string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ namespace VAR.ExpressionEvaluator
|
|||||||
_operation = operation;
|
_operation = operation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Eval()
|
public object Eval(IEvaluationContext evaluationContext)
|
||||||
{
|
{
|
||||||
object value = _node.Eval();
|
object value = _node.Eval(evaluationContext);
|
||||||
|
|
||||||
object result = _operation(value);
|
object result = _operation(value);
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
17
VAR.ExpressionEvaluator/ExpressionVariableNode.cs
Normal file
17
VAR.ExpressionEvaluator/ExpressionVariableNode.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
namespace VAR.ExpressionEvaluator
|
||||||
|
{
|
||||||
|
public class ExpressionVariableNode : IExpressionNode
|
||||||
|
{
|
||||||
|
private readonly string _name;
|
||||||
|
|
||||||
|
public ExpressionVariableNode(string name)
|
||||||
|
{
|
||||||
|
_name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object Eval(IEvaluationContext evaluationContext)
|
||||||
|
{
|
||||||
|
return evaluationContext.GetVariable(_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
VAR.ExpressionEvaluator/IEvaluationContext.cs
Normal file
10
VAR.ExpressionEvaluator/IEvaluationContext.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace VAR.ExpressionEvaluator
|
||||||
|
{
|
||||||
|
public interface IEvaluationContext
|
||||||
|
{
|
||||||
|
object GetVariable(string name);
|
||||||
|
Func<object[], object> GetFunction(string name);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,6 @@
|
|||||||
{
|
{
|
||||||
public interface IExpressionNode
|
public interface IExpressionNode
|
||||||
{
|
{
|
||||||
object Eval();
|
object Eval(IEvaluationContext evaluationContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace VAR.ExpressionEvaluator
|
namespace VAR.ExpressionEvaluator
|
||||||
@@ -101,7 +102,7 @@ namespace VAR.ExpressionEvaluator
|
|||||||
if (_tokenizer.Token == Token.ParenthesisStart)
|
if (_tokenizer.Token == Token.ParenthesisStart)
|
||||||
{
|
{
|
||||||
_tokenizer.NextToken();
|
_tokenizer.NextToken();
|
||||||
var node = ParsePlusAndMinus();
|
IExpressionNode node = ParsePlusAndMinus();
|
||||||
if (_tokenizer.Token != Token.ParenthesisEnd)
|
if (_tokenizer.Token != Token.ParenthesisEnd)
|
||||||
{
|
{
|
||||||
throw new Exception("Missing close parenthesis");
|
throw new Exception("Missing close parenthesis");
|
||||||
@@ -110,6 +111,40 @@ namespace VAR.ExpressionEvaluator
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_tokenizer.Token == Token.Identifier)
|
||||||
|
{
|
||||||
|
string identifier = _tokenizer.Text;
|
||||||
|
_tokenizer.NextToken();
|
||||||
|
if (_tokenizer.Token != Token.ParenthesisStart)
|
||||||
|
{
|
||||||
|
IExpressionNode node = new ExpressionVariableNode(identifier);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_tokenizer.NextToken();
|
||||||
|
var parameters = new List<IExpressionNode>();
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
parameters.Add(ParsePlusAndMinus());
|
||||||
|
if (_tokenizer.Token == Token.Comma)
|
||||||
|
{
|
||||||
|
_tokenizer.NextToken();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (_tokenizer.Token != Token.ParenthesisEnd)
|
||||||
|
{
|
||||||
|
throw new Exception("Missing close parenthesis");
|
||||||
|
}
|
||||||
|
_tokenizer.NextToken();
|
||||||
|
|
||||||
|
IExpressionNode node = new ExpressionFunctionNode(identifier, parameters.ToArray());
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
throw new Exception(string.Format("Unexpected token: {0}", _tokenizer.Token.ToString()));
|
throw new Exception(string.Format("Unexpected token: {0}", _tokenizer.Token.ToString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,10 +156,10 @@ namespace VAR.ExpressionEvaluator
|
|||||||
return parser.ParseExpression();
|
return parser.ParseExpression();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static object EvaluateString(string str)
|
public static object EvaluateString(string str, IEvaluationContext evaluationContext = null)
|
||||||
{
|
{
|
||||||
IExpressionNode node = ParseString(str);
|
IExpressionNode node = ParseString(str);
|
||||||
return node.Eval();
|
return node.Eval(evaluationContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,8 @@
|
|||||||
LessOrEqualThan,
|
LessOrEqualThan,
|
||||||
ParenthesisStart,
|
ParenthesisStart,
|
||||||
ParenthesisEnd,
|
ParenthesisEnd,
|
||||||
Keyword,
|
Comma,
|
||||||
|
Identifier,
|
||||||
String,
|
String,
|
||||||
Number,
|
Number,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,9 +134,14 @@ namespace VAR.ExpressionEvaluator
|
|||||||
_currentToken = Token.LessOrEqualThan;
|
_currentToken = Token.LessOrEqualThan;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case ',':
|
||||||
|
NextChar();
|
||||||
|
_currentToken = Token.Comma;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keywords
|
// Identifier
|
||||||
if (char.IsLetter(_currentChar))
|
if (char.IsLetter(_currentChar))
|
||||||
{
|
{
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
@@ -147,7 +152,7 @@ namespace VAR.ExpressionEvaluator
|
|||||||
if (_currentChar == '\0') { break; }
|
if (_currentChar == '\0') { break; }
|
||||||
}
|
}
|
||||||
_text = sb.ToString();
|
_text = sb.ToString();
|
||||||
_currentToken = Token.Keyword;
|
_currentToken = Token.Identifier;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,9 @@
|
|||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="EvaluationContext.cs" />
|
||||||
<Compile Include="ExpressionDivisionNode.cs" />
|
<Compile Include="ExpressionDivisionNode.cs" />
|
||||||
|
<Compile Include="ExpressionFunctionNode.cs" />
|
||||||
<Compile Include="ExpressionMultiplyNode.cs" />
|
<Compile Include="ExpressionMultiplyNode.cs" />
|
||||||
<Compile Include="ExpressionUnaryNode.cs" />
|
<Compile Include="ExpressionUnaryNode.cs" />
|
||||||
<Compile Include="ExpressionBinaryNode.cs" />
|
<Compile Include="ExpressionBinaryNode.cs" />
|
||||||
@@ -50,6 +52,8 @@
|
|||||||
<Compile Include="ExpressionPlusNode.cs" />
|
<Compile Include="ExpressionPlusNode.cs" />
|
||||||
<Compile Include="ExpressionStringNode.cs" />
|
<Compile Include="ExpressionStringNode.cs" />
|
||||||
<Compile Include="ExpressionNumberNode.cs" />
|
<Compile Include="ExpressionNumberNode.cs" />
|
||||||
|
<Compile Include="ExpressionVariableNode.cs" />
|
||||||
|
<Compile Include="IEvaluationContext.cs" />
|
||||||
<Compile Include="IExpressionNode.cs" />
|
<Compile Include="IExpressionNode.cs" />
|
||||||
<Compile Include="ITokenizer.cs" />
|
<Compile Include="ITokenizer.cs" />
|
||||||
<Compile Include="Parser.cs" />
|
<Compile Include="Parser.cs" />
|
||||||
|
|||||||
Reference in New Issue
Block a user