/// <summary>
 /// The node has to be transformed before being passed
 /// </summary>
 /// <param name="n"></param>
 /// <returns></returns>
 public Expression ToExpr(Node n)
 {
     switch (n.Label)
     {
         case "Empty":
             return Noop;
         case "Return":
             return (n.Count > 0)
                 ? Expression.Return(GetReturnTarget(), ToExpr(n[0]), typeof(Object))
                 : Expression.Return(GetReturnTarget(), Expression.Constant(typeof(Object), null));
         case "If":
             return n.Count > 2
                 ? Expression.IfThenElse(Expression.Convert(ToExpr(n[0]), typeof(Boolean)), ToExpr(n[1]), ToExpr(n[2]))
                 : Expression.IfThen(Expression.Convert(ToExpr(n[0]), typeof(Boolean)), ToExpr(n[1]));
         case "Else":
             return ToExpr(n[0]);
         case "For":                    
             return CompileForLoop(ToExpr(n[0]), ToExpr(n[1]), ToExpr(n[2]), ToExpr(n[3]));
         case "VarDecl":
             {
                 var r = AddBinding(Expression.Variable(typeof(object), n[0].Text));
                 if (n.Count > 1)
                     r = Expression.Assign(r, ToExpr(n[1]));
                 return r;
             }
         case "Block":
             return ScopedExpr(() => Expression.Block(n.Nodes.Select(ToExpr)));
         case "TertiaryExpr":
             return Expression.Condition(ToExpr(n[0]), ToExpr(n[1]), ToExpr(n[2]));
         case "BinaryExpr":
             return Expression.Call(null, Primitives.GetMethodFromBinaryOperator(n[1].Text), ToExpr(n[0]), ToExpr(n[2]));
         case "AssignExpr":
             return Expression.Assign(ToExpr(n[0]), ToExpr(n[2]));
         case "FieldExpr":
             return Expression.Field(ToExpr(n[0]), n[1].Text);
         case "IndexExpr":
             return CompileIndexExpr(ToExpr(n[0]), ToExpr(n[2]));
         case "CallExpr":
             return CompileCallExpr(ToExpr(n[0]), n[1].Nodes.Select(ToExpr));
         case "MethodCallExpr":
             {
                 var self = ToExpr(n[0]);
                 var func = Expression.Field(self, n[1].Text);
                 var args = new List<Expression>() { self };
                 args.AddRange(n[2].Nodes.Select(ToExpr));
                 return CompileCallExpr(func, args);
             }
         case "PrefixExpr":
             switch (n[0].Text)
             {
                 case "!": return Expression.Not(ToExpr(n[1]));
                 case "~": return Expression.OnesComplement(ToExpr(n[1]));
                 case "-": return Expression.Negate(ToExpr(n[1]));
                 default: throw new Exception("Unrecognized prefix operator " + n[0].Text);
             }
         case "NewExpr":
             return ScopedExpr(() => 
                 {
                     AddBinding("this", Expression.Constant(new JsonObject()));
                     return ToExpr(n[0]);
                 });                    
         case "ParenExpr":
             return ToExpr(n[0]);
         case "AnonFunc":
             {
                 var ps = n[0].Nodes.Select(x => Expression.Parameter(typeof(Object), x.Text));
                 return CreateStatementLambda(ps, () => ToExpr(n[1]));
             };
         case "Object":
             throw new NotImplementedException();
         case "Array":
             return Expression.NewArrayInit(typeof(object), n.Nodes.Select(ToExpr));
         case "Identifier":
             return Lookup(n.Text);
         case "String":
             return Expression.Constant(Utilities.Unquote(n.Text));
         case "Integer":
             return Expression.Convert(Expression.Constant(int.Parse(n.Text)), typeof(Object));
         case "Float":
             return Expression.Convert(Expression.Constant(float.Parse(n.Text)), typeof(Object));
         case "True":
             return Expression.Convert(Expression.Constant(true), typeof(Object));
         case "False":
             return Expression.Convert(Expression.Constant(false), typeof(Object));
         case "Null":
             return Expression.Constant(null);
         default:
             throw new Exception("Unrecognized node type " + n.Label);
     }
 }