diff --git a/VAR.ExpressionEvaluator.Tests/ParserTests.cs b/VAR.ExpressionEvaluator.Tests/ParserTests.cs index 049a2aa..d4d9000 100644 --- a/VAR.ExpressionEvaluator.Tests/ParserTests.cs +++ b/VAR.ExpressionEvaluator.Tests/ParserTests.cs @@ -1,4 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; using System.Linq; namespace VAR.ExpressionEvaluator.Tests @@ -152,8 +153,8 @@ namespace VAR.ExpressionEvaluator.Tests public void Variables__Var1PlusVar2() { EvaluationContext evaluationContex = new EvaluationContext(); - evaluationContex.SetVariable("v1", 1m); - evaluationContex.SetVariable("v2", 1m); + evaluationContex.SetVariable("v1", 1); + evaluationContex.SetVariable("v2", 1); string expression = "v1 + v2"; object result = Parser.EvaluateString(expression, evaluationContex); Assert.AreEqual(2m, result); @@ -163,8 +164,8 @@ namespace VAR.ExpressionEvaluator.Tests public void Variables__Var1MultiplyVar2() { EvaluationContext evaluationContex = new EvaluationContext(); - evaluationContex.SetVariable("v1", 10m); - evaluationContex.SetVariable("v2", 5m); + evaluationContex.SetVariable("v1", 10); + evaluationContex.SetVariable("v2", 5); string expression = "v1 * v2"; object result = Parser.EvaluateString(expression, evaluationContex); Assert.AreEqual(50m, result); @@ -189,5 +190,71 @@ namespace VAR.ExpressionEvaluator.Tests #endregion Functions + #region Strings + + [TestMethod()] + public void Strings__Contatenate_Hello_World() + { + string expression = "\"Hello\" + ' ' +\"World\""; + object result = Parser.EvaluateString(expression); + Assert.AreEqual("Hello World", result); + } + + [TestMethod()] + public void Strings__Contatenate_Hello_World_WithVariables() + { + EvaluationContext evaluationContex = new EvaluationContext(); + evaluationContex.SetVariable("v1", "Hello"); + evaluationContex.SetVariable("v2", " "); + evaluationContex.SetVariable("v3", "World"); + string expression = "v1 + v2 + v3"; + object result = Parser.EvaluateString(expression, evaluationContex); + Assert.AreEqual("Hello World", result); + } + + [TestMethod()] + public void Strings__Fail_Minus() + { + string expression = "'Hello' - 'World'"; + try + { + object result = Parser.EvaluateString(expression); + Assert.Fail(); + } + catch (Exception) + { + } + } + + [TestMethod()] + public void Strings__Fail_Multiply() + { + string expression = "'Hello' * 'World'"; + try + { + object result = Parser.EvaluateString(expression); + Assert.Fail(); + } + catch (Exception) + { + } + } + + [TestMethod()] + public void Strings__Fail_Division() + { + string expression = "'Hello' / 'World'"; + try + { + object result = Parser.EvaluateString(expression); + Assert.Fail(); + } + catch (Exception) + { + } + } + + #endregion Strings + } } \ No newline at end of file diff --git a/VAR.ExpressionEvaluator/ExpressionBooleanNode.cs b/VAR.ExpressionEvaluator/ExpressionBooleanNode.cs new file mode 100644 index 0000000..eea9494 --- /dev/null +++ b/VAR.ExpressionEvaluator/ExpressionBooleanNode.cs @@ -0,0 +1,17 @@ +namespace VAR.ExpressionEvaluator +{ + public class ExpressionBooleanNode : IExpressionNode + { + private bool _value; + + public ExpressionBooleanNode(bool value) + { + _value = value; + } + + public object Eval(IEvaluationContext evaluationContext) + { + return _value; + } + } +} diff --git a/VAR.ExpressionEvaluator/ExpressionDivisionNode.cs b/VAR.ExpressionEvaluator/ExpressionDivisionNode.cs index babf853..5385222 100644 --- a/VAR.ExpressionEvaluator/ExpressionDivisionNode.cs +++ b/VAR.ExpressionEvaluator/ExpressionDivisionNode.cs @@ -1,4 +1,6 @@ -namespace VAR.ExpressionEvaluator +using System; + +namespace VAR.ExpressionEvaluator { public class ExpressionDivisionNode : ExpressionBinaryNode { @@ -9,6 +11,27 @@ private static object DivisionOp(object leftValue, object rightValue) { + if (leftValue is string) + { + if (decimal.TryParse((string)leftValue, out decimal dec) == false) + { + throw new Exception(string.Format("Can't convert to decimal string value \"{0}\"", (string)leftValue)); + } + leftValue = dec; + } + if (rightValue is string) + { + if (decimal.TryParse((string)rightValue, out decimal dec) == false) + { + throw new Exception(string.Format("Can't convert to decimal string value \"{0}\"", (string)rightValue)); + } + rightValue = dec; + } + + if ((leftValue is decimal) == false || (rightValue is decimal) == false) + { + throw new Exception("Can't divive non decimal values"); + } return (decimal)leftValue / (decimal)rightValue; } } diff --git a/VAR.ExpressionEvaluator/ExpressionMinusNode.cs b/VAR.ExpressionEvaluator/ExpressionMinusNode.cs index 30e0c81..76ef5c1 100644 --- a/VAR.ExpressionEvaluator/ExpressionMinusNode.cs +++ b/VAR.ExpressionEvaluator/ExpressionMinusNode.cs @@ -1,4 +1,6 @@ -namespace VAR.ExpressionEvaluator +using System; + +namespace VAR.ExpressionEvaluator { public class ExpressionMinusNode : ExpressionBinaryNode { @@ -9,6 +11,27 @@ private static object MinusOp(object leftValue, object rightValue) { + if (leftValue is string) + { + if (decimal.TryParse((string)leftValue, out decimal dec) == false) + { + throw new Exception(string.Format("Can't convert to decimal string value \"{0}\"", (string)leftValue)); + } + leftValue = dec; + } + if (rightValue is string) + { + if (decimal.TryParse((string)rightValue, out decimal dec) == false) + { + throw new Exception(string.Format("Can't convert to decimal string value \"{0}\"", (string)rightValue)); + } + rightValue = dec; + } + + if ((leftValue is decimal) == false || (rightValue is decimal) == false) + { + throw new Exception("Can't substract non decimal values"); + } return (decimal)leftValue - (decimal)rightValue; } } diff --git a/VAR.ExpressionEvaluator/ExpressionMultiplyNode.cs b/VAR.ExpressionEvaluator/ExpressionMultiplyNode.cs index fed99db..e581840 100644 --- a/VAR.ExpressionEvaluator/ExpressionMultiplyNode.cs +++ b/VAR.ExpressionEvaluator/ExpressionMultiplyNode.cs @@ -1,4 +1,6 @@ -namespace VAR.ExpressionEvaluator +using System; + +namespace VAR.ExpressionEvaluator { public class ExpressionMultiplyNode : ExpressionBinaryNode { @@ -9,6 +11,27 @@ private static object MultiplyOp(object leftValue, object rightValue) { + if (leftValue is string) + { + if (decimal.TryParse((string)leftValue, out decimal dec) == false) + { + throw new Exception(string.Format("Can't convert to decimal string value \"{0}\"", (string)leftValue)); + } + leftValue = dec; + } + if (rightValue is string) + { + if (decimal.TryParse((string)rightValue, out decimal dec) == false) + { + throw new Exception(string.Format("Can't convert to decimal string value \"{0}\"", (string)rightValue)); + } + rightValue = dec; + } + + if ((leftValue is decimal) == false || (rightValue is decimal) == false) + { + throw new Exception("Can't multiply non decimal values"); + } return (decimal)leftValue * (decimal)rightValue; } } diff --git a/VAR.ExpressionEvaluator/ExpressionNumberNegateNode.cs b/VAR.ExpressionEvaluator/ExpressionNumberNegateNode.cs index 4dc3aef..b76f5eb 100644 --- a/VAR.ExpressionEvaluator/ExpressionNumberNegateNode.cs +++ b/VAR.ExpressionEvaluator/ExpressionNumberNegateNode.cs @@ -1,4 +1,6 @@ -namespace VAR.ExpressionEvaluator +using System; + +namespace VAR.ExpressionEvaluator { public class ExpressionNumberNegateNode : ExpressionUnaryNode { @@ -9,7 +11,20 @@ private static object NumberNegateOp(object value) { - return - (decimal)value; + if (value is string) + { + if (decimal.TryParse((string)value, out decimal dec) == false) + { + throw new Exception(string.Format("Can't convert to decimal string value \"{0}\"", (string)value)); + } + value = dec; + } + + if ((value is decimal) == false) + { + throw new Exception("Can't negate non decimal values"); + } + return -(decimal)value; } } } diff --git a/VAR.ExpressionEvaluator/ExpressionPlusNode.cs b/VAR.ExpressionEvaluator/ExpressionPlusNode.cs index 9adb6c3..91430cc 100644 --- a/VAR.ExpressionEvaluator/ExpressionPlusNode.cs +++ b/VAR.ExpressionEvaluator/ExpressionPlusNode.cs @@ -1,4 +1,6 @@ -namespace VAR.ExpressionEvaluator +using System; + +namespace VAR.ExpressionEvaluator { public class ExpressionPlusNode : ExpressionBinaryNode { @@ -9,6 +11,15 @@ private static object PlusOp(object leftValue, object rightValue) { + if (leftValue is string || rightValue is string) + { + return string.Concat(Convert.ToString(leftValue), Convert.ToString(rightValue)); + } + + if ((leftValue is decimal) == false || (rightValue is decimal) == false) + { + throw new Exception("Can't sum non decimal values"); + } return (decimal)leftValue + (decimal)rightValue; } } diff --git a/VAR.ExpressionEvaluator/Parser.cs b/VAR.ExpressionEvaluator/Parser.cs index b7adf36..d764be0 100644 --- a/VAR.ExpressionEvaluator/Parser.cs +++ b/VAR.ExpressionEvaluator/Parser.cs @@ -99,20 +99,27 @@ namespace VAR.ExpressionEvaluator return node; } - if (_tokenizer.Token == Token.ParenthesisStart) + if (_tokenizer.Token == Token.String) { - _tokenizer.NextToken(); - IExpressionNode node = ParsePlusAndMinus(); - if (_tokenizer.Token != Token.ParenthesisEnd) - { - throw new Exception("Missing close parenthesis"); - } + IExpressionNode node = new ExpressionStringNode(_tokenizer.Text); _tokenizer.NextToken(); return node; } if (_tokenizer.Token == Token.Identifier) { + string identifierToLower = _tokenizer.Text.ToLower(); + if (identifierToLower == "true") + { + _tokenizer.NextToken(); + return new ExpressionBooleanNode(true); + } + if (identifierToLower == "false") + { + _tokenizer.NextToken(); + return new ExpressionBooleanNode(false); + } + string identifier = _tokenizer.Text; _tokenizer.NextToken(); if (_tokenizer.Token != Token.ParenthesisStart) @@ -145,6 +152,18 @@ namespace VAR.ExpressionEvaluator } } + if (_tokenizer.Token == Token.ParenthesisStart) + { + _tokenizer.NextToken(); + IExpressionNode node = ParsePlusAndMinus(); + if (_tokenizer.Token != Token.ParenthesisEnd) + { + throw new Exception("Missing close parenthesis"); + } + _tokenizer.NextToken(); + return node; + } + throw new Exception(string.Format("Unexpected token: {0}", _tokenizer.Token.ToString())); } diff --git a/VAR.ExpressionEvaluator/VAR.ExpressionEvaluator.csproj b/VAR.ExpressionEvaluator/VAR.ExpressionEvaluator.csproj index 1722541..e123fad 100644 --- a/VAR.ExpressionEvaluator/VAR.ExpressionEvaluator.csproj +++ b/VAR.ExpressionEvaluator/VAR.ExpressionEvaluator.csproj @@ -42,6 +42,7 @@ +