diff --git a/tests/Makefile.am b/tests/Makefile.am
index bcf82e1..eb158d7 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -112,6 +112,7 @@ TESTS = \
 	delegates/bug639751.vala \
 	delegates/bug659778.vala \
 	delegates/bug703804.vala \
+	objects/cascade.vala \
 	objects/chainup.vala \
 	objects/classes.vala \
 	objects/fields.vala \
diff --git a/tests/objects/cascade.vala b/tests/objects/cascade.vala
new file mode 100644
index 0000000..e40babb
--- /dev/null
+++ b/tests/objects/cascade.vala
@@ -0,0 +1,69 @@
+using GLib;
+
+class Test : Object {
+	public bool method_called;
+	public bool arguments_called;
+	public int arguments_num;
+
+	public int variable;
+
+	public string another;
+
+	public void method () {
+		method_called = true;
+	}
+
+	public void arguments (Arg arg, int num) {
+		arguments_called = true;
+		arguments_num = num;
+	}
+}
+
+class Arg : Object {
+	public bool method_called;
+
+	public int variable;
+
+	public string another;
+
+	public void method () {
+		method_called = true;
+	}
+}
+
+class MainBlock {
+
+	private static int private_method () {
+		return 4;
+	}
+
+	public static int main () {
+		Arg arg = new Arg ();
+		Test test = new Test ()
+			..variable = 0
+			..method ()
+			..arguments (arg
+				..variable = 1
+				..another = "2"
+				..method (), private_method ())
+			..another = "3";
+
+		(true) ? test : test..variable = 1..another = "4" ;
+
+		assert (test.variable == 0);
+		assert (test.another == "3");
+		assert (test.method_called);
+		assert (test.arguments_called);
+		assert (test.arguments_num == 4);
+		assert (arg.variable == 1);
+		assert (arg.another == "2");
+		assert (arg.method_called);
+
+
+		return 0;
+	}
+}
+
+static int main () {
+	return MainBlock.main ();
+}
\ No newline at end of file
diff --git a/vala/Makefile.am b/vala/Makefile.am
index ee8d410..e69c1c1 100644
--- a/vala/Makefile.am
+++ b/vala/Makefile.am
@@ -29,6 +29,8 @@ libvalacore_la_VALASOURCES = \
 	valabooleanliteral.vala \
 	valabooleantype.vala \
 	valabreakstatement.vala \
+	valacascadecall.vala \
+	valacascademethod.vala \
 	valacastexpression.vala \
 	valacatchclause.vala \
 	valacharacterliteral.vala \
