diff --git a/README.md b/README.md
index dbe660ff..36bc26f9 100644
--- a/README.md
+++ b/README.md
@@ -216,9 +216,14 @@ Statements can be written using a subset of the C# syntax. Here you can find a l
Conditional | ?: |
+
+ Assignment | = |
+
+Operators precedence is respected following [C# rules (Operator precedence and associativity)](http://msdn.microsoft.com/en-us/library/aa691323(v=vs.71).aspx).
+
### Literals
@@ -397,6 +402,10 @@ For one reason or another none of these projects exactly fit my needs so I decid
## Release notes
+- 1.1.0
+
+ - Added support for equal assignement operator (=
). [#24](https://github.com/davideicardi/DynamicExpresso/issues/24)
+
- 1.0.0
- Added Interpreter.DetectIdentifiers method to discovery identifiers (variables, parameters, types) used in expression before parsing it. ([#23](https://github.com/davideicardi/DynamicExpresso/issues/23))
diff --git a/src/DynamicExpresso.Core/Parsing/Parser.cs b/src/DynamicExpresso.Core/Parsing/Parser.cs
index 13f72714..5be682b2 100644
--- a/src/DynamicExpresso.Core/Parsing/Parser.cs
+++ b/src/DynamicExpresso.Core/Parsing/Parser.cs
@@ -79,9 +79,26 @@ Expression ParseExpressionSegment(Type returnType)
Expression ParseExpressionSegment()
{
// The following methods respect the operator precedence as defined in
+ // MSDN C# "Operator precedence and associativity"
// http://msdn.microsoft.com/en-us/library/aa691323(v=vs.71).aspx
- return ParseConditional();
+ return ParseAssignement();
+ }
+
+ // = operator
+ Expression ParseAssignement()
+ {
+ int errorPos = _token.pos;
+ Expression left = ParseConditional();
+ if (_token.id == TokenId.Equal)
+ {
+ Token op = _token;
+ NextToken();
+ Expression right = ParseAssignement();
+ CheckAndPromoteOperands(typeof(ParseSignatures.IEqualitySignatures), op.text, ref left, ref right, op.pos);
+ left = Expression.Assign(left, right);
+ }
+ return left;
}
// ?: operator
@@ -1852,7 +1869,7 @@ void NextToken()
}
else
{
- throw CreateParseException(_parsePosition, ErrorMessages.InvalidCharacter, _parseChar);
+ t = TokenId.Equal;
}
break;
case '>':
diff --git a/src/DynamicExpresso.Core/Parsing/TokenId.cs b/src/DynamicExpresso.Core/Parsing/TokenId.cs
index b9b0dcfa..553e8714 100644
--- a/src/DynamicExpresso.Core/Parsing/TokenId.cs
+++ b/src/DynamicExpresso.Core/Parsing/TokenId.cs
@@ -36,7 +36,8 @@ internal enum TokenId
LessThanEqual,
DoubleEqual,
GreaterThanEqual,
- DoubleBar
+ DoubleBar,
+ Equal
}
}
diff --git a/test/DynamicExpresso.UnitTest/InvalidExpressionTest.cs b/test/DynamicExpresso.UnitTest/InvalidExpressionTest.cs
index f75a1512..a59d766b 100644
--- a/test/DynamicExpresso.UnitTest/InvalidExpressionTest.cs
+++ b/test/DynamicExpresso.UnitTest/InvalidExpressionTest.cs
@@ -18,6 +18,33 @@ public void Not_existing_variable()
target.Eval("not_existing");
}
+ [TestMethod]
+ [ExpectedException(typeof(ParseException))]
+ public void Invalid_equal_assignment_operator_left()
+ {
+ var target = new Interpreter();
+
+ target.Eval("=234");
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(ArgumentException))]
+ public void Invalid_equal_assignment_operator_left_is_literal()
+ {
+ var target = new Interpreter();
+
+ target.Eval("352=234");
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(ParseException))]
+ public void Unkonwn_operator_triple_equal()
+ {
+ var target = new Interpreter();
+
+ target.Eval("352===234");
+ }
+
[TestMethod]
[ExpectedException(typeof(UnknownIdentifierException))]
public void Not_existing_function()
diff --git a/test/DynamicExpresso.UnitTest/OperatorsTest.cs b/test/DynamicExpresso.UnitTest/OperatorsTest.cs
index 73340e7d..6686535f 100644
--- a/test/DynamicExpresso.UnitTest/OperatorsTest.cs
+++ b/test/DynamicExpresso.UnitTest/OperatorsTest.cs
@@ -144,6 +144,49 @@ public void Comparison_Operators()
Assert.IsFalse((bool)target.Eval("\"dav\" == \"jack\""));
}
+ [TestMethod]
+ public void Assignment_Operator_Equal()
+ {
+ var x = new TypeWithProperty();
+
+ var target = new Interpreter()
+ .SetVariable("x", x);
+
+ // simple assignment
+ target.Eval("x.Property1 = 156");
+ Assert.AreEqual(156, x.Property1);
+
+ // assignment without space
+ target.Eval("x.Property1=156");
+ Assert.AreEqual(156, x.Property1);
+
+ // assignment with many spaces
+ target.Eval("x.Property1 = 156");
+ Assert.AreEqual(156, x.Property1);
+
+ // assignment should return the assigned value
+ var returnValue = target.Eval("x.Property1 = 81");
+ Assert.AreEqual(81, x.Property1);
+ Assert.AreEqual(x.Property1, returnValue);
+
+ // assignment can be chained
+ returnValue = target.Eval("x.Property1 = x.Property2 = 2014");
+ Assert.AreEqual(2014, x.Property1);
+ Assert.AreEqual(x.Property1, x.Property2);
+
+ // assignment can be nested with other operators
+ returnValue = target.Eval("x.Property1 = (486 + 4) * 10");
+ Assert.AreEqual(4900, x.Property1);
+ Assert.AreEqual(x.Property1, returnValue);
+
+ // right member is not modified
+ x.Property2 = 2;
+ returnValue = target.Eval("x.Property1 = x.Property2 * 10");
+ Assert.AreEqual(20, x.Property1);
+ Assert.AreEqual(2, x.Property2);
+ }
+ class TypeWithProperty { public int Property1 { get; set; } public int Property2 { get; set; } }
+
[TestMethod]
public void Can_compare_numeric_parameters_of_different_compatible_types()
{
@@ -196,15 +239,6 @@ public void If_Operators()
Assert.AreEqual(10 < 3 ? "yes" : "no", target.Eval("10 < 3 ? \"yes\" : \"no\""));
}
- [TestMethod]
- [ExpectedException(typeof(ParseException))]
- public void Operator_Equal_Is_Not_Supported()
- {
- var target = new Interpreter();
-
- target.Parse("5 = 4");
- }
-
[TestMethod]
[ExpectedException(typeof(ParseException))]
public void Operator_LessGreater_Is_Not_Supported()
@@ -434,6 +468,5 @@ public TypeWithoutOverloadedBinaryOperators(int value)
_value = value;
}
}
-
}
}