// Returns a dynamic InvokeMember or Invoke expression, depending on the // Function expression. // public static DynamicExpression AnalyzeFunCallExpr( SymplFunCallExpr expr, AnalysisScope scope) { if (expr.Function is SymplDottedExpr) { SymplDottedExpr dottedExpr = (SymplDottedExpr)expr.Function; Expression objExpr; int length = dottedExpr.Exprs.Length; if (length > 1) { objExpr = AnalyzeDottedExpr( // create a new dot expression for the object that doesn't // include the last part new SymplDottedExpr( dottedExpr.ObjectExpr, RuntimeHelpers.RemoveLast(dottedExpr.Exprs)), scope ); } else { objExpr = AnalyzeExpr(dottedExpr.ObjectExpr, scope); } List <Expression> args = new List <Expression>(); args.Add(objExpr); args.AddRange(expr.Arguments.Select(a => AnalyzeExpr(a, scope))); // last expr must be an id var lastExpr = (SymplIdExpr)(dottedExpr.Exprs.Last()); return(Expression.Dynamic( scope.GetRuntime().GetInvokeMemberBinder( new InvokeMemberBinderKey( lastExpr.IdToken.Name, new CallInfo(expr.Arguments.Length))), typeof(object), args )); } else { var fun = AnalyzeExpr(expr.Function, scope); List <Expression> args = new List <Expression>(); args.Add(fun); args.AddRange(expr.Arguments.Select(a => AnalyzeExpr(a, scope))); // Use DynExpr so that I don't always have to have a delegate to call, // such as what happens with IPy interop. return(Expression.Dynamic( scope.GetRuntime() .GetInvokeBinder(new CallInfo(expr.Arguments.Length)), typeof(object), args )); } }
// Returns a chain of GetMember and InvokeMember dynamic expressions for // the dotted expr. // public static Expression AnalyzeDottedExpr(SymplDottedExpr expr, AnalysisScope scope) { var curExpr = AnalyzeExpr(expr.ObjectExpr, scope); foreach (var e in expr.Exprs) { if (e is SymplIdExpr) { curExpr = Expression.Dynamic( scope.GetRuntime() .GetGetMemberBinder(((SymplIdExpr)e).IdToken.Name), typeof(object), curExpr ); } else if (e is SymplFunCallExpr) { var call = (SymplFunCallExpr)e; List <Expression> args = new List <Expression>(); args.Add(curExpr); args.AddRange(call.Arguments.Select(a => AnalyzeExpr(a, scope))); curExpr = Expression.Dynamic( // Dotted exprs must be simple invoke members, a.b.(c ...) scope.GetRuntime().GetInvokeMemberBinder( new InvokeMemberBinderKey( ((SymplIdExpr)call.Function).IdToken.Name, new CallInfo(call.Arguments.Length))), typeof(object), args ); } else { throw new InvalidOperationException( "Internal: dotted must be IDs or Funs."); } } return(curExpr); }
// first sub form must be expr resulting in callable, but if it is dotted expr, // then eval the first N-1 dotted exprs and use invoke member or get member // on last of dotted exprs so that the 2..N sub forms are the arguments to // the invoke member. It's as if the call breaks into a block of a temp // assigned to the N-1 dotted exprs followed by an invoke member (or a get // member and call, which the runtime binder decides). The non-dotted expr // simply evals to an object that better be callable with the supplied args, // which may be none. // private SymplFunCallExpr ParseFunctionCall(Lexer lexer) { // First sub expr is callable object or invoke member expr. var fun = ParseExprAux(lexer); if (fun is SymplDottedExpr) { SymplDottedExpr dottedExpr = (SymplDottedExpr)fun; // Keywords ok as members. if (!(dottedExpr.Exprs.Last() is SymplIdExpr)) { throw new SymplParseException( "Function call with dotted expression for function must " + "end with ID Expr, not member invoke." + dottedExpr.Exprs.Last().ToString()); } } // Tail exprs are args. var args = ParseBody(lexer, "Unexpected EOF in arg list for " + fun.ToString()); return(new SymplFunCallExpr(fun, args)); }