diff --git a/vala/valacascadecall.vala b/vala/valacascadecall.vala
new file mode 100644
index 0000000..5779f5e
--- /dev/null
+++ b/vala/valacascadecall.vala
@@ -0,0 +1,40 @@
+/* valacascadecall.vala
+*
+* Copyright (C) 2006-2010  Jürg Billeter
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+*
+* Author:
+* 	Jürg Billeter <j@bitron.ch>
+*/
+
+using GLib;
+
+/**
+* Represents a variable generated for a cascade.
+*/
+public class Vala.CascadeCall : MethodCall {
+	public CascadeCall (Expression call, SourceReference? source_reference = null) {
+		base (call, source_reference);
+	}
+
+	public override bool check (CodeContext context) {
+		foreach (Expression arg in get_argument_list ()) {
+			arg.check (context);
+		}
+
+		return base.check (context);
+	}
+}
diff --git a/vala/valacascademethod.vala b/vala/valacascademethod.vala
new file mode 100644
index 0000000..612125f
--- /dev/null
+++ b/vala/valacascademethod.vala
@@ -0,0 +1,117 @@
+/* valacascademethod.vala
+*
+* Copyright (C) 2006-2010  Jürg Billeter
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+*
+* Author:
+* 	Jürg Billeter <j@bitron.ch>
+*/
+
+using GLib;
+
+/**
+* Represents a variable generated for a cascade.
+*/
+public class Vala.CascadeMethod : Method {
+	private static int count = 0;
+	private Expression root_expr;
+	private List<MemberAccess> argument_list = new ArrayList<MemberAccess> ();
+
+	public CascadeMethod (Expression root_expr, SourceReference? source_reference = null) {
+		base ("Cascade_"+count.to_string (), null, source_reference);
+		body = new Block (source_reference);
+		add_parameter (new Parameter ("cascade", null, source_reference));
+		this.root_expr = root_expr;
+		count++;
+	}
+
+	public void add_expression (Expression expr) {
+		var stmt = new ExpressionStatement (expr, source_reference);
+		body.add_statement (stmt);
+
+		search  (expr);
+	}
+
+	private void search (Expression? expr) {
+		if (expr is MethodCall) {
+			var mc = (MethodCall) expr;
+			var ma = (MemberAccess) mc.call;
+			search (ma.inner);
+			foreach (var arg in mc.get_argument_list()) {
+				search (arg);
+			}
+		} else if (expr is Assignment) {
+			var a = (Assignment) expr;
+			search (a.left);
+			search (a.right);
+		} else if (expr is MemberAccess) {
+			var ma = (MemberAccess) expr;
+			if  (ma.inner != null) {
+				search (ma.inner);
+			} else if (ma.member_name != "cascade") {
+				foreach (var arg in argument_list) {
+					if (arg.member_name == ma.member_name) {
+						return;
+					}
+				}
+				add_parameter (new Parameter (ma.member_name, null, source_reference));
+				argument_list.add (ma);
+			}
+		}
+	}
+
+	public MethodCall get_call () {
+		var ma = new MemberAccess.simple (name, source_reference);
+		var mc = new CascadeCall (ma, source_reference);
+		mc.add_argument (root_expr);
+		for (var i = 0; i < argument_list.size; i++) {
+			var arg = new MemberAccess.simple (argument_list[i].member_name, source_reference);
+			mc.add_argument (arg);
+			argument_list.remove_at (i);
+			argument_list.insert (i, arg);
+		}
+
+		return mc;
+	}
+
+	public MemberAccess get_access () {
+		var ma = new MemberAccess.simple ("cascade", source_reference);
+		return ma;
+	}
+
+	public override bool check (CodeContext context) {
+		var continue_check = root_expr.value_type != null;
+		foreach (var arg in argument_list) {
+			continue_check = arg.value_type != null && continue_check;
+		}
+		if (!continue_check) {
+			return true;
+		}
+
+		var return_stmt = new ReturnStatement (get_access (), source_reference);
+		body.add_statement (return_stmt);
+
+		var ps = get_parameters ();
+		return_type = root_expr.value_type.copy ();
+		ps[0].variable_type = root_expr.value_type.copy ();
+
+		for (var i = 1; i < ps.size; i++) {
+			ps[i].variable_type = argument_list[i-1].value_type.copy ();
+		}
+
+		return base.check (context);
+	}
+}
\ No newline at end of file
diff --git a/vala/valamethod.vala b/vala/valamethod.vala
index afc7053..a0c6ccc 100644
--- a/vala/valamethod.vala
+++ b/vala/valamethod.vala
@@ -211,9 +211,10 @@ public class Vala.Method : Subroutine {
 	 * @param source      reference to source code
 	 * @return            newly created method
 	 */
-	public Method (string? name, DataType return_type, SourceReference? source_reference = null, Comment? comment = null) {
+	public Method (string? name, DataType? return_type, SourceReference? source_reference = null, Comment? comment = null) {
 		base (name, source_reference, comment);
-		this.return_type = return_type;
+		if (return_type != null)
+			this.return_type = return_type;
 	}
 
 	/**
diff --git a/vala/valaparameter.vala b/vala/valaparameter.vala
index 2a89e69..f3e2723 100644
--- a/vala/valaparameter.vala
+++ b/vala/valaparameter.vala
@@ -80,7 +80,9 @@ public class Vala.Parameter : Variable {
 
 	public override void accept_children (CodeVisitor visitor) {
 		if (!ellipsis) {
-			variable_type.accept (visitor);
+			if (variable_type != null) {
+				variable_type.accept (visitor);
+			}
 			
 			if (initializer != null) {
 				initializer.accept (visitor);
diff --git a/vala/valaparser.vala b/vala/valaparser.vala
index c465a8e..ecac8ed 100644
--- a/vala/valaparser.vala
+++ b/vala/valaparser.vala
@@ -39,6 +39,9 @@ public class Vala.Parser : CodeVisitor {
 
 	Comment comment;
 
+	Class current_class = null;
+	MemberBinding binding = MemberBinding.INSTANCE;
+
 	const int BUFFER_SIZE = 32;
 
 	static List<TypeParameter> _empty_type_parameter_list;
@@ -353,7 +356,7 @@ public class Vala.Parser : CodeVisitor {
 	void skip_symbol_name () throws ParseError {
 		do {
 			skip_identifier ();
-		} while (accept (TokenType.DOT) || accept (TokenType.DOUBLE_COLON));
+		} while (accept (TokenType.CASCADE) || accept (TokenType.DOT) || accept (TokenType.DOUBLE_COLON));
 	}
 
 	UnresolvedSymbol parse_symbol_name () throws ParseError {
@@ -585,8 +588,6 @@ public class Vala.Parser : CodeVisitor {
 	}
 
 	Expression parse_primary_expression () throws ParseError {
-		var begin = get_location ();
-
 		Expression expr;
 
 		switch (current ()) {
@@ -640,6 +641,13 @@ public class Vala.Parser : CodeVisitor {
 			break;
 		}
 
+		return parse_inner_primary_expression (expr);
+	}
+
+	Expression parse_inner_primary_expression (Expression inner) throws ParseError {
+		var expr = inner;
+		var begin = get_location ();
+
 		// process primary expressions that start with an inner primary expression
 		bool found = true;
 		while (found) {
@@ -662,6 +670,7 @@ public class Vala.Parser : CodeVisitor {
 			case TokenType.OP_DEC:
 				expr = parse_post_decrement_expression (begin, expr);
 				break;
+			case TokenType.CASCADE:
 			default:
 				found = false;
 				break;
@@ -747,6 +756,19 @@ public class Vala.Parser : CodeVisitor {
 		return expr;
 	}
 
+	Expression parse_cascade_member_access (SourceLocation begin, Expression inner) throws ParseError {
+		expect (TokenType.CASCADE);
+		string id = parse_identifier ();
+		List<DataType> type_arg_list = parse_type_argument_list (true);
+		var expr = new MemberAccess (inner, id, get_src (begin));
+		if (type_arg_list != null) {
+			foreach (DataType type_arg in type_arg_list) {
+				expr.add_type_argument (type_arg);
+			}
+		}
+		return expr;
+	}
+
 	Expression parse_pointer_member_access (SourceLocation begin, Expression inner) throws ParseError {
 		expect (TokenType.OP_PTR);
 		string id = parse_identifier ();
@@ -1456,35 +1478,110 @@ public class Vala.Parser : CodeVisitor {
 	}
 
 	Expression parse_expression () throws ParseError {
-		if (is_lambda_expression ()) {
-			return parse_lambda_expression ();
+		var expr = parse_before_cascade ();
+
+		CascadeMethod method = null;
+		if (current () == TokenType.CASCADE) {
+			method = new CascadeMethod (expr, get_src (get_location()));
+			method.binding = binding;
+			if (current_class == null) {
+				context.root.add_method (method);
+			} else {
+				current_class.add_method (method);
+			}
+			expr = parse_cascade_expression (expr, method);
 		}
 
+		return parse_assignment (expr, method);
+	}
+
+	Expression parse_assignment (Expression lhs, CascadeMethod? method) throws ParseError {
+		var expr = lhs;
 		var begin = get_location ();
+		var operator = get_assignment_operator (current ());
+		if (operator != AssignmentOperator.NONE) {
+			next ();
+			var rhs = parse_before_cascade ();
+			expr = new Assignment (expr, rhs, operator, get_src (begin));
+			if (current () == TokenType.CASCADE) {
+				method.add_expression (expr);
+				expr = parse_cascade_expression (expr, method);
+			} else if (method != null) {
+				method.add_expression (expr);
+				expr = method.get_call ();
+			}
+
+			expr = parse_assignment (expr, method);
+		} else if (current () == TokenType.OP_GT) { // >>=
+			char* first_gt_pos = tokens[index].begin.pos;
+			next ();
+			// only accept >>= when there is no space between the two > signs
+			if (current () == TokenType.OP_GE && tokens[index].begin.pos == first_gt_pos + 1) {
+				next ();
+				var rhs = parse_before_cascade ();
+				expr = new Assignment (expr, rhs, AssignmentOperator.SHIFT_RIGHT, get_src (begin));
+				if (current () == TokenType.CASCADE) {
+					method.add_expression (expr);
+					expr = parse_cascade_expression (expr, method);
+				} else if (method != null) {
+					method.add_expression (expr);
+					expr = method.get_call ();
+				}
 
-		Expression expr = parse_conditional_expression ();
+				expr = parse_assignment (expr, method);
+			} else {
+				prev ();
+			}
+		}
 
-		while (true) {
-			var operator = get_assignment_operator (current ());
-			if (operator != AssignmentOperator.NONE) {
-				next ();
-				var rhs = parse_expression ();
-				expr = new Assignment (expr, rhs, operator, get_src (begin));
-			} else if (current () == TokenType.OP_GT) { // >>=
-				char* first_gt_pos = tokens[index].begin.pos;
-				next ();
-				// only accept >>= when there is no space between the two > signs
-				if (current () == TokenType.OP_GE && tokens[index].begin.pos == first_gt_pos + 1) {
-					next ();
-					var rhs = parse_expression ();
-					expr = new Assignment (expr, rhs, AssignmentOperator.SHIFT_RIGHT, get_src (begin));
+		return expr;
+	}
+
+	Expression parse_cascade_expression (Expression inner, CascadeMethod? method) throws ParseError {
+		Expression prv = null;
+		Expression return_val = null;
+		Expression expr = inner;
+		if (current () == TokenType.CASCADE) {
+			expr = parse_cascade_member_access (get_location(), method.get_access ());
+		}
+		var ma = expr as MemberAccess;
+		while (expr != prv && ma != null) {
+			prv = expr;
+
+			expr = parse_inner_primary_expression (ma);
+
+			if (current () == TokenType.CASCADE) {
+				ma = parse_cascade_member_access (get_location(), method.get_access ()) as MemberAccess;
+			} else {
+				ma = null;
+			}
+
+			if (expr == prv || ma == null) {
+				if (get_assignment_operator (current ()) != AssignmentOperator.NONE) {
+					return_val = expr;
+					break;
 				} else {
-					prev ();
+					method.add_expression (expr);
+					return_val = method.get_call ();
 					break;
 				}
-			} else {
-				break;
 			}
+
+			method.add_expression (expr);
+		}
+
+		return return_val;
+	}
+
+	Expression parse_before_cascade () throws ParseError {
+		if (is_lambda_expression ()) {
+			return parse_lambda_expression ();
+		}
+
+		Expression expr = parse_conditional_expression ();
+
+		if (current () != TokenType.CASCADE) {
+			expr = parse_assignment (expr, null);
 		}
 
 		return expr;
@@ -1619,6 +1716,8 @@ public class Vala.Parser : CodeVisitor {
 		case TokenType.OP_GT: // >>=
 		// member access
 		case TokenType.DOT:
+		// cascade member access
+		case TokenType.CASCADE:
 		// pointer member access
 		case TokenType.OP_PTR:
 			rollback (begin);
@@ -2174,6 +2273,7 @@ public class Vala.Parser : CodeVisitor {
 		var begin = get_location ();
 
 		var method = new Method ("main", new VoidType (), get_src (begin));
+		binding = MemberBinding.STATIC;
 		method.body = new Block (get_src (begin));
 		parse_statements (method.body);
 		if (current () != TokenType.EOF) {
@@ -2488,6 +2588,7 @@ public class Vala.Parser : CodeVisitor {
 		}
 
 		var cl = new Class (sym.name, get_src (begin), comment);
+		current_class = cl;
 		cl.access = access;
 		if (ModifierFlags.ABSTRACT in flags) {
 			cl.is_abstract = true;
@@ -2526,6 +2627,8 @@ public class Vala.Parser : CodeVisitor {
 			}
 			result = next;
 		}
+
+		current_class = null;
 	}
 
 	void parse_constant_declaration (Symbol parent, List<Attribute>? attrs) throws ParseError {
@@ -2635,6 +2738,7 @@ public class Vala.Parser : CodeVisitor {
 		} else if (ModifierFlags.CLASS in flags) {
 			method.binding = MemberBinding.CLASS;
 		}
+		binding = method.binding;
 		if (ModifierFlags.ASYNC in flags) {
 			method.coroutine = true;
 		}
@@ -3420,6 +3524,7 @@ public class Vala.Parser : CodeVisitor {
 				case TokenType.SEMICOLON:
 				case TokenType.COMMA:
 				case TokenType.DOT:
+				case TokenType.CASCADE:
 				case TokenType.INTERR:
 				case TokenType.OP_EQ:
 				case TokenType.OP_NE:
@@ -3460,7 +3565,7 @@ public class Vala.Parser : CodeVisitor {
 			}
 
 			first = false;
-		} while (accept (TokenType.DOT));
+		} while (accept (TokenType.DOT) || accept (TokenType.CASCADE));
 		return expr;
 	}
 
diff --git a/vala/valascanner.vala b/vala/valascanner.vala
index 19eb5c4..7b909f4 100644
--- a/vala/valascanner.vala
+++ b/vala/valascanner.vala
@@ -873,6 +873,9 @@ public class Vala.Scanner {
 					if (current[0] == '.' && current[1] == '.') {
 						type = TokenType.ELLIPSIS;
 						current += 2;
+					} else if (current[0] == '.') {
+						type = TokenType.CASCADE;
+						current++;
 					}
 				}
 				break;
diff --git a/vala/valatokentype.vala b/vala/valatokentype.vala
index 91b50b9..50b68e0 100644
--- a/vala/valatokentype.vala
+++ b/vala/valatokentype.vala
@@ -43,6 +43,7 @@ public enum Vala.TokenType {
 	BREAK,
 	CARRET,
 	CASE,
+	CASCADE,
 	CATCH,
 	CHARACTER_LITERAL,
 	CLASS,
@@ -175,6 +176,7 @@ public enum Vala.TokenType {
 		case BREAK: return "`break'";
 		case CARRET: return "`^'";
 		case CASE: return "`case'";
+		case CASCADE: return "`..";
 		case CATCH: return "`catch'";
 		case CHARACTER_LITERAL: return "character literal";
 		case CLASS: return "`class'";
-- 
1.9.3

