Parser: Variables and functions.
This commit is contained in:
@@ -14,7 +14,7 @@ namespace VAR.ExpressionEvaluator.Tests
|
||||
operation: (a, b) => (decimal)a + (decimal)b
|
||||
);
|
||||
|
||||
var result = expr.Eval();
|
||||
var result = expr.Eval(null);
|
||||
|
||||
Assert.AreEqual(30m, result);
|
||||
}
|
||||
@@ -28,7 +28,7 @@ namespace VAR.ExpressionEvaluator.Tests
|
||||
operation: (a, b) => (decimal)a - (decimal)b
|
||||
);
|
||||
|
||||
var result = expr.Eval();
|
||||
var result = expr.Eval(null);
|
||||
|
||||
Assert.AreEqual(-10m, result);
|
||||
}
|
||||
@@ -42,7 +42,7 @@ namespace VAR.ExpressionEvaluator.Tests
|
||||
operation: (a, b) => (decimal)a * (decimal)b
|
||||
);
|
||||
|
||||
var result = expr.Eval();
|
||||
var result = expr.Eval(null);
|
||||
|
||||
Assert.AreEqual(200m, result);
|
||||
}
|
||||
@@ -56,7 +56,7 @@ namespace VAR.ExpressionEvaluator.Tests
|
||||
operation: (a, b) => (decimal)a / (decimal)b
|
||||
);
|
||||
|
||||
var result = expr.Eval();
|
||||
var result = expr.Eval(null);
|
||||
|
||||
Assert.AreEqual(2m, result);
|
||||
}
|
||||
|
||||
@@ -9,21 +9,21 @@ namespace VAR.ExpressionEvaluator.Tests
|
||||
public void ExpressionNumberNode__One()
|
||||
{
|
||||
IExpressionNode node = new ExpressionNumberNode(1);
|
||||
Assert.AreEqual(1m, node.Eval());
|
||||
Assert.AreEqual(1m, node.Eval(null));
|
||||
}
|
||||
|
||||
[TestMethod()]
|
||||
public void ExpressionNumberNode__Two()
|
||||
{
|
||||
IExpressionNode node = new ExpressionNumberNode(2);
|
||||
Assert.AreEqual(2m, node.Eval());
|
||||
Assert.AreEqual(2m, node.Eval(null));
|
||||
}
|
||||
|
||||
[TestMethod()]
|
||||
public void ExpressionNumberNode__OneHundredDotFortyFive()
|
||||
{
|
||||
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()
|
||||
{
|
||||
IExpressionNode node = new ExpressionStringNode("Hello");
|
||||
Assert.AreEqual("Hello", node.Eval());
|
||||
Assert.AreEqual("Hello", node.Eval(null));
|
||||
}
|
||||
|
||||
[TestMethod()]
|
||||
public void ExpressionNumberNode__World()
|
||||
{
|
||||
IExpressionNode node = new ExpressionStringNode("World");
|
||||
Assert.AreEqual("World", node.Eval());
|
||||
Assert.AreEqual("World", node.Eval(null));
|
||||
}
|
||||
|
||||
[TestMethod()]
|
||||
public void ExpressionNumberNode__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 System.Linq;
|
||||
|
||||
namespace VAR.ExpressionEvaluator.Tests
|
||||
{
|
||||
@@ -145,5 +146,48 @@ namespace VAR.ExpressionEvaluator.Tests
|
||||
|
||||
#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));
|
||||
|
||||
// "null"
|
||||
Assert.AreEqual(t.Token, Token.Keyword);
|
||||
Assert.AreEqual(t.Token, Token.Identifier);
|
||||
Assert.AreEqual(t.Text, "null");
|
||||
t.NextToken();
|
||||
|
||||
// "true"
|
||||
Assert.AreEqual(t.Token, Token.Keyword);
|
||||
Assert.AreEqual(t.Token, Token.Identifier);
|
||||
Assert.AreEqual(t.Text, "true");
|
||||
t.NextToken();
|
||||
|
||||
// "false"
|
||||
Assert.AreEqual(t.Token, Token.Keyword);
|
||||
Assert.AreEqual(t.Token, Token.Identifier);
|
||||
Assert.AreEqual(t.Text, "false");
|
||||
t.NextToken();
|
||||
|
||||
@@ -168,7 +168,7 @@ namespace VAR.ExpressionEvaluator.Tests
|
||||
t.NextToken();
|
||||
|
||||
// "false"
|
||||
Assert.AreEqual(t.Token, Token.Keyword);
|
||||
Assert.AreEqual(t.Token, Token.Identifier);
|
||||
Assert.AreEqual(t.Text, "false");
|
||||
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;
|
||||
}
|
||||
|
||||
public object Eval()
|
||||
public object Eval(IEvaluationContext evaluationContext)
|
||||
{
|
||||
object leftValue = _leftNode.Eval();
|
||||
object rightValue = _rightNode.Eval();
|
||||
object leftValue = _leftNode.Eval(evaluationContext);
|
||||
object rightValue = _rightNode.Eval(evaluationContext);
|
||||
|
||||
object result = _operation(leftValue, rightValue);
|
||||
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;
|
||||
}
|
||||
|
||||
public object Eval()
|
||||
public object Eval(IEvaluationContext evaluationContext)
|
||||
{
|
||||
return _number;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
_string = str;
|
||||
}
|
||||
|
||||
public object Eval()
|
||||
public object Eval(IEvaluationContext evaluationContext)
|
||||
{
|
||||
return _string;
|
||||
}
|
||||
|
||||
@@ -13,9 +13,9 @@ namespace VAR.ExpressionEvaluator
|
||||
_operation = operation;
|
||||
}
|
||||
|
||||
public object Eval()
|
||||
public object Eval(IEvaluationContext evaluationContext)
|
||||
{
|
||||
object value = _node.Eval();
|
||||
object value = _node.Eval(evaluationContext);
|
||||
|
||||
object result = _operation(value);
|
||||
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
|
||||
{
|
||||
object Eval();
|
||||
object Eval(IEvaluationContext evaluationContext);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace VAR.ExpressionEvaluator
|
||||
@@ -101,7 +102,7 @@ namespace VAR.ExpressionEvaluator
|
||||
if (_tokenizer.Token == Token.ParenthesisStart)
|
||||
{
|
||||
_tokenizer.NextToken();
|
||||
var node = ParsePlusAndMinus();
|
||||
IExpressionNode node = ParsePlusAndMinus();
|
||||
if (_tokenizer.Token != Token.ParenthesisEnd)
|
||||
{
|
||||
throw new Exception("Missing close parenthesis");
|
||||
@@ -110,6 +111,40 @@ namespace VAR.ExpressionEvaluator
|
||||
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()));
|
||||
}
|
||||
|
||||
@@ -121,10 +156,10 @@ namespace VAR.ExpressionEvaluator
|
||||
return parser.ParseExpression();
|
||||
}
|
||||
|
||||
public static object EvaluateString(string str)
|
||||
public static object EvaluateString(string str, IEvaluationContext evaluationContext = null)
|
||||
{
|
||||
IExpressionNode node = ParseString(str);
|
||||
return node.Eval();
|
||||
return node.Eval(evaluationContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
LessOrEqualThan,
|
||||
ParenthesisStart,
|
||||
ParenthesisEnd,
|
||||
Keyword,
|
||||
Comma,
|
||||
Identifier,
|
||||
String,
|
||||
Number,
|
||||
}
|
||||
|
||||
@@ -134,9 +134,14 @@ namespace VAR.ExpressionEvaluator
|
||||
_currentToken = Token.LessOrEqualThan;
|
||||
}
|
||||
return;
|
||||
|
||||
case ',':
|
||||
NextChar();
|
||||
_currentToken = Token.Comma;
|
||||
return;
|
||||
}
|
||||
|
||||
// Keywords
|
||||
// Identifier
|
||||
if (char.IsLetter(_currentChar))
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
@@ -147,7 +152,7 @@ namespace VAR.ExpressionEvaluator
|
||||
if (_currentChar == '\0') { break; }
|
||||
}
|
||||
_text = sb.ToString();
|
||||
_currentToken = Token.Keyword;
|
||||
_currentToken = Token.Identifier;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,9 @@
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="EvaluationContext.cs" />
|
||||
<Compile Include="ExpressionDivisionNode.cs" />
|
||||
<Compile Include="ExpressionFunctionNode.cs" />
|
||||
<Compile Include="ExpressionMultiplyNode.cs" />
|
||||
<Compile Include="ExpressionUnaryNode.cs" />
|
||||
<Compile Include="ExpressionBinaryNode.cs" />
|
||||
@@ -50,6 +52,8 @@
|
||||
<Compile Include="ExpressionPlusNode.cs" />
|
||||
<Compile Include="ExpressionStringNode.cs" />
|
||||
<Compile Include="ExpressionNumberNode.cs" />
|
||||
<Compile Include="ExpressionVariableNode.cs" />
|
||||
<Compile Include="IEvaluationContext.cs" />
|
||||
<Compile Include="IExpressionNode.cs" />
|
||||
<Compile Include="ITokenizer.cs" />
|
||||
<Compile Include="Parser.cs" />
|
||||
|
||||
Reference in New Issue
Block a user