diff --git a/CompiledExpression.cs b/CompiledExpression.cs index 6e691c0..13536ef 100644 --- a/CompiledExpression.cs +++ b/CompiledExpression.cs @@ -17,6 +17,7 @@ public class CompiledExpression : ExpressionCompiler public CompiledExpression() { Parser = new AntlrParser(); + Parser.ReturnType = typeof(TResult); } public CompiledExpression(string expression) @@ -106,6 +107,7 @@ public class CompiledExpression : ExpressionCompiler public CompiledExpression() { Parser = new AntlrParser(); + Parser.ReturnType = typeof(object); } public CompiledExpression(string expression) diff --git a/ExpressionEvaluator.csproj b/ExpressionEvaluator.csproj index 2161342..d8f180a 100644 --- a/ExpressionEvaluator.csproj +++ b/ExpressionEvaluator.csproj @@ -69,6 +69,7 @@ + @@ -89,6 +90,7 @@ + diff --git a/Parser/AntlrParser.cs b/Parser/AntlrParser.cs index e50f5f4..c4b83b7 100644 --- a/Parser/AntlrParser.cs +++ b/Parser/AntlrParser.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -56,5 +57,6 @@ public Expression Parse(Expression scope, bool isCall = false) public CompiledExpressionType ExpressionType { get; set; } public List ExternalParameters { get; set; } + public Type ReturnType { get; set; } } } diff --git a/Parser/CompilerState.cs b/Parser/CompilerState.cs new file mode 100644 index 0000000..c86d812 --- /dev/null +++ b/Parser/CompilerState.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; + +namespace ExpressionEvaluator.Parser +{ + internal class CompilerState + { + public LabelTarget ReturnTarget { get; set; } + public Stack BreakContext { get; private set; } + public Stack ContinueContext { get; private set; } + public LabelTarget CurrentBreak { get; private set; } + public LabelTarget CurrentContinue { get; private set; } + + public CompilerState() + { + BreakContext = new Stack(); + ContinueContext = new Stack(); + } + + public LabelTarget PushContinue() + { + CurrentContinue = Expression.Label(); + ContinueContext.Push(CurrentContinue); + return CurrentContinue; + } + + public LabelTarget PushBreak() + { + CurrentBreak = Expression.Label(); + BreakContext.Push(CurrentBreak); + return CurrentBreak; + } + + public void PopBreak() + { + CurrentBreak = BreakContext.Pop(); + } + + public void PopContinue() + { + CurrentContinue = ContinueContext.Pop(); + } + + public Expression Break() + { + if (BreakContext.Count == 0) + { + throw new Exception("No enclosing loop out of which to break or continue"); + } + return Expression.Break(BreakContext.Peek()); + } + + public Expression Continue() + { + if (ContinueContext.Count == 0) + { + throw new Exception("No enclosing loop out of which to break or continue"); + } + return Expression.Continue(ContinueContext.Peek()); + } + + } +} \ No newline at end of file diff --git a/Parser/ExprEval.g3 b/Parser/ExprEval.g3 index 7afd44d..8f0677b 100644 --- a/Parser/ExprEval.g3 +++ b/Parser/ExprEval.g3 @@ -509,7 +509,44 @@ public expression_list returns [List values] first=expression { $values.Add($first.value); } (',' succeeding=expression { $values.Add($succeeding.value); })* ; public assignment returns [Expression value]: - unary_expression assignment_operator expression { $value = ExpressionHelper.Assign($unary_expression.value, $expression.value); } ; + unary_expression assignment_operator expression { + switch($assignment_operator.text) { + case "=": + $value = ExpressionHelper.Assign($unary_expression.value, $expression.value); + break; + case "+=": + $value = ExpressionHelper.BinaryOperator($unary_expression.value, $expression.value, ExpressionType.AddAssign); + break; + case "-=": + $value = ExpressionHelper.BinaryOperator($unary_expression.value, $expression.value, ExpressionType.SubtractAssign); + break; + case "*=": + $value = ExpressionHelper.BinaryOperator($unary_expression.value, $expression.value, ExpressionType.MultiplyAssign); + break; + case "/=": + $value = ExpressionHelper.BinaryOperator($unary_expression.value, $expression.value, ExpressionType.DivideAssign); + break; + // Note: StringTemplate requires escaping the percent symbol + case "\%=": + $value = ExpressionHelper.BinaryOperator($unary_expression.value, $expression.value, ExpressionType.ModuloAssign); + break; + case "&=": + $value = ExpressionHelper.BinaryOperator($unary_expression.value, $expression.value, ExpressionType.AndAssign); + break; + case "|=": + $value = ExpressionHelper.BinaryOperator($unary_expression.value, $expression.value, ExpressionType.OrAssign); + break; + case "^=": + $value = ExpressionHelper.BinaryOperator($unary_expression.value, $expression.value, ExpressionType.PowerAssign); + break; + case "<<=": + $value = ExpressionHelper.BinaryOperator($unary_expression.value, $expression.value, ExpressionType.LeftShiftAssign); + break; + case ">>=": + $value = ExpressionHelper.BinaryOperator($unary_expression.value, $expression.value, ExpressionType.RightShiftAssign); + break; + } + } ; public unary_expression returns [Expression value]: //('(' arguments ')' ('[' | '.' | '(')) => primary_or_array_creation_expression (cast_expression) => cast_expression { $value = $cast_expression.value; } @@ -525,8 +562,8 @@ public unary_expression returns [Expression value]: ; public cast_expression returns [Expression value]: '(' type ')' unary_expression { $value = Expression.Convert($unary_expression.value, GetType($type.text)); } ; -public assignment_operator returns [Expression value]: - '=' | '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' | '>' '>=' ; +public assignment_operator: + '=' | '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' | '>>=' ; public pre_increment_expression returns [Expression value]: '++' unary_expression ; public pre_decrement_expression returns [Expression value]: @@ -1259,20 +1296,11 @@ public if_statement returns [Expression value]: public else_statement returns [Expression value]: 'else' embedded_statement { $value = $embedded_statement.value; }; -public switch_statement returns [Expression value]: +public switch_statement returns [Expression value] +@init{ var breakTarget = compilerState.PushBreak(); } +@after{ compilerState.PopBreak(); }: 'switch' '(' expression ')' switch_block { - // TODO: Better switch handling??? - var defaultCase = $switch_block.value.SingleOrDefault(x=>x.TestValues[0].Type == typeof(void)); - var cases = $switch_block.value.Where(x=>x.TestValues[0].Type != typeof(void)).ToArray(); - - if(defaultCase == null) - { - $value = Expression.Switch($expression.value, cases); - } - else - { - $value = Expression.Switch($expression.value, defaultCase.Body, cases); - } + $value = ExpressionHelper.Switch(breakTarget, $expression.value, $switch_block.value); }; public switch_block returns [List value]: @@ -1304,26 +1332,26 @@ public iteration_statement returns [Expression value]: | foreach_statement { $value = $foreach_statement.value; }; public while_statement returns [Expression value] -@init{ var breakTarget = Expression.Label(); var continueTarget = Expression.Label(); _breakContext.Push(breakTarget); _continueContext.Push(continueTarget); } -@after{ _continueContext.Pop(); _breakContext.Pop(); }: +@init{ var breakTarget = compilerState.PushBreak(); var continueTarget = compilerState.PushContinue(); } +@after{ compilerState.PopContinue(); compilerState.PopBreak(); }: 'while' '(' boolean_expression ')' embedded_statement { $value = ExpressionHelper.While(breakTarget, continueTarget, null, $boolean_expression.value, $embedded_statement.value); }; public do_statement returns [Expression value] -@init{ var breakTarget = Expression.Label(); var continueTarget = Expression.Label(); _breakContext.Push(breakTarget); _continueContext.Push(continueTarget); } -@after{ _continueContext.Pop(); _breakContext.Pop(); }: +@init{ var breakTarget = compilerState.PushBreak(); var continueTarget = compilerState.PushContinue(); } +@after{ compilerState.PopContinue(); compilerState.PopBreak(); }: 'do' embedded_statement 'while' '(' boolean_expression ')' ';' { $value = ExpressionHelper.DoWhile(breakTarget, continueTarget, $embedded_statement.value, $boolean_expression.value); }; public for_statement returns [Expression value] -@init{ var breakTarget = Expression.Label(); var continueTarget = Expression.Label(); _breakContext.Push(breakTarget); _continueContext.Push(continueTarget); } -@after{ _continueContext.Pop(); _breakContext.Pop(); }: +@init{ var breakTarget = compilerState.PushBreak(); var continueTarget = compilerState.PushContinue(); } +@after{ compilerState.PopContinue(); compilerState.PopBreak(); }: 'for' '(' for_initializer? ';' for_condition? ';' for_iterator? ')' embedded_statement { $value = ExpressionHelper.For(breakTarget, continueTarget, $for_initializer.value, $for_condition.value, $for_iterator.value, $embedded_statement.value); }; public foreach_statement returns [Expression value] -@init{ ParameterExpression parameter = null; var breakTarget = Expression.Label(); var continueTarget = Expression.Label(); _breakContext.Push(breakTarget); _continueContext.Push(continueTarget); } -@after{ _continueContext.Pop(); _breakContext.Pop(); }: +@init{ ParameterExpression parameter = null; var breakTarget = compilerState.PushBreak(); var continueTarget = compilerState.PushContinue(); } +@after{ compilerState.PopContinue(); compilerState.PopBreak(); }: 'foreach' '(' local_variable_type identifier 'in' expression ')' { var typeName = $local_variable_type.value.Identifier; @@ -1384,10 +1412,10 @@ public jump_statement returns [Expression value]: | throw_statement ; public break_statement returns [Expression value]: - 'break' ';' { $value = Expression.Break(_breakContext.Peek()); }; + 'break' ';' { $value = compilerState.Break(); }; public continue_statement returns [Expression value]: - 'continue' ';' { $value = Expression.Continue(_continueContext.Peek()); }; + 'continue' ';' { $value = compilerState.Continue(); }; public goto_statement: 'goto' ( identifier @@ -1395,7 +1423,17 @@ public goto_statement: | 'default') ';' ; public return_statement returns [Expression value]: - 'return' expression? ';' ; + 'return' expression? { + HasReturn = true; + if($expression.value == null) + { + $value = Expression.Return(ReturnTarget); + } + else + { + $value = Expression.Return(ReturnTarget, $expression.value, ReturnTarget.Type); + } + } ';' ; public throw_statement: 'throw' expression? ';' ; diff --git a/Parser/ExprEval.g3.parser.cs b/Parser/ExprEval.g3.parser.cs index fce7ee3..8afad69 100644 --- a/Parser/ExprEval.g3.parser.cs +++ b/Parser/ExprEval.g3.parser.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using System.Linq; +using System.Diagnostics; using System.Linq.Expressions; using Antlr.Runtime; using ExpressionEvaluator; @@ -9,16 +9,27 @@ namespace ExpressionEvaluator.Parser { public partial class ExprEvalParser { - private Stack _breakContext = new Stack(); - private Stack _continueContext = new Stack(); + private CompilerState compilerState = new CompilerState(); public Expression Scope { get; set; } public bool IsCall { get; set; } - + public LabelTarget ReturnTarget { get; set; } + public bool HasReturn { get; private set; } public TypeRegistry TypeRegistry { get; set; } public List ExternalParameters { get; set; } + //partial void EnterRule(string ruleName, int ruleIndex) + //{ + // base.TraceIn(ruleName, ruleIndex); + // Debug.WriteLine("In: {0} {1}", ruleName, ruleIndex); + //} + + //partial void LeaveRule(string ruleName, int ruleIndex) + //{ + // Debug.WriteLine("Out: {0} {1}", ruleName, ruleIndex); + //} + public override void ReportError(RecognitionException e) { base.ReportError(e); @@ -30,7 +41,7 @@ public override void ReportError(RecognitionException e) } else { - message = string.Format("Error parsing token '{0}", e.Token.Text); + message = string.Format("Error parsing token '{0}'", e.Token.Text); } throw new ExpressionParseException(message, input); @@ -73,52 +84,4 @@ public Type GetType(string type) private ParameterList ParameterList = new ParameterList(); } - - public class ParameterList - { - private readonly List _parameters = new List(); - - public void Add(ParameterExpression parameter) - { - ParameterExpression p; - if (!ParameterLookup.TryGetValue(parameter.Name, out p)) - { - _parameters.Add(parameter); - } - else - { - throw new Exception(string.Format("Parameter \"{0}\" conflicts with an existing parameter", parameter.Name)); - } - - } - - public void Add(List list) - { - foreach (var parameterExpression in list) - { - Add(parameterExpression); - } - } - - private Dictionary ParameterLookup { get { return _parameters.ToDictionary(expression => expression.Name); } } - - public bool TryGetValue(string name, out ParameterExpression parameter) - { - return ParameterLookup.TryGetValue(name, out parameter); - } - - public void Remove(List list) - { - foreach (var parameterExpression in list) - { - ParameterExpression p; - - if (ParameterLookup.TryGetValue(parameterExpression.Name, out p)) - { - _parameters.Remove(parameterExpression); - } - } - } - - } } diff --git a/Parser/ExpressionHelper.cs b/Parser/ExpressionHelper.cs index b332deb..04f7f63 100644 --- a/Parser/ExpressionHelper.cs +++ b/Parser/ExpressionHelper.cs @@ -990,9 +990,42 @@ public static Expression New(Type t, IEnumerable arguments, IEnumera } } + public static Expression Switch(LabelTarget breakTarget, Expression switchCase, List switchBlock) + { + var defaultCase = switchBlock.SingleOrDefault(x => x.TestValues[0].Type == typeof(void)); + var cases = switchBlock.Where(x => x.TestValues[0].Type != typeof(void)).ToArray(); + + foreach (var @case in cases) + { + if (@case.Body.NodeType != ExpressionType.Block) continue; + var caseBlock = (BlockExpression)@case.Body; + if (caseBlock.Expressions.Last().NodeType != ExpressionType.Goto) + { + throw new Exception("Break statement is missing"); + } + } + + Expression switchExp = null; + + if (defaultCase == null) + { + switchExp = Expression.Switch(switchCase, cases); + } + else + { + switchExp = Expression.Switch(switchCase, defaultCase.Body, cases); + } + + return Expression.Block(new[] { + switchExp, + Expression.Label(breakTarget) + }); + + } + public static Expression ForEach(LabelTarget exitLabel, LabelTarget continueLabel, ParameterExpression parameter, Expression iterator, Expression body) { - var enumerator = GetMethod(iterator, new TypeOrGeneric() {Identifier = "GetEnumerator"}, new List()); + var enumerator = GetMethod(iterator, new TypeOrGeneric() { Identifier = "GetEnumerator" }, new List()); var enumParam = Expression.Variable(enumerator.Type); var assign = Expression.Assign(enumParam, enumerator); @@ -1007,22 +1040,15 @@ public static Expression ForEach(LabelTarget exitLabel, LabelTarget continueLabe variables.Add(parameter); - if (body.NodeType == ExpressionType.Block) - { - var current = GetProperty(enumParam, "Current"); - if (current.Type == typeof(object) && parameter.Type != typeof(object)) - { - current = Expression.Convert(current, parameter.Type); - } - expressions.Add(Assign(parameter, current)); - variables.AddRange(((BlockExpression)body).Variables); - expressions.AddRange(((BlockExpression)body).Expressions); - } - else + var current = GetProperty(enumParam, "Current"); + + if (current.Type == typeof(object) && parameter.Type != typeof(object)) { - expressions.Add(body); + current = Expression.Convert(current, parameter.Type); } + expressions.Add(Assign(parameter, current)); + expressions.Add(body); var newbody = Expression.Block(variables, expressions); return While(exitLabel, continueLabel, localVar, condition, newbody); @@ -1046,12 +1072,12 @@ public static Expression For(LabelTarget exitLabel, LabelTarget continueLabel, M var loopblock = new List(); - + if (condition != null) { loopblock.Add(Expression.IfThen(Expression.Not(condition), Expression.Goto(exitLabel))); } - + loopblock.Add(body); loopblock.Add(Expression.Label(continueLabel)); @@ -1106,7 +1132,7 @@ public static Expression While(LabelTarget breakTarget, LabelTarget continueLabe return Expression.Block(setup.Variables, loopBody); } - return Expression.Block(loopBody); + return Expression.Block(loopBody); } } } \ No newline at end of file diff --git a/Parser/Expressions/Statement.cs b/Parser/Expressions/Statement.cs index b5d88ab..d856c61 100644 --- a/Parser/Expressions/Statement.cs +++ b/Parser/Expressions/Statement.cs @@ -51,7 +51,7 @@ public void Add(Expression expression) public IEnumerable Expressions { get { return Statements.Select(x => x.Expression); } } - public Expression ToBlock() + public BlockExpression ToBlock() { var expressions = new List(); IList parameters = null; diff --git a/Parser/ParameterList.cs b/Parser/ParameterList.cs new file mode 100644 index 0000000..e1b4e99 --- /dev/null +++ b/Parser/ParameterList.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; + +namespace ExpressionEvaluator.Parser +{ + public class ParameterList + { + private readonly List _parameters = new List(); + + public void Add(ParameterExpression parameter) + { + ParameterExpression p; + if (!ParameterLookup.TryGetValue(parameter.Name, out p)) + { + _parameters.Add(parameter); + } + else + { + throw new Exception(string.Format("A local variable named '{0}' cannot be declared in this scope because it would give a different meaning to '{0}', which is already used in a 'parent or current' scope to denote something else", parameter.Name)); + } + + } + + public void Add(List list) + { + foreach (var parameterExpression in list) + { + Add(parameterExpression); + } + } + + private Dictionary ParameterLookup { get { return _parameters.ToDictionary(expression => expression.Name); } } + + public bool TryGetValue(string name, out ParameterExpression parameter) + { + return ParameterLookup.TryGetValue(name, out parameter); + } + + public void Remove(List list) + { + foreach (var parameterExpression in list) + { + ParameterExpression p; + + if (ParameterLookup.TryGetValue(parameterExpression.Name, out p)) + { + _parameters.Remove(parameterExpression); + } + } + } + + } +} \ No newline at end of file diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs index 34ce102..0667dd3 100644 --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.0.0.2")] -[assembly: AssemblyFileVersion("2.0.0.2")] +[assembly: AssemblyVersion("2.0.1.1")] +[assembly: AssemblyFileVersion("2.0.1.1")] diff --git a/TestProject1/UnitTest1.cs b/TestProject1/UnitTest1.cs index b4f1330..94af7b8 100644 --- a/TestProject1/UnitTest1.cs +++ b/TestProject1/UnitTest1.cs @@ -306,6 +306,76 @@ public void IfTheElseStatementList() Assert.AreEqual(a.y, a.z); } + [TestMethod] + public void SwitchStatement() + { + var a = new ClassA() { x = 1 }; + var t = new TypeRegistry(); + + for (a.x = 1; a.x < 7; a.x++) + { + switch (a.x) + { + case 1: + case 2: + Debug.WriteLine("Hello"); + break; + case 3: + Debug.WriteLine("There"); + break; + case 4: + Debug.WriteLine("World"); + break; + default: + Debug.WriteLine("Undefined"); + break; + } + } + + t.RegisterSymbol("Debug", typeof(Debug)); + var p = new CompiledExpression { StringToParse = "switch(x) { case 1: case 2: Debug.WriteLine('Hello'); break; case 3: Debug.WriteLine('There'); break; case 4: Debug.WriteLine('World'); break; default: Debug.WriteLine('Undefined'); break; }", TypeRegistry = t }; + p.ExpressionType = CompiledExpressionType.StatementList; + var func = p.ScopeCompile(); + for (a.x = 1; a.x < 7; a.x++) + { + func(a); + } + } + + [TestMethod] + public void Return() + { + var t = new TypeRegistry(); + + var p = new CompiledExpression { StringToParse = "return true;", TypeRegistry = t }; + p.ExpressionType = CompiledExpressionType.StatementList; + Assert.AreEqual(true, p.Compile()()); + + p.StringToParse = "var x = 3; if (x == 3) { return true; } return false;"; + Assert.AreEqual(true, p.Compile()()); + + p.StringToParse = "var x = 2; if (x == 3) { return true; } "; + Assert.AreEqual(true, p.Compile()()); + + p.StringToParse = "var x = true; x;"; + Assert.AreEqual(true, p.Compile()()); + } + + [TestMethod] + public void SwitchReturn() + { + var a = new ClassA() { x = 1 }; + var t = new TypeRegistry(); + + var p = new CompiledExpression { StringToParse = "var retval = 'Exit'; switch(x) { case 1: case 2: return 'Hello'; case 3: return 'There'; case 4: return 'World'; default: return 'Undefined'; } return retval;", TypeRegistry = t }; + p.ExpressionType = CompiledExpressionType.StatementList; + var func = p.ScopeCompile(); + for (a.x = 1; a.x < 7; a.x++) + { + Debug.WriteLine(func(a)); + } + } + [TestMethod] public void LocalImplicitVariables() { @@ -350,7 +420,7 @@ public void ForLoop() registry.RegisterSymbol("obj", obj); registry.RegisterType("objHolder", typeof(objHolder)); registry.RegisterDefaultTypes(); - + for (var i = 0; i < 10; i++) { obj.number2++; } var cc = new CompiledExpression() { StringToParse = "for(var i = 0; i < 10; i++) { obj.number++; }", TypeRegistry = registry }; @@ -387,7 +457,7 @@ public void ForEachLoop() var obj = new objHolder() { iterator = new List() { "Hello", "there", "world" } }; registry.RegisterSymbol("obj", obj); - registry.RegisterType("Debug", typeof(Debug)); + registry.RegisterType("Debug", typeof(Debug)); registry.RegisterType("objHolder", typeof(objHolder)); registry.RegisterDefaultTypes(); @@ -404,6 +474,31 @@ public void ForEachLoop() cc.Eval(); } + [TestMethod] + public void ForEachLoopNoBlock() + { + var registry = new TypeRegistry(); + + var obj = new objHolder() { iterator = new List() { "Hello", "there", "world" } }; + + registry.RegisterSymbol("obj", obj); + registry.RegisterType("Debug", typeof(Debug)); + registry.RegisterType("objHolder", typeof(objHolder)); + registry.RegisterDefaultTypes(); + + //var iterator = new List() { "Hello", "there", "world" }; + //var enumerator = iterator.GetEnumerator(); + //while (enumerator.MoveNext()) + //{ + // var word = enumerator.Current; + // Debug.WriteLine(word); + //} + + var cc = new CompiledExpression() { StringToParse = "foreach(var word in obj.iterator) Debug.WriteLine(word);", TypeRegistry = registry }; + cc.ExpressionType = CompiledExpressionType.StatementList; + cc.Eval(); + } + [TestMethod] public void ForEachLoopArray() @@ -413,7 +508,7 @@ public void ForEachLoopArray() var obj = new objHolder() { stringIterator = new[] { "Hello", "there", "world" } }; //foreach (var word in obj.stringIterator) { Debug.WriteLine(word); } - + var enumerator = obj.stringIterator.GetEnumerator(); while (enumerator.MoveNext()) { @@ -443,7 +538,7 @@ public void ForLoopWithContinue() registry.RegisterSymbol("obj", obj); registry.RegisterType("objHolder", typeof(objHolder)); registry.RegisterDefaultTypes(); - + for (var i = 0; i < 10; i++) { obj2.number++; if (i > 5) continue; obj2.number2++; } var cc = new CompiledExpression() { StringToParse = "for(var i = 0; i < 10; i++) { obj.number++; if(i > 5) continue; obj.number2++; }", TypeRegistry = registry }; @@ -525,9 +620,136 @@ public void ScopeCompileTypedResultObjectParam() target.ScopeCompile(); } + [TestMethod] + public void AssignmentOperators() + { + var classA = new ClassA(); + + var exp = new CompiledExpression(); + Func func; + exp.StringToParse = "x = 1"; + func = exp.ScopeCompile(); + func(classA); + Assert.AreEqual(1, classA.x); + + exp.StringToParse = "x += 9"; + func = exp.ScopeCompile(); + func(classA); + Assert.AreEqual(10, classA.x); + + exp.StringToParse = "x -= 4"; + func = exp.ScopeCompile(); + func(classA); + Assert.AreEqual(6, classA.x); + + exp.StringToParse = "x *= 5"; + func = exp.ScopeCompile(); + func(classA); + Assert.AreEqual(30, classA.x); + + exp.StringToParse = "x /= 2"; + func = exp.ScopeCompile(); + func(classA); + Assert.AreEqual(15, classA.x); + + exp.StringToParse = "x %= 13"; + func = exp.ScopeCompile(); + func(classA); + Assert.AreEqual(2, classA.x); + + exp.StringToParse = "x <<= 4"; + func = exp.ScopeCompile(); + func(classA); + Assert.AreEqual(32, classA.x); + + exp.StringToParse = "x >>= 1"; + func = exp.ScopeCompile(); + func(classA); + Assert.AreEqual(16, classA.x); + } + + + [TestMethod] + public void MethodOverLoading() + { + var controlScope = new MethodOverloading(); + var testScope = new MethodOverloading(); + + var exp = new CompiledExpression(); + Func func; + + controlScope.sum(1, 2, 3, 4, 5, 6, 7, 8); + + exp.StringToParse = "sum(1, 2, 3, 4, 5, 6, 7, 8)"; + func = exp.ScopeCompile(); + func(testScope); + // expect sum(float i, params float[] nums) + Assert.AreEqual(controlScope.MethodCalled, testScope.MethodCalled); + + controlScope.sum(1, 2); + + exp.StringToParse = "sum(1, 2)"; + func = exp.ScopeCompile(); + func(testScope); + // expect sum(int,int) is called + Assert.AreEqual(controlScope.MethodCalled, testScope.MethodCalled); + + controlScope.sum(1.0d, 2.0d); + + exp.StringToParse = "sum(1.0d, 2.0d)"; + func = exp.ScopeCompile(); + func(testScope); + // expect sum(double, double) is called + Assert.AreEqual(controlScope.MethodCalled, testScope.MethodCalled); + + controlScope.sum(1, 2.0d); + + exp.StringToParse = "sum(1,2.0d)"; + func = exp.ScopeCompile(); + func(testScope); + // expect sum(double, double) is called (no matching int, double) + Assert.AreEqual(controlScope.MethodCalled, testScope.MethodCalled); + } + + //[TestMethod] + //public void Lambda() + //{ + // var tr = new TypeRegistry(); + // tr.RegisterType("Enumerable", typeof(Enumerable)); + // var data = new MyClass(); + // data.Y = new List() { 1, 2, 3, 4, 5, 4, 4, 3, 4, 2 }; + // var c9 = new CompiledExpression() { StringToParse = "Enumerable.Where(Y, (y) => y == 4)", TypeRegistry = tr }; + // var f9 = c9.ScopeCompile(); + + // Console.WriteLine(data.X); + // f9(data); + // Console.WriteLine(data.X); + //} } + public class MyClass + { + public int X { get; set; } + public List Y { get; set; } + public Func Value { get; set; } + public void Foo() + { + X++; + } + + public void Foo(int value) + { + X += value; + } + + public int Bar(int value) + { + return value * 2; + } + } + + public class objHolder { public bool result { get; set; } @@ -559,4 +781,82 @@ public class NameValue public string Name { get; set; } public T Value { get; set; } } + + public class MethodOverloading + { + public int MethodCalled { get; set; } + + public double sum(double i, double t) + { + MethodCalled = 1; + var result = 0d; + return result; + } + + public double sum(double i, int t) + { + MethodCalled = 2; + var result = 0d; + return result; + } + + public int sum(int i, int t) + { + MethodCalled = 3; + var result = 0; + return result; + } + + public int sum(int i1, int i2, int i3, int i4, int i5) + { + MethodCalled = 4; + var result = 0; + return result; + } + + + public double sum(double i, params double[] nums) + { + MethodCalled = 5; + var result = 0d; + foreach (var num in nums) + { + result += num; + } + return result; + } + + public float sum(float i, params float[] nums) + { + MethodCalled = 6; + var result = 0f; + foreach (var num in nums) + { + result += num; + } + return result; + } + + + public int yes() + { + return 1234; + } + + public bool no + { + get { return false; } + } + + public int fix(int x) + { + return x + 1; + } + + public int func(Predicate t) + { + return t(5) ? 1 : 2; + } + } + } diff --git a/Tests/Program.cs b/Tests/Program.cs index f13b795..ae3b567 100644 --- a/Tests/Program.cs +++ b/Tests/Program.cs @@ -2,82 +2,11 @@ using System.Collections.Generic; using System.Dynamic; using System.Globalization; -using System.Linq; using System.Linq.Expressions; -using System.Text; -using System.Threading.Tasks; using ExpressionEvaluator; namespace Tests { - public class c - { - public double sum(double i, double t) - { - var result = 0d; - return result; - } - - public double sum(double i, int t) - { - var result = 0d; - return result; - } - - public int sum(int i, int t) - { - var result = 0; - return result; - } - - public int sum(int i1, int i2, int i3, int i4, int i5) - { - var result = 0; - return result; - } - - - public double sum(double i, params double[] nums) - { - var result = 0d; - foreach (var num in nums) - { - result += num; - } - return result; - } - - public float sum(float i, params float[] nums) - { - var result = 0f; - foreach (var num in nums) - { - result += num; - } - return result; - } - - - public int yes() - { - return 1234; - } - - public bool no - { - get { return false; } - } - - public int fix(int x) - { - return x + 1; - } - - public int func(Predicate t) - { - return t(5) ? 1 : 2; - } - } public class c2 { @@ -189,7 +118,6 @@ public float AMT(float value) class Program { - static void Main(string[] args) { var exp = "@ATADJ( @MAX( @SUBTR(@PR( 987043 ) , @AMT( 913000 ) ) , @MULT( @PR( 987043 ) , 0.20f ) ) , 60f ) "; @@ -269,16 +197,6 @@ static void Main(string[] args) f1(data); Console.WriteLine(data.X); - //var tr = new TypeRegistry(); - //tr.RegisterType("Enumerable", typeof(Enumerable)); - - //var c9 = new CompiledExpression() { StringToParse = "Enumerable.Where(Y, (y) => y == 4)", TypeRegistry = tr }; - //var f9 = c9.ScopeCompile(); - - //Console.WriteLine(data.X); - //f9(data); - //Console.WriteLine(data.X); - var qq = (25.82).ToString("0.00", new CultureInfo("fr-FR")) + "px"; var test = "(25.82).ToString('0.00') + 'px'"; @@ -292,27 +210,6 @@ static void Main(string[] args) f2(scope); Console.WriteLine(scope.data.X); - scope.c = new c(); - - var c3 = new CompiledExpression() { StringToParse = "temp = c.sum(1,2,3,4,5,6,7,8)" }; - var f3 = c3.ScopeCompile(); - var x3 = f3(scope); - - //var c3 = new CompiledExpression() { StringToParse = "c.sum(1,2,3,4,5,6,7,8)" }; - //var f3 = c3.ScopeCompile(); - //var x3 = f3(scope); - - var c4 = new CompiledExpression() { StringToParse = "c.sum(1,2)" }; - var f4 = c4.ScopeCompile(); - var x4 = f4(scope); - - var c5 = new CompiledExpression() { StringToParse = "c.sum(1.0d,2.0d)" }; - var f5 = c5.ScopeCompile(); - var x5 = f5(scope); - - var c6 = new CompiledExpression() { StringToParse = "c.sum(1,2.0d)" }; - var f6 = c6.ScopeCompile(); - var x6 = f6(scope); Console.ReadLine(); }