protected override NodeBase Expand(Context ctx, bool mustReturn) { var leftType = LeftOperand.Resolve(ctx, mustReturn); // create a lambda expression that passes the result of left function to the right one if (leftType.IsCallableType()) { var leftVar = ctx.Unique.TempVariableName(); var rightVar = ctx.Unique.TempVariableName(); var delegateType = ReflectionHelper.WrapDelegate(leftType); var argDefs = delegateType.ArgumentTypes.Select(x => Expr.Arg(ctx.Unique.AnonymousArgName(), x.FullName)).ToArray(); return(Expr.Lambda( argDefs, Expr.Block( Expr.Let(leftVar, LeftOperand), Expr.Let(rightVar, RightOperand), Expr.Invoke( Expr.Get(rightVar), Expr.Invoke( Expr.Get(leftVar), argDefs.Select(x => Expr.Get(x.Name)).ToArray() ) ) ) )); } return(base.Expand(ctx, mustReturn)); }
public void Should_not_allow_null_expression_in_array() { var ex = Assert.Throws <ArgumentNullException>(() => Expr.Block(Expr.Empty(), null, Expr.Empty())); Assert.That(ex.Message, Is.StringContaining("Value cannot be null.")); Assert.That(ex.Message, Is.StringContaining("expressions[1]")); }
/// <summary> /// Expands the foreach loop if it iterates over T[]. /// </summary> private NodeBase expandArray(Context ctx) { var arrayVar = ctx.Scope.DeclareImplicit(ctx, IterableExpression.Resolve(ctx), false); var idxVar = ctx.Scope.DeclareImplicit(ctx, typeof(int), false); var lenVar = ctx.Scope.DeclareImplicit(ctx, typeof(int), false); return(Expr.Block( Expr.Set(idxVar, Expr.Int(0)), Expr.Set(arrayVar, IterableExpression), Expr.Set(lenVar, Expr.GetMember(Expr.Get(arrayVar), "Length")), Expr.While( Expr.Less( Expr.Get(idxVar), Expr.Get(lenVar) ), Expr.Block( getIndexAssignment( Expr.GetIdx( Expr.Get(arrayVar), Expr.Get(idxVar) ) ), Expr.Set( idxVar, Expr.Add(Expr.Get(idxVar), Expr.Int(1)) ), Body ) ) )); }
/// <summary> /// Repeats a string. /// </summary> private NodeBase StringExpand(Context ctx) { var tmpString = ctx.Scope.DeclareImplicit(ctx, typeof(string), false); var tmpSb = ctx.Scope.DeclareImplicit(ctx, typeof(StringBuilder), false); var tmpIdx = ctx.Scope.DeclareImplicit(ctx, RightOperand.Resolve(ctx), false); // var sb = new StringBuilder(); // for _ in 1..N do // sb.Append (str) // str.ToString () return(Expr.Block( Expr.Let(tmpString, LeftOperand), Expr.Let(tmpSb, Expr.New(typeof(StringBuilder))), Expr.For( tmpIdx, Expr.Int(1), RightOperand, Expr.Block( Expr.Invoke( Expr.Get(tmpSb), "Append", Expr.Get(tmpString) ) ) ), Expr.Invoke( Expr.Get(tmpSb), "ToString" ) )); }
/// <summary> /// Expands the foreach loop if it iterates over a numeric range. /// </summary> private NodeBase expandRange(Context ctx) { var signVar = ctx.Scope.DeclareImplicit(ctx, _VariableType, false); var idxVar = ctx.Scope.DeclareImplicit(ctx, _VariableType, false); return(Expr.Block( Expr.Set(idxVar, RangeStart), Expr.Set( signVar, Expr.Invoke( "Math", "Sign", Expr.Sub(RangeEnd, Expr.Get(idxVar)) ) ), Expr.While( Expr.NotEqual(Expr.Get(idxVar), RangeEnd), Expr.Block( getIndexAssignment(Expr.Get(idxVar)), Body, Expr.Set( idxVar, Expr.Add( Expr.Get(idxVar), Expr.Get(signVar) ) ) ) ) )); }
/// <summary> /// Creates a pure wrapper for parameterless function. /// </summary> private void CreatePureWrapper0(MethodEntity wrapper, string pureName) { var fieldName = string.Format(EntityNames.PureMethodCacheNameTemplate, wrapper.Name); var flagName = string.Format(EntityNames.PureMethodCacheFlagNameTemplate, wrapper.Name); CreateField(fieldName, wrapper.ReturnTypeSignature, true); CreateField(flagName, typeof(bool), true); wrapper.Body = Expr.Block( ScopeKind.FunctionRoot, // if (not $flag) $cache = $internal (); $flag = true Expr.If( Expr.Not(Expr.GetMember(EntityNames.MainTypeName, flagName)), Expr.Block( Expr.SetMember( EntityNames.MainTypeName, fieldName, Expr.Invoke(EntityNames.MainTypeName, pureName) ), Expr.SetMember(EntityNames.MainTypeName, flagName, Expr.True()) ) ), // $cache Expr.GetMember(EntityNames.MainTypeName, fieldName) ); }
public void FunWithIfThenElse() { var src = @" fun part (x:int) -> if x > 100 then (new Large x) as TestType else new Small x"; var result = Expr.Fun( "part", new[] { Expr.Arg("x", "int") }, Expr.If( Expr.Greater(Expr.Get("x"), Expr.Int(100)), Expr.Block( Expr.Cast( Expr.New("Large", Expr.Get("x")), "TestType" ) ), Expr.Block( Expr.New("Small", Expr.Get("x")) ) ) ); TestParser(src, result); }
/// <summary> /// Creates a pure wrapper for function with 2 and more arguments. /// </summary> private void createPureWrapperMany(MethodEntity wrapper, string pureName) { var args = wrapper.GetArgumentTypes(Context); var fieldName = string.Format(EntityNames.PureMethodCacheNameTemplate, wrapper.Name); var tupleType = FunctionalHelper.CreateTupleType(args); var fieldType = typeof(Dictionary <,>).MakeGenericType(tupleType, wrapper.ReturnType); CreateField(fieldName, fieldType, true); var argGetters = wrapper.Arguments.Select(a => (NodeBase)Expr.Get(a)).ToArray(); var tupleName = "<args>"; wrapper.Body = Expr.Block( ScopeKind.FunctionRoot, // $tmp = new Tuple<...> $arg1 $arg2 ... Expr.Let(tupleName, Expr.New(tupleType, argGetters)), // if ($dict == null) $dict = new Dictionary<$tupleType, $valueType> () Expr.If( Expr.Equal( Expr.GetMember(EntityNames.MainTypeName, fieldName), Expr.Null() ), Expr.Block( Expr.SetMember( EntityNames.MainTypeName, fieldName, Expr.New(fieldType) ) ) ), // if(not $dict.ContainsKey key) $dict.Add ($internal arg) Expr.If( Expr.Not( Expr.Invoke( Expr.GetMember(EntityNames.MainTypeName, fieldName), "ContainsKey", Expr.Get(tupleName) ) ), Expr.Block( Expr.Invoke( Expr.GetMember(EntityNames.MainTypeName, fieldName), "Add", Expr.Get(tupleName), Expr.Invoke(EntityNames.MainTypeName, pureName, argGetters) ) ) ), // $dict[arg] Expr.GetIdx( Expr.GetMember(EntityNames.MainTypeName, fieldName), Expr.Get(tupleName) ) ); }
public override IEnumerable <NodeBase> Expand(Context ctx, NodeBase expression, Label nextStatement) { yield return(Expr.If( Expr.NotEqual(Literal as NodeBase, expression), Expr.Block( Expr.JumpTo(nextStatement) ) )); }
/// <summary> /// Creates the entry point for an assembly if it is supposed to be saved. /// The entry point method basically calls the Run method and discards the result. /// </summary> private void createEntryPoint() { var ep = MainType.CreateMethod(EntityNames.EntryPointMethodName, "Void", args: null, isStatic: true); ep.Kind = TypeContentsKind.AutoGenerated; ep.Body = Expr.Block( Expr.Invoke(Expr.New(EntityNames.MainTypeName), "Run"), Expr.Unit() ); }
/// <summary> /// Expands the foreach loop if it iterates over an IEnumerable`1. /// </summary> private NodeBase expandEnumerable(Context ctx, bool mustReturn) { var iteratorVar = ctx.Scope.DeclareImplicit(ctx, _EnumeratorType, false); var enumerableType = _EnumeratorType.IsGenericType ? typeof(IEnumerable <>).MakeGenericType(_EnumeratorType.GetGenericArguments()[0]) : typeof(IEnumerable); var init = Expr.Set( iteratorVar, Expr.Invoke( Expr.Cast(IterableExpression, enumerableType), "GetEnumerator" ) ); var loop = Expr.While( Expr.Invoke(Expr.Get(iteratorVar), "MoveNext"), Expr.Block( getIndexAssignment(Expr.GetMember(Expr.Get(iteratorVar), "Current")), Body ) ); if (_EnumeratorType.Implements(typeof(IDisposable), false)) { var dispose = Expr.Block(Expr.Invoke(Expr.Get(iteratorVar), "Dispose")); var returnType = Resolve(ctx); var saveLast = mustReturn && !returnType.IsVoid(); if (saveLast) { var resultVar = ctx.Scope.DeclareImplicit(ctx, _EnumeratorType, false); return(Expr.Block( Expr.Try( Expr.Block( init, Expr.Set(resultVar, loop) ), dispose ), Expr.Get(resultVar) )); } return(Expr.Try( Expr.Block(init, loop), dispose )); } return(Expr.Block( init, loop )); }
protected override NodeBase Expand(Context ctx, bool mustReturn) { if (Resolve(ctx).IsNullableType()) { var leftNullable = LeftOperand.Resolve(ctx).IsNullableType(); var rightNullable = RightOperand.Resolve(ctx).IsNullableType(); if (leftNullable && rightNullable) { return(Expr.If( Expr.And( Expr.GetMember(LeftOperand, "HasValue"), Expr.GetMember(RightOperand, "HasValue") ), Expr.Block( RecreateSelfWithArgs( Expr.GetMember(LeftOperand, "Value"), Expr.GetMember(RightOperand, "Value") ) ), Expr.Block(Expr.Null()) )); } if (leftNullable) { return(Expr.If( Expr.GetMember(LeftOperand, "HasValue"), Expr.Block( RecreateSelfWithArgs( Expr.GetMember(LeftOperand, "Value"), RightOperand ) ), Expr.Block(Expr.Null()) )); } if (rightNullable) { return(Expr.If( Expr.GetMember(RightOperand, "HasValue"), Expr.Block( RecreateSelfWithArgs( LeftOperand, Expr.GetMember(RightOperand, "Value") ) ), Expr.Block(Expr.Null()) )); } } return(base.Expand(ctx, mustReturn)); }
protected override NodeBase Expand(Context ctx, bool mustReturn) { if (!IsConstant) { return(Kind == LogicalOperatorKind.And ? Expr.If(LeftOperand, Expr.Block(Expr.Cast <bool>(RightOperand)), Expr.Block(Expr.False())) : Expr.If(LeftOperand, Expr.Block(Expr.True()), Expr.Block(Expr.Cast <bool>(RightOperand)))); } return(base.Expand(ctx, mustReturn)); }
public void ConditionFull() { var src = "if true then a else b"; var result = Expr.If( Expr.True(), Expr.Block(Expr.Get("a")), Expr.Block(Expr.Get("b")) ); TestParser(src, result); }
/// <summary> /// Creates a pure wrapper for function with 1 argument. /// </summary> private void CreatePureWrapper1(MethodEntity wrapper, string pureName) { var args = wrapper.GetArgumentTypes(Context); var argName = wrapper.Arguments[0].Name; var fieldName = string.Format(EntityNames.PureMethodCacheNameTemplate, wrapper.Name); var fieldType = typeof(Dictionary <,>).MakeGenericType(args[0], wrapper.ReturnType); CreateField(fieldName, fieldType, true); wrapper.Body = Expr.Block( ScopeKind.FunctionRoot, // if ($dict == null) $dict = new Dictionary<$argType, $valueType> () Expr.If( Expr.Equal( Expr.GetMember(EntityNames.MainTypeName, fieldName), Expr.Null() ), Expr.Block( Expr.SetMember( EntityNames.MainTypeName, fieldName, Expr.New(fieldType) ) ) ), // if(not $dict.ContainsKey key) $dict.Add ($internal arg) Expr.If( Expr.Not( Expr.Invoke( Expr.GetMember(EntityNames.MainTypeName, fieldName), "ContainsKey", Expr.Get(argName) ) ), Expr.Block( Expr.Invoke( Expr.GetMember(EntityNames.MainTypeName, fieldName), "Add", Expr.Get(argName), Expr.Invoke(EntityNames.MainTypeName, pureName, Expr.Get(argName)) ) ) ), // $dict[arg] Expr.GetIdx( Expr.GetMember(EntityNames.MainTypeName, fieldName), Expr.Get(argName) ) ); }
public override IEnumerable <NodeBase> Expand(Context ctx, NodeBase expression, Label nextStatement) { yield return(Expr.If( Expr.Or( Expr.Less(expression, RangeStartRule.Literal as NodeBase), Expr.Greater(expression, RangeEndRule.Literal as NodeBase) ), Expr.Block( Expr.JumpTo(nextStatement) ) )); }
public void Should_execute_all_statements_in_block() { var addMethod = typeof(Counter).GetMethod("Add"); var func = CreateAction <Counter>(Expr.Block( Expr.Call(Expr.Parameter(0, typeof(Counter)), addMethod), Expr.Call(Expr.Parameter(0, typeof(Counter)), addMethod))); var cnt = new Counter(); func(cnt); Assert.That(cnt.Value, Is.EqualTo(2)); }
public void IfThenElse() { var src = @" if x then 1 else 2 "; var result = Expr.If(Expr.Get("x"), Expr.Block(Expr.Int(1)), Expr.Block(Expr.Int(2))); TestParser(src, result); }
/// <summary> /// Expands short assignment to an expression member: /// (expr).x += 1 /// or type::x += 1 /// </summary> private NodeBase ExpandMember(Context ctx, SetMemberNode node) { // type::name += value if (node.StaticType != null) { return(Expr.SetMember( node.StaticType, node.MemberName, _assignmentOperator( Expr.GetMember( node.StaticType, node.MemberName ), node.Value ) )); } // simple case: no need to cache expression if (node.Expression is SetIdentifierNode) { return(Expr.SetMember( node.Expression, node.MemberName, _assignmentOperator( Expr.GetMember( node.Expression, node.MemberName ), node.Value ) )); } // (x + y).name += value // must cache (x + y) to a local variable to prevent double execution var tmpVar = ctx.Scope.DeclareImplicit(ctx, node.Expression.Resolve(ctx), false); return(Expr.Block( Expr.Set(tmpVar, node.Expression), Expr.SetMember( Expr.Get(tmpVar), node.MemberName, _assignmentOperator( Expr.GetMember( Expr.Get(tmpVar), node.MemberName ), node.Value ) ) )); }
public void Algebraic3() { var src = @" type TestType Small of int Large of int fun part:TestType (x:int) -> if x > 100 then (new Large x) as TestType else new Small x var a = part 10 new [ a is TestType; a is Small; a is Large ]"; var result = new NodeBase[] { Expr.Type( "TestType", Expr.Label("Small", "int"), Expr.Label("Large", "int") ), Expr.Fun( "part", "TestType", new [] { Expr.Arg("x", "int") }, Expr.If( Expr.Greater(Expr.Get("x"), Expr.Int(100)), Expr.Block( Expr.Cast( Expr.New("Large", Expr.Get("x")), "TestType" ) ), Expr.Block( Expr.New("Small", Expr.Get("x")) ) ) ), Expr.Var("a", Expr.Invoke("part", Expr.Int(10))), Expr.Array( Expr.Is(Expr.Get("a"), "TestType"), Expr.Is(Expr.Get("a"), "Small"), Expr.Is(Expr.Get("a"), "Large") ) }; TestParser(src, result); }
/// <summary> /// Repeats a typed or untyped sequence. /// </summary> private NodeBase SeqExpand(Context ctx) { var seqType = LeftOperand.Resolve(ctx); NodeBase leftWrapper; if (seqType == typeof(IEnumerable)) { leftWrapper = Expr.Invoke(Expr.GetMember("System.Linq.Enumerable", "OfType", "object"), LeftOperand); seqType = typeof(IEnumerable <object>); } else { leftWrapper = LeftOperand; } var tmpLeft = ctx.Scope.DeclareImplicit(ctx, seqType, false); var tmpResult = ctx.Scope.DeclareImplicit(ctx, seqType, false); var tmpIndex = ctx.Scope.DeclareImplicit(ctx, typeof(int), false); // a = <left> // result = a // for x in 1..(Math.Abs <right>) do // result = result.Concat a // result return(Expr.Block( Expr.Set(tmpLeft, leftWrapper), Expr.Set(tmpResult, Expr.Get(tmpLeft)), Expr.For( tmpIndex, Expr.Int(1), Expr.Invoke( "System.Math", "Abs", RightOperand ), Expr.Block( Expr.Set( tmpResult, Expr.Invoke( "System.Linq.Enumerable", "Concat", Expr.Get(tmpResult), Expr.Get(tmpLeft) ) ) ) ), Expr.Get(tmpResult) )); }
public void Should_iterate_n_times_with_return() { var loc = Expr.LocalVariable(typeof(int), "i"); var func = CreateFunc <int, int>( Expr.DeclareLocal(loc, Expr.Constant(0)), Expr.Loop(Expr.Block( Expr.WriteLocal(loc, Expr.Add(Expr.ReadLocal(loc), Expr.Constant(1))), Expr.IfThen( Expr.Equal(Expr.ReadLocal(loc), Expr.Parameter(0, typeof(int))), Expr.Return(Expr.ReadLocal(loc))))), Expr.Return(Expr.Constant(0))); Assert.That(func(5), Is.EqualTo(5)); }
public void Should_allow_break_in_catch_block() { var loc = Expr.LocalVariable(typeof(int), "i"); var func = CreateFunc <int>( Expr.WriteLocal(loc, Expr.Constant(0)), Expr.Loop(Expr.TryCatch( Expr.Block( Expr.WriteLocal(loc, Expr.Add(Expr.ReadLocal(loc), Expr.Constant(1))), Expr.Throw(Expr.New(typeof(Exception)))), new CatchBlock(Expr.LoopBreak()))), Expr.Return(Expr.ReadLocal(loc))); Assert.That(func(), Is.EqualTo(2)); }
public void ConditionSimple() { var src = @" if true then a = 1 b = 2"; var result = Expr.If( Expr.True(), Expr.Block( Expr.Set("a", Expr.Int(1)), Expr.Set("b", Expr.Int(2)) ) ); TestParser(src, result); }
public void For1() { var src = "for x in y do test ()"; var result = new NodeBase[] { Expr.For( "x", Expr.Get("y"), Expr.Block( Expr.Invoke("test") ) ) }; TestParser(src, result); }
public override IEnumerable <NodeBase> Expand(Context ctx, NodeBase expression, Label nextStatement) { if (Type != null) { yield return(Expr.If( Expr.Not(Expr.Is(expression, Type)), Expr.Block( Expr.JumpTo(nextStatement) ) )); } if (!IsWildcard) { yield return(Expr.Set(Name, expression)); } }
/// <summary> /// Expands the current rule into a block of checks. /// </summary> public CodeBlockNode ExpandRules(Context ctx, NodeBase expression, Label expressionLabel) { var block = new CodeBlockNode(); // rule is never true: do not emit its code at all if (Condition != null && Condition.IsConstant && Condition.ConstantValue == false) { return(block); } // declare variables foreach (var binding in _BindingSet) { block.Add(Expr.Var(binding.Name, binding.Type.FullName)); } foreach (var rule in MatchRules) { // current and next labels for each rule var ruleLabels = ParentNode.GetRuleLabels(rule); block.Add(Expr.JumpLabel(ruleLabels.CurrentRule)); block.AddRange(rule.Expand(ctx, expression, ruleLabels.NextRule)); if (Condition != null) { block.Add( Expr.If( Expr.Not(Condition), Expr.Block(Expr.JumpTo(ruleLabels.NextRule)) ) ); } block.Add(Expr.JumpTo(expressionLabel)); } block.AddRange( Expr.JumpLabel(expressionLabel), Expression, Expr.JumpTo(ParentNode.EndLabel) ); return(block); }
/// <summary> /// Returns the code to concatenate two arrays. /// </summary> private NodeBase ArrayExpand(Context ctx) { var type = Resolve(ctx); var tmpArray = ctx.Scope.DeclareImplicit(ctx, type, false); var tmpLeft = ctx.Scope.DeclareImplicit(ctx, type, false); var tmpRight = ctx.Scope.DeclareImplicit(ctx, type, false); // a = <left> // b = <right> // c = new T[a.Length + b.Length] // Array.Copy(from: a, to: c, count: a.Length) // Array.Copy(from: b, startFrom: 0, to: c, startTo: a.Length, count: b.Length) return(Expr.Block( Expr.Set(tmpLeft, LeftOperand), Expr.Set(tmpRight, RightOperand), Expr.Set( tmpArray, Expr.Array( type.GetElementType(), Expr.Add( Expr.GetMember(Expr.Get(tmpLeft), "Length"), Expr.GetMember(Expr.Get(tmpRight), "Length") ) ) ), Expr.Invoke( "System.Array", "Copy", Expr.Get(tmpLeft), Expr.Get(tmpArray), Expr.GetMember(Expr.Get(tmpLeft), "Length") ), Expr.Invoke( "System.Array", "Copy", Expr.Get(tmpRight), Expr.Int(0), Expr.Get(tmpArray), Expr.GetMember(Expr.Get(tmpLeft), "Length"), Expr.GetMember(Expr.Get(tmpRight), "Length") ), Expr.Get(tmpArray) )); }
public void Should_allow_inner_loop_with_break_continue_in_catch_block() { var result = Expr.LocalVariable(typeof(int), "r"); var func = CreateFunc <int>( Expr.DeclareLocal(result, Expr.Constant(0)), Expr.TryCatch( Expr.Throw(Expr.New(typeof(Exception))), new CatchBlock(Expr.Block( Expr.Loop(Expr.Block( Expr.WriteLocal(result, Expr.Add(Expr.ReadLocal(result), Expr.Constant(1))), Expr.IfThen( Expr.Less(Expr.ReadLocal(result), Expr.Constant(2)), Expr.LoopContinue()), Expr.LoopBreak()))))), Expr.Return(Expr.ReadLocal(result))); Assert.That(func(), Is.EqualTo(2)); }
/// <summary> /// Returns the code to concatenate two dictionaries. /// </summary> private NodeBase DictExpand(Context ctx) { var keyValueTypes = LeftOperand.Resolve(ctx).GetGenericArguments(); var dictType = typeof(Dictionary <,>).MakeGenericType(keyValueTypes); var currType = typeof(KeyValuePair <,>).MakeGenericType(keyValueTypes); var tmpDict = ctx.Scope.DeclareImplicit(ctx, dictType, false); var tmpCurr = ctx.Scope.DeclareImplicit(ctx, currType, false); // a = new Dictionary<T, T2>(<left>) // foreach(var kvp in <right>) // a[kvp.Key] = kvp.Value return(Expr.Block( Expr.Set( tmpDict, Expr.New( dictType, Expr.Cast( LeftOperand, typeof(IDictionary <,>).MakeGenericType(keyValueTypes) ) ) ), Expr.For( tmpCurr, RightOperand, Expr.Block( Expr.SetIdx( Expr.Get(tmpDict), Expr.GetMember( Expr.Get(tmpCurr), "Key" ), Expr.GetMember( Expr.Get(tmpCurr), "Value" ) ) ) ), Expr.Get(tmpDict) )); }