private static Expression CompileObj(Obj obj, Scope scope)
        {
            var temp = scope.FreeVariable("obj");
            var expressions = new List<Expression>
            {
                Expression.Assign(temp, Expression.New(ExpandoConstructor))
            };

            foreach (var @base in obj.Properties)
            {
                var property = @base as Assign;

                foreach (var name in CompileToNames(property.Variable))
                {
                    var expression = Compile(property.Value, scope);

                    expressions.Add(Expression.Dynamic(scope.GetRuntime().SetMemberBinders.Get(name), typeof(object), temp, expression));
                }
            }

            expressions.Add(temp);

            return Expression.Block(expressions);
        }
        public static Expression CompileValue(Value node, Scope scope)
        {
            var curExpr = Compile(node.Base, scope);

            foreach (var name in CompileToNames(node.Properties))
            {
                curExpr = Expression.Dynamic(scope.GetRuntime().GetMemberBinders.Get(name),
                    typeof (object),
                    curExpr
                );
            }

            Helper.IsNotNull(curExpr);

            return curExpr;
        }
        public static Expression CompileOp(Op op, Scope scope)
        {
            var ischain = op.IsChainable && op.First is Op && ((Op)op.First).IsChainable;

            if (op.Second == null)
                return CompileUnary(op, scope);

            if (ischain)
                return CompileChain(op, scope);

            if (op.Operator == "?")
                return CompileExistence(op, scope);

            var first = Compile(op.First, scope);
            var second = Compile(op.Second, scope);

            var operation = GetExpressionType(op.Operator);
            return Expression.Dynamic(
                scope.GetRuntime().BinaryOperationBinders.Get(operation),
                typeof(object),
                first,
                second
            );
        }
        public static Expression CompileChain(Op op, Scope scope)
        {
            //for a chain we need to convert the second param of the first to a local. then we can do two comparisons and merge them with &&
            var first = op.First as Op;

            var refName = scope.FreeVariableName("ref");

            first.Second = first.Second.Cache(refName);

            var fst = Compile(first, scope);
            var scd = Compile(new Op {First = first.Second, Second = op.Second, Operator = op.Operator}, scope);

            return Expression.Dynamic(
                scope.GetRuntime().BinaryOperationBinders.Get(ExpressionType.And),
                typeof (object),
                fst, scd);
        }
        public static Expression CompileCall(Call node, Scope scope)
        {
            var args = new List<Expression>();
            args.AddRange(node.Arguments.Select(arg => Compile(arg, scope)));

            return GetMemberInvocaton(node.Variable, scope,
                (memberObject, name) =>
                {
                    args.Insert(0, memberObject);
                    return Expression.Dynamic(scope.GetRuntime().InvokeMemberBinders.Get(new InvokeMemberBinderKey(name, new CallInfo(node.Arguments.Count))), typeof(object), args);
                },
                (memberObject) =>
                {
                    args.Insert(0, memberObject);
                    return Expression.Dynamic(scope.GetRuntime().InvokeBinders.Get(new CallInfo(node.Arguments.Count)), typeof (object), args);
                });
        }
        public static Expression CompileAssign(Assign node, Scope scope)
        {
            var variable = node.Variable as Value;
            var value = node.Value;

            if (variable != null)
            {
                if (variable.IsObject)
                {
                    var obj = variable.Base as Obj;
                    return CompileObjectAssignment(obj, value, scope);
                }

                if (!node.Variable.IsAssignable)
                    throw new InvalidOperationException("Variable is not assignable");
                if (!variable.HasProperties)
                {
                    foreach(var name in CompileToNames(variable))
                        scope.Add(name, VariableType.Variable);
                }
            }

            var right = Compile(value, scope);

            return GetMemberInvocaton(node.Variable, scope,
                        (memberObject, name) => Expression.Dynamic(scope.GetRuntime().SetMemberBinders.Get(name), typeof (object), memberObject, right),
                        (memberObject) => Expression.Assign(memberObject, right));
        }