diff --git a/vala/Makefile.am b/vala/Makefile.am
index ee8d410..300c56c 100644
--- a/vala/Makefile.am
+++ b/vala/Makefile.am
@@ -29,6 +29,7 @@ libvalacore_la_VALASOURCES = \
 	valabooleanliteral.vala \
 	valabooleantype.vala \
 	valabreakstatement.vala \
+	valacascadevariable.vala \
 	valacastexpression.vala \
 	valacatchclause.vala \
 	valacharacterliteral.vala \
diff --git a/vala/valacascadevariable.vala b/vala/valacascadevariable.vala
new file mode 100644
index 0000000..77cb45e
--- /dev/null
+++ b/vala/valacascadevariable.vala
@@ -0,0 +1,39 @@
+/* valacascadevariable.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.CascadeVariable : LocalVariable {
+	private static int count = 0;
+
+	public CascadeVariable (Expression initializer, SourceReference? source_reference = null) {
+		base (null, "cascade_"+count.to_string (), initializer, source_reference);
+		count++;
+	}
+
+	public MemberAccess get_access () {
+		return new MemberAccess (null, name, null);
+	}
+}
\ No newline at end of file
diff --git a/vala/valaparser.vala b/vala/valaparser.vala
index c465a8e..a299223 100644
--- a/vala/valaparser.vala
+++ b/vala/valaparser.vala
@@ -37,6 +37,8 @@ public class Vala.Parser : CodeVisitor {
 	// number of tokens in buffer
 	int size;
 
+	Block current_block;
+
 	Comment comment;
 
 	const int BUFFER_SIZE = 32;
@@ -353,7 +355,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 {
@@ -370,7 +372,7 @@ public class Vala.Parser : CodeVisitor {
 				continue;
 			}
 			sym = new UnresolvedSymbol (sym, name, get_src (begin));
-		} while (accept (TokenType.DOT));
+		} while (accept (TokenType.CASCADE) || accept (TokenType.DOT));
 		return sym;
 	}
 
@@ -585,8 +587,6 @@ public class Vala.Parser : CodeVisitor {
 	}
 
 	Expression parse_primary_expression () throws ParseError {
-		var begin = get_location ();
-
 		Expression expr;
 
 		switch (current ()) {
@@ -640,6 +640,13 @@ public class Vala.Parser : CodeVisitor {
 			break;
 		}
 
+		return parse_inner_primary_expression (expr, expr);
+	}
+
+	Expression parse_inner_primary_expression (Expression inner, Expression cascade_root) 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 +669,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 +755,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 +1477,109 @@ public class Vala.Parser : CodeVisitor {
 	}
 
 	Expression parse_expression () throws ParseError {
-		if (is_lambda_expression ()) {
-			return parse_lambda_expression ();
+		var expr = parse_before_cascade ();
+
+		CascadeVariable variable = null;
+		if (current () == TokenType.CASCADE) {
+			variable =  new CascadeVariable (expr, get_src (get_location()));
+			var declaration = new DeclarationStatement (variable, get_src (get_location ()));
+			current_block.add_statement (declaration);
+			expr = parse_cascade_expression (expr, variable);
 		}
 
+		return parse_assignment (expr, variable);
+	}
+
+	Expression parse_assignment (Expression lhs, CascadeVariable? variable) 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) {
+				var stmt = new ExpressionStatement (expr, get_src (get_location ()));
+				current_block.add_statement (stmt);
+				expr = parse_cascade_expression (expr, variable);
+			} else if (variable != null) {
+				var stmt = new ExpressionStatement (expr, get_src (get_location ()));
+				current_block.add_statement (stmt);
+				expr = variable.get_access ();
+			}
+
+			expr = parse_assignment (expr, variable);
+		} 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) {
+					var stmt = new ExpressionStatement (expr, get_src (get_location ()));
+					current_block.add_statement (stmt);
+					expr = parse_cascade_expression (expr, variable);
+				} else if (variable != null) {
+					var stmt = new ExpressionStatement (expr, get_src (get_location ()));
+					current_block.add_statement (stmt);
+					expr = variable.get_access ();
+				}
 
-		Expression expr = parse_conditional_expression ();
+				expr = parse_assignment (expr, variable);
+			} 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));
-				} else {
-					prev ();
+		return expr;
+	}
+
+	Expression parse_cascade_expression (Expression inner, CascadeVariable? variable) throws ParseError {
+		Expression prv = null;
+		Expression return_val = null;
+		Expression expr = inner;
+		if (current () == TokenType.CASCADE) {
+			expr = parse_cascade_member_access (get_location(), variable.get_access ());
+		}
+		var ma = expr as MemberAccess;
+		while (expr != prv && ma != null) {
+			prv = expr;
+
+			expr = parse_inner_primary_expression (ma, ma);
+
+			if (current () == TokenType.CASCADE) {
+				ma = parse_cascade_member_access (get_location(), variable.get_access ()) as MemberAccess;
+			} else {
+				ma = null;
+			}
+
+			if (expr == prv || ma == null) {
+				if (get_assignment_operator (current ()) != AssignmentOperator.NONE) {
+					return_val = expr;
 					break;
+				} else {
+					return_val = variable.get_access ();
 				}
-			} else {
-				break;
 			}
+
+			var stmt = new ExpressionStatement (expr, get_src (get_location ()));
+			current_block.add_statement (stmt);
+		}
+
+		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 +1714,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);
@@ -1730,6 +1827,7 @@ public class Vala.Parser : CodeVisitor {
 		var begin = get_location ();
 		expect (TokenType.OPEN_BRACE);
 		var block = new Block (get_src (begin));
+		current_block = block;
 		parse_statements (block);
 		if (!accept (TokenType.CLOSE_BRACE)) {
 			// only report error if it's not a secondary error
@@ -3420,6 +3518,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 +3559,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..1461020 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

