Last active
December 30, 2015 20:29
-
-
Save RedHatter/7881160 to your computer and use it in GitHub Desktop.
A patch to the Vala and Genie compilers to add support for cascading member access. Includes vala unit tests. Recursive cascading now supported. Similar to described here: http://news.dartlang.org/2012/02/method-cascades-in-dart-posted-by-gilad.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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..45a985f | |
--- /dev/null | |
+++ b/tests/objects/cascade.vala | |
@@ -0,0 +1,53 @@ | |
+using GLib; | |
+ | |
+class Test : Object { | |
+ public bool method_called; | |
+ public bool arguments_called; | |
+ | |
+ public int variable; | |
+ | |
+ public string another; | |
+ | |
+ public void method () { | |
+ method_called = true; | |
+ } | |
+ | |
+ public void arguments (Arg arg) { | |
+ arguments_called = true; | |
+ } | |
+} | |
+ | |
+class Arg : Object { | |
+ public bool method_called; | |
+ | |
+ public int variable; | |
+ | |
+ public string another; | |
+ | |
+ public void method () { | |
+ method_called = true; | |
+ } | |
+} | |
+ | |
+static int main () { | |
+ Arg arg = new Arg (); | |
+ Test test = new Test () | |
+ ..variable = 0 | |
+ ..method () | |
+ ..arguments (arg | |
+ ..variable = 1 | |
+ ..another = "2" | |
+ ..method ()) | |
+ ..another = "3"; | |
+ | |
+ assert (test.variable == 0); | |
+ assert (test.another == "3"); | |
+ assert (test.method_called); | |
+ assert (test.arguments_called); | |
+ assert (arg.variable == 1); | |
+ assert (arg.another == "2"); | |
+ assert (arg.method_called); | |
+ | |
+ | |
+ return 0; | |
+} | |
\ No newline at end of file | |
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 <[email protected]> | |
+ */ | |
+ | |
+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/valagenieparser.vala b/vala/valagenieparser.vala | |
index 7abd943..0df41b1 100644 | |
--- a/vala/valagenieparser.vala | |
+++ b/vala/valagenieparser.vala | |
@@ -46,6 +46,8 @@ public class Vala.Genie.Parser : CodeVisitor { | |
/* hack needed to know if any part of an expression is a lambda one */ | |
bool current_expr_is_lambda; | |
+ Block current_block; | |
+ | |
const int BUFFER_SIZE = 32; | |
static List<TypeParameter> _empty_type_parameter_list; | |
@@ -414,7 +416,7 @@ public class Vala.Genie.Parser : CodeVisitor { | |
void skip_symbol_name () throws ParseError { | |
do { | |
skip_identifier (); | |
- } while (accept (TokenType.DOT)); | |
+ } while (accept (TokenType.DOT) || accept (TokenType.CASCADE)); | |
} | |
UnresolvedSymbol parse_symbol_name () throws ParseError { | |
@@ -423,7 +425,7 @@ public class Vala.Genie.Parser : CodeVisitor { | |
do { | |
string name = parse_identifier (); | |
sym = new UnresolvedSymbol (sym, name, get_src (begin)); | |
- } while (accept (TokenType.DOT)); | |
+ } while (accept (TokenType.DOT) || accept (TokenType.CASCADE)); | |
return sym; | |
} | |
@@ -656,7 +658,6 @@ public class Vala.Genie.Parser : CodeVisitor { | |
} | |
Expression parse_primary_expression () throws ParseError { | |
- var begin = get_location (); | |
Expression expr; | |
@@ -712,6 +713,13 @@ public class Vala.Genie.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) { | |
@@ -734,7 +742,7 @@ public class Vala.Genie.Parser : CodeVisitor { | |
case TokenType.OP_DEC: | |
expr = parse_post_decrement_expression (begin, expr); | |
break; | |
- | |
+ case TokenType.CASCADE: | |
default: | |
found = false; | |
break; | |
@@ -804,6 +812,19 @@ public class Vala.Genie.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 (); | |
@@ -1607,6 +1628,101 @@ public class Vala.Genie.Parser : CodeVisitor { | |
} | |
Expression parse_expression () throws ParseError { | |
+ 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 (); | |
+ } | |
+ | |
+ expr = parse_assignment (expr, variable); | |
+ } 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); | |
+ | |
+ 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 (); | |
+ } | |
+ } | |
+ | |
+ var stmt = new ExpressionStatement (expr, get_src (get_location ())); | |
+ current_block.add_statement (stmt); | |
+ } | |
+ | |
+ return return_val; | |
+ } | |
+ | |
+ Expression parse_before_cascade () throws ParseError { | |
if (current () == TokenType.DEF) { | |
var lambda = parse_lambda_expression (); | |
current_expr_is_lambda = true; | |
@@ -1615,36 +1731,15 @@ public class Vala.Genie.Parser : CodeVisitor { | |
current_expr_is_lambda = false; | |
} | |
- var begin = get_location (); | |
Expression expr = parse_conditional_expression (); | |
- 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 (); | |
- break; | |
- } | |
- } else { | |
- break; | |
- } | |
+ if (current () != TokenType.CASCADE) { | |
+ expr = parse_assignment (expr, null); | |
} | |
return expr; | |
} | |
- | |
Statement get_for_statement_type () throws ParseError { | |
var begin = get_location (); | |
@@ -1816,6 +1911,8 @@ public class Vala.Genie.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); | |
@@ -1881,6 +1978,7 @@ public class Vala.Genie.Parser : CodeVisitor { | |
var begin = get_location (); | |
expect (TokenType.INDENT); | |
var block = new Block (get_src (begin)); | |
+ current_block = block; | |
parse_statements (block); | |
if (!accept (TokenType.DEDENT)) { | |
// only report error if it's not a secondary error | |
@@ -3860,7 +3958,7 @@ public class Vala.Genie.Parser : CodeVisitor { | |
expr.add_type_argument (type_arg); | |
} | |
} | |
- } while (accept (TokenType.DOT)); | |
+ } while (accept (TokenType.DOT) || accept (TokenType.CASCADE)); | |
return expr; | |
} | |
} | |
diff --git a/vala/valageniescanner.vala b/vala/valageniescanner.vala | |
index e2c5466..cb3f0cb 100644 | |
--- a/vala/valageniescanner.vala | |
+++ b/vala/valageniescanner.vala | |
@@ -1013,6 +1013,9 @@ public class Vala.Genie.Scanner { | |
if (current[0] == '.' && current[1] == '.') { | |
type = TokenType.ELLIPSIS; | |
current += 2; | |
+ } else if (current[0] == '.') { | |
+ type = TokenType.CASCADE; | |
+ current++; | |
} | |
} | |
break; | |
diff --git a/vala/valagenietokentype.vala b/vala/valagenietokentype.vala | |
index 920a96a..fdb1920 100644 | |
--- a/vala/valagenietokentype.vala | |
+++ b/vala/valagenietokentype.vala | |
@@ -44,6 +44,7 @@ public enum Vala.Genie.TokenType { | |
BITWISE_OR, | |
BREAK, | |
CARRET, | |
+ CASCADE, | |
CASE, | |
CHARACTER_LITERAL, | |
CLASS, | |
@@ -193,6 +194,7 @@ public enum Vala.Genie.TokenType { | |
case BITWISE_OR: return "`|'"; | |
case BREAK: return "`break'"; | |
case CARRET: return "`^'"; | |
+ case CASCADE: return "`.."; | |
case CASE: return "`case'"; | |
case CHARACTER_LITERAL: return "character literal"; | |
case CLASS: return "`class'"; | |
diff --git a/vala/valaparser.vala b/vala/valaparser.vala | |
index c465a8e..0c6c5ea 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); | |
+ } | |
+ | |
+ 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 +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); | |
+ | |
+ 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 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment