/* valaparser.vala * * Copyright (C) 2006-2013 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 */ using GLib; /** * Code visitor parsing all Vala source files. */ public class Vala.Parser : CodeVisitor { Scanner scanner; CodeContext context; // token buffer TokenInfo[] tokens; // index of current token in buffer int index; // number of tokens in buffer int size; Comment comment; const int BUFFER_SIZE = 32; static List _empty_type_parameter_list; static int next_local_func_id = 0; PartialInfo[] partials; struct TokenInfo { public TokenType type; public SourceLocation begin; public SourceLocation end; } struct PartialInfo { public Symbol parent; public SourceLocation begin; public List? attributes; } [Flags] enum ModifierFlags { NONE = 0, ABSTRACT, CLASS, EXTERN, INLINE, NEW, OVERRIDE, STATIC, VIRTUAL, ASYNC, SEALED, PARTIAL } public Parser () { tokens = new TokenInfo[BUFFER_SIZE]; } /** * Parses all .vala and .vapi source files in the specified code * context and builds a code tree. * * @param context a code context */ public void parse (CodeContext context) { this.context = context; context.accept (this); this.context = null; } public override void visit_source_file (SourceFile source_file) { if ((context != null && context.run_output) || source_file.filename.has_suffix (".vala") || source_file.filename.has_suffix (".vapi")) { parse_file (source_file); } } inline bool next () { index = (index + 1) % BUFFER_SIZE; size--; if (size <= 0) { SourceLocation begin, end; TokenType type = scanner.read_token (out begin, out end); tokens[index] = { type, begin, end }; size = 1; } return (tokens[index].type != TokenType.EOF); } inline void prev () { index = (index - 1 + BUFFER_SIZE) % BUFFER_SIZE; size++; assert (size <= BUFFER_SIZE); } inline void safe_prev () { switch (previous ()) { case TokenType.DOT: case TokenType.DOUBLE_COLON: break; default: prev (); break; } } inline TokenType current () { return tokens[index].type; } inline TokenType previous () { return tokens[(index - 1 + BUFFER_SIZE) % BUFFER_SIZE].type; } inline bool accept (TokenType type) { if (current () == type) { next (); return true; } return false; } void report_parse_error (ParseError e) { var begin = get_location (); next (); Report.error (get_src (begin), "syntax error, %s", e.message); } inline bool expect (TokenType type) throws ParseError { if (accept (type)) { return true; } switch (type) { case TokenType.CLOSE_BRACE: safe_prev (); report_parse_error (new ParseError.SYNTAX ("following block delimiter %s missing", type.to_string ())); return true; case TokenType.CLOSE_BRACKET: case TokenType.CLOSE_PARENS: case TokenType.SEMICOLON: safe_prev (); report_parse_error (new ParseError.SYNTAX ("following expression/statement delimiter %s missing", type.to_string ())); return true; default: throw new ParseError.SYNTAX ("expected %s", type.to_string ()); } } inline SourceLocation get_location () { return tokens[index].begin; } string get_location_string () { var begin = get_location (); return "__VALA_L%d_C%d__".printf (begin.line, begin.column); } string get_current_string () { var token = tokens[index]; return ((string) token.begin.pos).substring (0, (int) (token.end.pos - token.begin.pos)); } string get_last_string () { int last_index = (index + BUFFER_SIZE - 1) % BUFFER_SIZE; var token = tokens[last_index]; return ((string) token.begin.pos).substring (0, (int) (token.end.pos - token.begin.pos)); } SourceReference get_src (SourceLocation begin) { int last_index = (index + BUFFER_SIZE - 1) % BUFFER_SIZE; return new SourceReference (scanner.source_file, begin, tokens[last_index].end); } SourceReference get_current_src () { var token = tokens[index]; return new SourceReference (scanner.source_file, token.begin, token.end); } SourceReference get_last_src () { int last_index = (index + BUFFER_SIZE - 1) % BUFFER_SIZE; var token = tokens[last_index]; return new SourceReference (scanner.source_file, token.begin, token.end); } void rollback (SourceLocation location) { while (tokens[index].begin.pos != location.pos) { index = (index - 1 + BUFFER_SIZE) % BUFFER_SIZE; size++; if (size > BUFFER_SIZE) { scanner.seek (location); size = 0; index = 0; next (); } } } void jump (SourceLocation location) { while (tokens[index].begin.pos != location.pos) { index = (index + 1) % BUFFER_SIZE; size--; if (size <= 0) { scanner.seek (location); size = 0; index = 0; next (); } } } void skip_identifier () throws ParseError { // also accept keywords as identifiers where there is no conflict switch (current ()) { case TokenType.ABSTRACT: case TokenType.AS: case TokenType.ASYNC: case TokenType.BASE: case TokenType.BREAK: case TokenType.CASE: case TokenType.CATCH: case TokenType.CLASS: case TokenType.CONST: case TokenType.CONSTRUCT: case TokenType.CONTINUE: case TokenType.DEFAULT: case TokenType.DELEGATE: case TokenType.DELETE: case TokenType.DO: case TokenType.DYNAMIC: case TokenType.ELSE: case TokenType.ENUM: case TokenType.ENSURES: case TokenType.ERRORDOMAIN: case TokenType.EXTERN: case TokenType.FALSE: case TokenType.FINALLY: case TokenType.FOR: case TokenType.FOREACH: case TokenType.GET: case TokenType.IDENTIFIER: case TokenType.IF: case TokenType.IN: case TokenType.INLINE: case TokenType.INTERFACE: case TokenType.INTERNAL: case TokenType.IS: case TokenType.LOCK: case TokenType.NAMESPACE: case TokenType.NEW: case TokenType.NULL: case TokenType.OUT: case TokenType.OVERRIDE: case TokenType.OWNED: case TokenType.PARAMS: case TokenType.PARTIAL: case TokenType.PRIVATE: case TokenType.PROTECTED: case TokenType.PUBLIC: case TokenType.REF: case TokenType.REQUIRES: case TokenType.RETURN: case TokenType.SEALED: case TokenType.SET: case TokenType.SIGNAL: case TokenType.SIZEOF: case TokenType.STATIC: case TokenType.STRUCT: case TokenType.SWITCH: case TokenType.THIS: case TokenType.THROW: case TokenType.THROWS: case TokenType.TRUE: case TokenType.TRY: case TokenType.TYPEOF: case TokenType.UNLOCK: case TokenType.UNOWNED: case TokenType.USING: case TokenType.VAR: case TokenType.VIRTUAL: case TokenType.VOID: case TokenType.VOLATILE: case TokenType.WEAK: case TokenType.WHILE: case TokenType.WITH: case TokenType.YIELD: next (); return; case TokenType.INTEGER_LITERAL: case TokenType.REAL_LITERAL: // also accept integer and real literals // as long as they contain at least one character // and no decimal point // for example, 2D and 3D string id = get_current_string (); if (id[id.length - 1].isalpha () && !("." in id)) { next (); return; } break; default: throw new ParseError.SYNTAX ("expected identifier"); } } string parse_identifier (bool required = false) throws ParseError { try { skip_identifier (); } catch (ParseError e) { if (!required && context.keep_going) { report_parse_error (e); prev (); return get_location_string (); } else { throw e; } } return get_last_string (); } Expression parse_literal () throws ParseError { var begin = get_location (); switch (current ()) { case TokenType.TRUE: next (); return new BooleanLiteral (true, get_src (begin)); case TokenType.FALSE: next (); return new BooleanLiteral (false, get_src (begin)); case TokenType.INTEGER_LITERAL: next (); return new IntegerLiteral (get_last_string (), get_src (begin)); case TokenType.REAL_LITERAL: next (); return new RealLiteral (get_last_string (), get_src (begin)); case TokenType.CHARACTER_LITERAL: next (); // FIXME validate and unescape here and just pass unichar to CharacterLiteral var lit = new CharacterLiteral (get_last_string (), get_src (begin)); if (lit.error) { Report.error (lit.source_reference, "invalid character literal"); } return lit; case TokenType.REGEX_LITERAL: next (); string match_part = get_last_string (); SourceReference src_begin = get_src (begin); expect (TokenType.CLOSE_REGEX_LITERAL); string close_token = get_last_string (); return new RegexLiteral ("%s/%s".printf (close_token, match_part), src_begin); case TokenType.STRING_LITERAL: next (); return new StringLiteral (get_last_string (), get_src (begin)); case TokenType.TEMPLATE_STRING_LITERAL: next (); return new StringLiteral ("\"%s\"".printf (get_last_string ()), get_src (begin)); case TokenType.VERBATIM_STRING_LITERAL: next (); string raw_string = get_last_string (); string escaped_string = raw_string.substring (3, raw_string.length - 6).escape (""); return new StringLiteral ("\"%s\"".printf (escaped_string), get_src (begin)); case TokenType.VERBATIM_TEMPLATE_STRING_LITERAL: next (); string raw_string = get_last_string (); string escaped_string = raw_string.escape (""); return new StringLiteral ("\"%s\"".printf (escaped_string), get_src (begin)); case TokenType.NULL: next (); return new NullLiteral (get_src (begin)); default: throw new ParseError.SYNTAX ("expected literal"); } } public void parse_file (SourceFile source_file) { var has_global_context = (context != null); if (!has_global_context) { context = source_file.context; } scanner = new Scanner (source_file); parse_file_comments (); index = -1; size = 0; next (); try { partials = new PartialInfo[] {}; var begin = get_location (); parse_using_directives (context.root); parse_declarations (context.root, true); if (accept (TokenType.CLOSE_BRACE)) { // only report error if it's not a secondary error if (context.report.get_errors () == 0) { Report.error (get_last_src (), "unexpected `}'"); } } if (partials.length > 0) { rollback (begin); foreach (var info in partials) { jump (info.begin); parse_class_declaration (info.parent, info.attributes, true); } } partials = null; } catch (ParseError e) { report_parse_error (e); } scanner = null; if (!has_global_context) { context = null; } } void parse_file_comments () { scanner.parse_file_comments (); } void skip_symbol_name () throws ParseError { do { skip_identifier (); } while (accept (TokenType.DOT) || accept (TokenType.DOUBLE_COLON)); } UnresolvedSymbol parse_symbol_name () throws ParseError { var begin = get_location (); UnresolvedSymbol sym = null; do { string name = parse_identifier (); if (name == "global" && accept (TokenType.DOUBLE_COLON)) { // global::Name // qualified access to global symbol name = parse_identifier (); sym = new UnresolvedSymbol (sym, name, get_src (begin)); sym.qualified = true; continue; } sym = new UnresolvedSymbol (sym, name, get_src (begin)); } while (accept (TokenType.DOT)); return sym; } void skip_type () throws ParseError { accept (TokenType.DYNAMIC); accept (TokenType.OWNED); accept (TokenType.UNOWNED); accept (TokenType.WEAK); if (is_inner_array_type ()) { expect (TokenType.OPEN_PARENS); expect (TokenType.UNOWNED); skip_type (); expect (TokenType.CLOSE_PARENS); expect (TokenType.OPEN_BRACKET); prev (); } else { if (accept (TokenType.VOID)) { } else { skip_symbol_name (); skip_type_argument_list (); } while (accept (TokenType.STAR)) { } accept (TokenType.INTERR); } while (accept (TokenType.OPEN_BRACKET)) { do { // required for decision between expression and declaration statement if (current () != TokenType.COMMA && current () != TokenType.CLOSE_BRACKET && current () != TokenType.COLON) { parse_expression (); } } while (accept (TokenType.COMMA)); if (accept (TokenType.COLON)) { skip_symbol_name (); } expect (TokenType.CLOSE_BRACKET); accept (TokenType.INTERR); } accept (TokenType.OP_NEG); } bool is_inner_array_type () { var begin = get_location (); var result = accept (TokenType.OPEN_PARENS) && accept (TokenType.UNOWNED) && current() != TokenType.CLOSE_PARENS; rollback (begin); return result; } DataType parse_type (bool owned_by_default, bool can_weak_ref, bool require_unowned = false) throws ParseError { var begin = get_location (); bool is_dynamic = accept (TokenType.DYNAMIC); bool value_owned = owned_by_default; if (require_unowned) { expect (TokenType.UNOWNED); } else { if (owned_by_default) { if (accept (TokenType.UNOWNED)) { value_owned = false; } else if (accept (TokenType.WEAK)) { if (!can_weak_ref && !context.deprecated) { Report.warning (get_last_src (), "deprecated syntax, use `unowned` modifier"); } value_owned = false; } else if (accept (TokenType.OWNED)) { Report.warning (get_last_src (), "`owned' is default in this context"); } } else { if (accept (TokenType.OWNED)) { value_owned = true; } else { value_owned = false; if (accept (TokenType.UNOWNED)) { Report.warning (get_last_src (), "`unowned' is default in this context"); } } } } DataType type; bool inner_type_owned = true; if (accept (TokenType.OPEN_PARENS)) { type = parse_type (false, false, true); expect (TokenType.CLOSE_PARENS); inner_type_owned = false; expect (TokenType.OPEN_BRACKET); prev (); } else { if (!is_dynamic && value_owned == owned_by_default && accept (TokenType.VOID)) { type = new VoidType (get_src (begin)); } else { var sym = parse_symbol_name (); List type_arg_list = parse_type_argument_list (false); type = new UnresolvedType.from_symbol (sym, get_src (begin)); if (type_arg_list != null) { foreach (DataType type_arg in type_arg_list) { type.add_type_argument (type_arg); } } } while (accept (TokenType.STAR)) { type = new PointerType (type, get_src (begin)); } if (!(type is PointerType)) { type.nullable = accept (TokenType.INTERR); type.source_reference = get_src (begin); } } // array brackets in types are read from right to left, // this is more logical, especially when nullable arrays // or pointers are involved DataType? array_length_type = null; while (accept (TokenType.OPEN_BRACKET)) { bool invalid_array = false; int array_rank = 0; do { array_rank++; // required for decision between expression and declaration statement if (current () != TokenType.COMMA && current () != TokenType.CLOSE_BRACKET && current () != TokenType.COLON) { parse_expression (); // only used for parsing, reject use as real type invalid_array = true; } } while (accept (TokenType.COMMA)); if (accept (TokenType.COLON)) { array_length_type = parse_type (true, false); } expect (TokenType.CLOSE_BRACKET); type.value_owned = inner_type_owned; var array_type = new ArrayType (type, array_rank, get_src (begin)); array_type.nullable = accept (TokenType.INTERR); array_type.invalid_syntax = invalid_array; if (array_length_type != null) { array_type.length_type = array_length_type.copy (); } type = array_type; } if (accept (TokenType.OP_NEG)) { Report.warning (get_last_src (), "obsolete syntax, types are non-null by default"); } if (type is PointerType) { value_owned = false; } type.is_dynamic = is_dynamic; type.value_owned = value_owned; return type; } DataType? parse_inline_array_type (DataType? type) throws ParseError { var begin = get_location (); // inline-allocated array if (type != null && accept (TokenType.OPEN_BRACKET)) { Expression array_length = null; DataType? array_length_type = null; if (current () != TokenType.CLOSE_BRACKET) { array_length = parse_expression (); } if (accept (TokenType.COLON)) { array_length_type = parse_type (true, false); } expect (TokenType.CLOSE_BRACKET); var array_type = new ArrayType (type, 1, get_src (begin)); array_type.inline_allocated = true; if (array_length != null) { array_type.fixed_length = true; array_type.length = array_length; } if (array_length_type != null) { array_type.length_type = array_length_type; } array_type.value_owned = type.value_owned; return array_type; } return type; } List parse_argument_list () throws ParseError { expect (TokenType.OPEN_PARENS); var list = new ArrayList (); if (current () != TokenType.CLOSE_PARENS) { do { try { list.add (parse_argument ()); } catch (ParseError e) { if (current () == TokenType.CLOSE_PARENS) { prev (); report_parse_error (new ParseError.SYNTAX ("incomplete argument list")); } else if (context.keep_going) { report_parse_error (e); } else { throw e; } } } while (accept (TokenType.COMMA)); } expect (TokenType.CLOSE_PARENS); return list; } Expression parse_argument () throws ParseError { var begin = get_location (); if (accept (TokenType.REF)) { var inner = parse_expression (); return new UnaryExpression (UnaryOperator.REF, inner, get_src (begin)); } else if (accept (TokenType.OUT)) { var inner = parse_expression (); return new UnaryExpression (UnaryOperator.OUT, inner, get_src (begin)); } else { var expr = parse_expression (); var ma = expr as MemberAccess; if (ma != null && ma.inner == null && accept (TokenType.COLON)) { // named argument expr = parse_expression (); return new NamedArgument (ma.member_name, expr, get_src (begin)); } else { return expr; } } } Expression parse_primary_expression () throws ParseError { var begin = get_location (); Expression expr; switch (current ()) { case TokenType.TRUE: case TokenType.FALSE: case TokenType.INTEGER_LITERAL: case TokenType.REAL_LITERAL: case TokenType.CHARACTER_LITERAL: case TokenType.STRING_LITERAL: case TokenType.REGEX_LITERAL: case TokenType.TEMPLATE_STRING_LITERAL: case TokenType.VERBATIM_STRING_LITERAL: case TokenType.VERBATIM_TEMPLATE_STRING_LITERAL: case TokenType.NULL: expr = parse_literal (); break; case TokenType.OPEN_BRACE: expr = parse_initializer (); break; case TokenType.OPEN_BRACKET: expr = parse_simple_name (); break; case TokenType.OPEN_PARENS: expr = parse_tuple (); break; case TokenType.OPEN_TEMPLATE: expr = parse_template (); break; case TokenType.OPEN_REGEX_LITERAL: expr = parse_regex_literal (); break; case TokenType.THIS: expr = parse_this_access (); break; case TokenType.BASE: expr = parse_base_access (); break; case TokenType.NEW: expr = parse_object_or_array_creation_expression (); break; case TokenType.YIELD: expr = parse_yield_expression (); break; case TokenType.SIZEOF: expr = parse_sizeof_expression (); break; case TokenType.TYPEOF: expr = parse_typeof_expression (); break; default: expr = parse_simple_name (); break; } // process primary expressions that start with an inner primary expression bool found = true; while (found) { switch (current ()) { case TokenType.INTERR: // check for null-safe member or element access next (); switch (current ()) { case TokenType.DOT: expr = parse_member_access (begin, expr); break; case TokenType.OPEN_BRACKET: expr = parse_element_access (begin, expr); break; default: prev (); found = false; break; } break; case TokenType.DOT: expr = parse_member_access (begin, expr); break; case TokenType.OP_PTR: expr = parse_pointer_member_access (begin, expr); break; case TokenType.OPEN_PARENS: expr = parse_method_call (begin, expr); break; case TokenType.OPEN_BRACKET: expr = parse_element_access (begin, expr); break; case TokenType.OP_INC: expr = parse_post_increment_expression (begin, expr); break; case TokenType.OP_DEC: expr = parse_post_decrement_expression (begin, expr); break; default: found = false; break; } } return expr; } Expression parse_simple_name () throws ParseError { var begin = get_location (); string id = parse_identifier (); bool qualified = false; if (id == "global" && accept (TokenType.DOUBLE_COLON)) { id = parse_identifier (); qualified = true; } List type_arg_list = parse_type_argument_list (true); var expr = new MemberAccess (null, id, get_src (begin)); expr.qualified = qualified; if (type_arg_list != null) { foreach (DataType type_arg in type_arg_list) { expr.add_type_argument (type_arg); } } return expr; } Expression parse_tuple () throws ParseError { var begin = get_location (); expect (TokenType.OPEN_PARENS); var expr_list = new ArrayList (); if (current () != TokenType.CLOSE_PARENS) { do { expr_list.add (parse_expression ()); } while (accept (TokenType.COMMA)); } expect (TokenType.CLOSE_PARENS); if (expr_list.size != 1) { var tuple = new Tuple (get_src (begin)); foreach (Expression expr in expr_list) { tuple.add_expression (expr); } return tuple; } else { var expr = expr_list.get (0); expr.source_reference = get_src (begin); return expr; } } Expression parse_template () throws ParseError { var begin = get_location (); var template = new Template (); expect (TokenType.OPEN_TEMPLATE); while (current () != TokenType.CLOSE_TEMPLATE) { template.add_expression (parse_expression ()); expect (TokenType.COMMA); } expect (TokenType.CLOSE_TEMPLATE); template.source_reference = get_src (begin); return template; } Expression parse_regex_literal () throws ParseError { expect (TokenType.OPEN_REGEX_LITERAL); var expr = parse_literal (); return expr; } Expression parse_member_access (SourceLocation begin, Expression inner) throws ParseError { bool null_safe = previous () == TokenType.INTERR; expect (TokenType.DOT); string id = parse_identifier (); List type_arg_list = parse_type_argument_list (true); var expr = new MemberAccess (inner, id, get_src (begin)); expr.null_safe_access = null_safe; 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 (); List type_arg_list = parse_type_argument_list (true); var expr = new MemberAccess.pointer (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_method_call (SourceLocation begin, Expression inner) throws ParseError { var arg_list = parse_argument_list (); var src = get_src (begin); var init_list = parse_object_initializer (); if (init_list.size > 0 && inner is MemberAccess) { // struct creation expression unowned MemberAccess member = (MemberAccess) inner; var expr = new ObjectCreationExpression (member, src); expr.struct_creation = true; foreach (Expression arg in arg_list) { expr.add_argument (arg); } foreach (MemberInitializer initializer in init_list) { expr.add_member_initializer (initializer); } return expr; } else { var expr = new MethodCall (inner, src); foreach (Expression arg in arg_list) { expr.add_argument (arg); } return expr; } } Expression parse_element_access (SourceLocation begin, Expression inner) throws ParseError { Expression? stop = null; List index_list; bool null_safe = previous () == TokenType.INTERR; expect (TokenType.OPEN_BRACKET); if (current () == TokenType.COLON) { // slice expression index_list = new ArrayList (); index_list.add (new IntegerLiteral ("0", get_src (begin))); } else { index_list = parse_expression_list (); } if (index_list.size == 1 && accept (TokenType.COLON)) { // slice expression if (current () == TokenType.CLOSE_BRACKET) { stop = new MemberAccess (inner, "length", get_src (begin)); } else { stop = parse_expression (); } } expect (TokenType.CLOSE_BRACKET); if (stop == null) { var expr = new ElementAccess (inner, get_src (begin)); expr.null_safe_access = null_safe; foreach (Expression index in index_list) { expr.append_index (index); } return expr; } else { var expr = new SliceExpression (inner, index_list[0], stop, get_src (begin)); expr.null_safe_access = null_safe; return expr; } } List parse_expression_list () throws ParseError { var list = new ArrayList (); do { list.add (parse_expression ()); } while (accept (TokenType.COMMA)); return list; } Expression parse_this_access () throws ParseError { var begin = get_location (); expect (TokenType.THIS); return new MemberAccess (null, "this", get_src (begin)); } Expression parse_base_access () throws ParseError { var begin = get_location (); expect (TokenType.BASE); return new BaseAccess (get_src (begin)); } Expression parse_post_increment_expression (SourceLocation begin, Expression inner) throws ParseError { expect (TokenType.OP_INC); return new PostfixExpression (inner, true, get_src (begin)); } Expression parse_post_decrement_expression (SourceLocation begin, Expression inner) throws ParseError { expect (TokenType.OP_DEC); return new PostfixExpression (inner, false, get_src (begin)); } Expression parse_object_or_array_creation_expression () throws ParseError { var begin = get_location (); expect (TokenType.NEW); if (is_inner_array_type ()) { rollback (begin); return parse_array_creation_expression (); } var member = parse_member_name (); if (current () == TokenType.OPEN_PARENS) { var expr = parse_object_creation_expression (begin, member); return expr; } else { bool is_pointer_type = false; while (accept (TokenType.STAR)) { is_pointer_type = true; } if (!is_pointer_type) { accept (TokenType.INTERR); } if (accept (TokenType.OPEN_BRACKET)) { rollback (begin); var expr = parse_array_creation_expression (); return expr; } else { throw new ParseError.SYNTAX ("expected ( or ["); } } } Expression parse_object_creation_expression (SourceLocation begin, MemberAccess member) throws ParseError { var arg_list = parse_argument_list (); var src = get_src (begin); var init_list = parse_object_initializer (); var expr = new ObjectCreationExpression (member, src); foreach (Expression arg in arg_list) { expr.add_argument (arg); } foreach (MemberInitializer initializer in init_list) { expr.add_member_initializer (initializer); } return expr; } Expression parse_array_creation_expression () throws ParseError { var begin = get_location (); expect (TokenType.NEW); bool inner_array_type = is_inner_array_type (); if (inner_array_type) { expect (TokenType.OPEN_PARENS); expect (TokenType.UNOWNED); } var member = parse_member_name (); DataType element_type = new UnresolvedType.from_expression (member); bool is_pointer_type = false; while (accept (TokenType.STAR)) { element_type = new PointerType (element_type, get_src (begin)); is_pointer_type = true; } if (!is_pointer_type) { if (accept (TokenType.INTERR)) { element_type.nullable = true; } } if (inner_array_type) { expect (TokenType.CLOSE_PARENS); element_type.value_owned = false; } else { element_type.value_owned = true; } expect (TokenType.OPEN_BRACKET); bool size_specified = false; List size_specifier_list = null; bool first = true; DataType? array_length_type = null; do { if (!first) { // array of arrays: new T[][42] if (size_specified) { throw new ParseError.SYNTAX ("size of inner arrays must not be specified in array creation expression"); } element_type = new ArrayType (element_type, size_specifier_list.size, element_type.source_reference); if (array_length_type != null) { ((ArrayType) element_type).length_type = array_length_type.copy (); } } else { first = false; } size_specifier_list = new ArrayList (); do { Expression size = null; if (current () != TokenType.CLOSE_BRACKET && current () != TokenType.COMMA && current () != TokenType.COLON) { size = parse_expression (); size_specified = true; } size_specifier_list.add (size); } while (accept (TokenType.COMMA)); if (accept (TokenType.COLON)) { array_length_type = parse_type (true, false); } expect (TokenType.CLOSE_BRACKET); } while (accept (TokenType.OPEN_BRACKET)); var src = get_src (begin); InitializerList initializer = null; if (current () == TokenType.OPEN_BRACE) { initializer = parse_initializer (); } var expr = new ArrayCreationExpression (element_type, size_specifier_list.size, initializer, src); if (array_length_type != null) { expr.length_type = array_length_type.copy (); } if (size_specified) { foreach (Expression size in size_specifier_list) { expr.append_size (size); } } else if (initializer == null) { Report.warning (src, "possibly missing array size"); throw new ParseError.SYNTAX ("expected array initializer list"); } return expr; } List parse_object_initializer () throws ParseError { var list = new ArrayList (); if (accept (TokenType.OPEN_BRACE)) { while (current () != TokenType.CLOSE_BRACE) { list.add (parse_member_initializer ()); if (!accept (TokenType.COMMA)) { break; } } expect (TokenType.CLOSE_BRACE); } return list; } MemberInitializer parse_member_initializer (bool chained = false) throws ParseError { var begin = get_location (); string id = parse_identifier (chained); expect (TokenType.ASSIGN); var inner = get_location (); Expression expr; try { // chained member initializer expr = parse_member_initializer (true); } catch { rollback (inner); expr = parse_expression (); } return new MemberInitializer (id, expr, get_src (begin)); } Expression parse_yield_expression () throws ParseError { expect (TokenType.YIELD); var expr = parse_expression (); unowned MethodCall? call = expr as MethodCall; unowned ObjectCreationExpression? object_creation = expr as ObjectCreationExpression; if (call == null && object_creation == null) { Report.error (expr.source_reference, "syntax error, expected method call"); expr.error = true; } if (call != null) { call.is_yield_expression = true; } else if (object_creation != null) { object_creation.is_yield_expression = true; } return expr; } Expression parse_sizeof_expression () throws ParseError { var begin = get_location (); expect (TokenType.SIZEOF); expect (TokenType.OPEN_PARENS); var type = parse_type (true, false); expect (TokenType.CLOSE_PARENS); return new SizeofExpression (type, get_src (begin)); } Expression parse_typeof_expression () throws ParseError { var begin = get_location (); expect (TokenType.TYPEOF); expect (TokenType.OPEN_PARENS); var type = parse_type (true, false); expect (TokenType.CLOSE_PARENS); return new TypeofExpression (type, get_src (begin)); } UnaryOperator get_unary_operator (TokenType token_type) { switch (token_type) { case TokenType.PLUS: return UnaryOperator.PLUS; case TokenType.MINUS: return UnaryOperator.MINUS; case TokenType.OP_NEG: return UnaryOperator.LOGICAL_NEGATION; case TokenType.TILDE: return UnaryOperator.BITWISE_COMPLEMENT; case TokenType.OP_INC: return UnaryOperator.INCREMENT; case TokenType.OP_DEC: return UnaryOperator.DECREMENT; default: return UnaryOperator.NONE; } } Expression parse_unary_expression () throws ParseError { var begin = get_location (); var operator = get_unary_operator (current ()); if (operator != UnaryOperator.NONE) { next (); var op = parse_unary_expression (); unowned IntegerLiteral? lit = op as IntegerLiteral; if (lit != null) { if (operator == UnaryOperator.PLUS) { return lit; } else if (operator == UnaryOperator.MINUS) { return new IntegerLiteral ("-"+lit.value, get_src (begin)); } } return new UnaryExpression (operator, op, get_src (begin)); } switch (current ()) { case TokenType.OPEN_PARENS: next (); switch (current ()) { case TokenType.UNOWNED: // inner array type break; case TokenType.OWNED: // (owned) foo next (); if (accept (TokenType.CLOSE_PARENS)) { var op = parse_unary_expression (); return new ReferenceTransferExpression (op, get_src (begin)); } break; case TokenType.VOID: case TokenType.DYNAMIC: case TokenType.OPEN_PARENS: case TokenType.IDENTIFIER: if (current () != TokenType.OPEN_PARENS || is_inner_array_type ()) { var type = parse_type (true, false); if (accept (TokenType.CLOSE_PARENS)) { // check follower to decide whether to create cast expression switch (current ()) { case TokenType.OP_NEG: case TokenType.TILDE: case TokenType.OPEN_PARENS: case TokenType.TRUE: case TokenType.FALSE: case TokenType.INTEGER_LITERAL: case TokenType.REAL_LITERAL: case TokenType.CHARACTER_LITERAL: case TokenType.STRING_LITERAL: case TokenType.TEMPLATE_STRING_LITERAL: case TokenType.VERBATIM_STRING_LITERAL: case TokenType.VERBATIM_TEMPLATE_STRING_LITERAL: case TokenType.REGEX_LITERAL: case TokenType.NULL: case TokenType.THIS: case TokenType.BASE: case TokenType.NEW: case TokenType.YIELD: case TokenType.SIZEOF: case TokenType.TYPEOF: case TokenType.IDENTIFIER: case TokenType.PARAMS: var inner = parse_unary_expression (); return new CastExpression (inner, type, get_src (begin)); case TokenType.STAR: next (); var op = parse_unary_expression (); var inner = new PointerIndirection (op, get_src (begin)); return new CastExpression (inner, type, get_src (begin)); case TokenType.BITWISE_AND: next (); var op = parse_unary_expression (); var inner = new AddressofExpression (op, get_src (begin)); return new CastExpression (inner, type, get_src (begin)); default: break; } } } break; case TokenType.OP_NEG: next (); if (accept (TokenType.CLOSE_PARENS)) { // (!) non-null cast var inner = parse_unary_expression (); return new CastExpression.non_null (inner, get_src (begin)); } break; default: break; } // no cast expression rollback (begin); break; case TokenType.STAR: next (); var op = parse_unary_expression (); return new PointerIndirection (op, get_src (begin)); case TokenType.BITWISE_AND: next (); var op = parse_unary_expression (); return new AddressofExpression (op, get_src (begin)); default: break; } var expr = parse_primary_expression (); return expr; } BinaryOperator get_binary_operator (TokenType token_type) { switch (token_type) { case TokenType.STAR: return BinaryOperator.MUL; case TokenType.DIV: return BinaryOperator.DIV; case TokenType.PERCENT: return BinaryOperator.MOD; case TokenType.PLUS: return BinaryOperator.PLUS; case TokenType.MINUS: return BinaryOperator.MINUS; case TokenType.OP_LT: return BinaryOperator.LESS_THAN; case TokenType.OP_GT: return BinaryOperator.GREATER_THAN; case TokenType.OP_LE: return BinaryOperator.LESS_THAN_OR_EQUAL; case TokenType.OP_GE: return BinaryOperator.GREATER_THAN_OR_EQUAL; case TokenType.OP_EQ: return BinaryOperator.EQUALITY; case TokenType.OP_NE: return BinaryOperator.INEQUALITY; default: return BinaryOperator.NONE; } } Expression parse_multiplicative_expression () throws ParseError { var begin = get_location (); var left = parse_unary_expression (); bool found = true; while (found) { var operator = get_binary_operator (current ()); switch (operator) { case BinaryOperator.MUL: case BinaryOperator.DIV: case BinaryOperator.MOD: next (); var right = parse_unary_expression (); left = new BinaryExpression (operator, left, right, get_src (begin)); break; default: found = false; break; } } return left; } Expression parse_additive_expression () throws ParseError { var begin = get_location (); var left = parse_multiplicative_expression (); bool found = true; while (found) { var operator = get_binary_operator (current ()); switch (operator) { case BinaryOperator.PLUS: case BinaryOperator.MINUS: next (); var right = parse_multiplicative_expression (); left = new BinaryExpression (operator, left, right, get_src (begin)); break; default: found = false; break; } } return left; } Expression parse_shift_expression () throws ParseError { var begin = get_location (); var left = parse_additive_expression (); bool found = true; while (found) { switch (current ()) { case TokenType.OP_SHIFT_LEFT: next (); var right = parse_additive_expression (); left = new BinaryExpression (BinaryOperator.SHIFT_LEFT, left, right, get_src (begin)); break; // don't use OP_SHIFT_RIGHT to support >> for nested generics case 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_GT && tokens[index].begin.pos == first_gt_pos + 1) { next (); var right = parse_additive_expression (); left = new BinaryExpression (BinaryOperator.SHIFT_RIGHT, left, right, get_src (begin)); } else { prev (); found = false; } break; default: found = false; break; } } return left; } Expression parse_type_check_expression () throws ParseError { var begin = get_location (); var left = parse_shift_expression (); if (accept (TokenType.IS)) { var type = parse_type (true, false); left = new TypeCheck (left, type, get_src (begin)); } else if (accept (TokenType.AS)) { var type = parse_type (true, false); left = new CastExpression.silent (left, type, get_src (begin)); } return left; } Expression parse_relational_expression () throws ParseError { var begin = get_location (); var left = parse_type_check_expression (); bool first = true; bool found = true; while (found) { var operator = get_binary_operator (current ()); switch (operator) { case BinaryOperator.LESS_THAN: case BinaryOperator.LESS_THAN_OR_EQUAL: case BinaryOperator.GREATER_THAN_OR_EQUAL: case BinaryOperator.EQUALITY: case BinaryOperator.INEQUALITY: next (); var right = parse_type_check_expression (); if (first) { left = new BinaryExpression (operator, left, right, get_src (begin)); } else { left = new BinaryExpression.chained (operator, left, right, get_src (begin)); } first = false; break; case BinaryOperator.GREATER_THAN: next (); // ignore >> and >>= (two tokens due to generics) if (current () != TokenType.OP_GT && current () != TokenType.OP_GE) { var right = parse_type_check_expression (); if (first) { left = new BinaryExpression (operator, left, right, get_src (begin)); } else { left = new BinaryExpression.chained (operator, left, right, get_src (begin)); } first = false; } else { prev (); found = false; } break; default: found = false; break; } } return left; } Expression parse_and_expression () throws ParseError { var begin = get_location (); var left = parse_relational_expression (); while (accept (TokenType.BITWISE_AND)) { var right = parse_relational_expression (); left = new BinaryExpression (BinaryOperator.BITWISE_AND, left, right, get_src (begin)); } return left; } Expression parse_exclusive_or_expression () throws ParseError { var begin = get_location (); var left = parse_and_expression (); while (accept (TokenType.CARRET)) { var right = parse_and_expression (); left = new BinaryExpression (BinaryOperator.BITWISE_XOR, left, right, get_src (begin)); } return left; } Expression parse_inclusive_or_expression () throws ParseError { var begin = get_location (); var left = parse_exclusive_or_expression (); while (accept (TokenType.BITWISE_OR)) { var right = parse_exclusive_or_expression (); left = new BinaryExpression (BinaryOperator.BITWISE_OR, left, right, get_src (begin)); } return left; } Expression parse_in_expression () throws ParseError { var begin = get_location (); var left = parse_inclusive_or_expression (); while (accept (TokenType.IN)) { var right = parse_inclusive_or_expression (); left = new BinaryExpression (BinaryOperator.IN, left, right, get_src (begin)); } return left; } Expression parse_conditional_and_expression () throws ParseError { var begin = get_location (); var left = parse_in_expression (); while (accept (TokenType.OP_AND)) { var right = parse_in_expression (); left = new BinaryExpression (BinaryOperator.AND, left, right, get_src (begin)); } return left; } Expression parse_conditional_or_expression () throws ParseError { var begin = get_location (); var left = parse_conditional_and_expression (); while (accept (TokenType.OP_OR)) { var right = parse_conditional_and_expression (); left = new BinaryExpression (BinaryOperator.OR, left, right, get_src (begin)); } return left; } Expression parse_coalescing_expression () throws ParseError { var begin = get_location (); var left = parse_conditional_or_expression (); if (accept (TokenType.OP_COALESCING)) { var right = parse_coalescing_expression (); return new BinaryExpression (BinaryOperator.COALESCE, left, right, get_src (begin)); } else { return left; } } Expression parse_conditional_expression () throws ParseError { var begin = get_location (); var condition = parse_coalescing_expression (); if (accept (TokenType.INTERR)) { var true_expr = parse_expression (); expect (TokenType.COLON); var false_expr = parse_expression (); return new ConditionalExpression (condition, true_expr, false_expr, get_src (begin)); } else { return condition; } } Parameter parse_lambda_parameter () throws ParseError { var begin = get_location (); var direction = ParameterDirection.IN; if (accept (TokenType.OUT)) { direction = ParameterDirection.OUT; } else if (accept (TokenType.REF)) { direction = ParameterDirection.REF; } string id = parse_identifier (); var param = new Parameter (id, null, get_src (begin)); param.direction = direction; return param; } Expression parse_lambda_expression () throws ParseError { var begin = get_location (); List params = new ArrayList (); if (accept (TokenType.OPEN_PARENS)) { if (current () != TokenType.CLOSE_PARENS) { do { params.add (parse_lambda_parameter ()); } while (accept (TokenType.COMMA)); } expect (TokenType.CLOSE_PARENS); } else { params.add (parse_lambda_parameter ()); } expect (TokenType.LAMBDA); LambdaExpression lambda; if (current () == TokenType.OPEN_BRACE) { var block = parse_block (); lambda = new LambdaExpression.with_statement_body (block, get_src (begin)); } else { var expr = parse_expression (); lambda = new LambdaExpression (expr, get_src (begin)); } foreach (var param in params) { lambda.add_parameter (param); } return lambda; } AssignmentOperator get_assignment_operator (TokenType token_type) { switch (token_type) { case TokenType.ASSIGN: return AssignmentOperator.SIMPLE; case TokenType.ASSIGN_ADD: return AssignmentOperator.ADD; case TokenType.ASSIGN_SUB: return AssignmentOperator.SUB; case TokenType.ASSIGN_BITWISE_OR: return AssignmentOperator.BITWISE_OR; case TokenType.ASSIGN_BITWISE_AND: return AssignmentOperator.BITWISE_AND; case TokenType.ASSIGN_BITWISE_XOR: return AssignmentOperator.BITWISE_XOR; case TokenType.ASSIGN_DIV: return AssignmentOperator.DIV; case TokenType.ASSIGN_MUL: return AssignmentOperator.MUL; case TokenType.ASSIGN_PERCENT: return AssignmentOperator.PERCENT; case TokenType.ASSIGN_SHIFT_LEFT: return AssignmentOperator.SHIFT_LEFT; default: return AssignmentOperator.NONE; } } Expression parse_expression () throws ParseError { if (is_lambda_expression ()) { return parse_lambda_expression (); } 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; } } return expr; } Statement? parse_statement (TokenType type) throws ParseError { Statement? stmt = null; var begin = get_location (); try { switch (type) { case TokenType.IF: stmt = parse_if_statement (); break; case TokenType.SWITCH: stmt = parse_switch_statement (); break; case TokenType.CASE: case TokenType.DEFAULT: stmt = parse_switch_section_statement (); break; case TokenType.WHILE: stmt = parse_while_statement (); break; case TokenType.DO: stmt = parse_do_statement (); break; case TokenType.FOR: stmt = parse_for_statement (); break; case TokenType.FOREACH: stmt = parse_foreach_statement (); break; case TokenType.BREAK: stmt = parse_break_statement (); break; case TokenType.CONTINUE: stmt = parse_continue_statement (); break; case TokenType.RETURN: stmt = parse_return_statement (); break; case TokenType.YIELD: stmt = parse_yield_statement (); break; case TokenType.THROW: stmt = parse_throw_statement (); break; case TokenType.TRY: stmt = parse_try_statement (); break; case TokenType.LOCK: stmt = parse_lock_statement (); break; case TokenType.UNLOCK: stmt = parse_unlock_statement (); break; case TokenType.DELETE: stmt = parse_delete_statement (); break; case TokenType.WITH: stmt = parse_with_statement (); break; default: assert_not_reached (); } } catch (ParseError e) { var e_begin = get_location (); string token = ((EnumClass) typeof (TokenType).class_ref ()).get_value (type).value_nick; rollback (begin); if (!is_expression ()) { rollback (e_begin); throw e; } try { stmt = parse_expression_statement (); Report.warning (get_src (begin), "`%s' is a syntax keyword, replace with `@%s'", token, token); } catch (ParseError e2) { var e2_begin = get_location (); rollback (e_begin); next (); Report.error (get_src (e_begin), "Possible `%s-statement' syntax error, %s", token, e.message); rollback (e2_begin); throw e2; } } return stmt; } void parse_statements (Block block) throws ParseError { while (current () != TokenType.CLOSE_BRACE && current () != TokenType.EOF) { try { Statement stmt = null; bool is_decl = false; comment = scanner.pop_comment (); switch (current ()) { case TokenType.OPEN_BRACE: stmt = parse_block (); break; case TokenType.SEMICOLON: stmt = parse_empty_statement (); break; case TokenType.IF: case TokenType.SWITCH: case TokenType.WHILE: case TokenType.DO: case TokenType.FOR: case TokenType.FOREACH: case TokenType.BREAK: case TokenType.CONTINUE: case TokenType.RETURN: case TokenType.YIELD: case TokenType.THROW: case TokenType.TRY: case TokenType.LOCK: case TokenType.UNLOCK: case TokenType.DELETE: case TokenType.WITH: stmt = parse_statement (current ()); break; case TokenType.CASE: case TokenType.DEFAULT: if (block is SwitchSection) { // begin a new switch case return; } else { stmt = parse_statement (current ()); } break; case TokenType.VAR: is_decl = true; parse_local_variable_declarations (block); break; case TokenType.CONST: is_decl = true; parse_local_constant_declarations (block); break; case TokenType.OP_INC: case TokenType.OP_DEC: case TokenType.BASE: case TokenType.THIS: case TokenType.STAR: case TokenType.NEW: stmt = parse_expression_statement (); break; default: bool is_expr = is_expression (); if (is_expr) { stmt = parse_expression_statement (); } else { is_decl = true; bool is_local_func = is_local_function (); if (is_local_func) { parse_local_function_declaration (block); } else { parse_local_variable_declarations (block); } } break; } if (!is_decl) { block.add_statement (stmt); } } catch (ParseError e) { report_parse_error (e); if (recover () != RecoveryState.STATEMENT_BEGIN) { // beginning of next declaration or end of file reached // return what we have so far break; } } } } bool is_expression () throws ParseError { if (current () == TokenType.OPEN_PARENS) { return !is_inner_array_type (); } var begin = get_location (); // decide between declaration and expression statement try { skip_type (); } catch (ParseError e) { var token = previous (); if (token == TokenType.DOT || token == TokenType.DOUBLE_COLON) { rollback (begin); return true; } else { throw e; } } switch (current ()) { // invocation expression case TokenType.OPEN_PARENS: // postfix increment case TokenType.OP_INC: // postfix decrement case TokenType.OP_DEC: // assignments case TokenType.ASSIGN: case TokenType.ASSIGN_ADD: case TokenType.ASSIGN_BITWISE_AND: case TokenType.ASSIGN_BITWISE_OR: case TokenType.ASSIGN_BITWISE_XOR: case TokenType.ASSIGN_DIV: case TokenType.ASSIGN_MUL: case TokenType.ASSIGN_PERCENT: case TokenType.ASSIGN_SHIFT_LEFT: case TokenType.ASSIGN_SUB: case TokenType.OP_GT: // >>= // member access case TokenType.DOT: // pointer member access case TokenType.OP_PTR: // literal expression case TokenType.INTEGER_LITERAL: case TokenType.REAL_LITERAL: rollback (begin); return true; default: rollback (begin); return false; } } bool is_lambda_expression () { var begin = get_location (); switch (current ()) { case TokenType.OUT: case TokenType.REF: next (); if (accept (TokenType.IDENTIFIER) && accept (TokenType.LAMBDA)) { rollback (begin); return true; } break; case TokenType.IDENTIFIER: next (); if (accept (TokenType.LAMBDA)) { rollback (begin); return true; } break; case TokenType.OPEN_PARENS: next (); if (current () != TokenType.CLOSE_PARENS) { do { if (current () == TokenType.OUT || current () == TokenType.REF) { next (); } if (!accept (TokenType.IDENTIFIER)) { rollback (begin); return false; } } while (accept (TokenType.COMMA)); } if (accept (TokenType.CLOSE_PARENS) && accept (TokenType.LAMBDA)) { rollback (begin); return true; } break; default: break; } rollback (begin); return false; } bool is_local_function () { var begin = get_location (); try { skip_type (); if (accept (TokenType.IDENTIFIER) && accept (TokenType.OPEN_PARENS)) { rollback (begin); return true; } } catch { } rollback (begin); return false; } Block parse_embedded_statement (string statement_name, bool accept_empty_body = true) throws ParseError { if (current () == TokenType.OPEN_BRACE) { var block = parse_block (); return block; } comment = scanner.pop_comment (); var block = new Block (get_src (get_location ())); try { var stmt = parse_embedded_statement_without_block (statement_name, accept_empty_body); block.add_statement (stmt); } catch (ParseError e) { if (context.keep_going) { report_parse_error (e); } else { throw e; } } block.source_reference.end = get_last_src ().end; return block; } Statement parse_embedded_statement_without_block (string statement_name, bool accept_empty_body) throws ParseError { switch (current ()) { case TokenType.SEMICOLON: if (!accept_empty_body) { Report.warning (get_current_src (), "%s-statement without body", statement_name); } return parse_empty_statement (); case TokenType.IF: case TokenType.SWITCH: case TokenType.WHILE: case TokenType.DO: case TokenType.FOR: case TokenType.FOREACH: case TokenType.BREAK: case TokenType.CONTINUE: case TokenType.RETURN: case TokenType.YIELD: case TokenType.THROW: case TokenType.TRY: case TokenType.LOCK: case TokenType.UNLOCK: case TokenType.DELETE: case TokenType.WITH: return parse_statement (current ()); case TokenType.VAR: case TokenType.CONST: throw new ParseError.SYNTAX ("embedded statement cannot be declaration "); case TokenType.OP_INC: case TokenType.OP_DEC: case TokenType.BASE: case TokenType.THIS: case TokenType.OPEN_PARENS: case TokenType.STAR: case TokenType.NEW: return parse_expression_statement (); default: if (is_expression ()) { return parse_expression_statement (); } else { throw new ParseError.SYNTAX ("embedded statement cannot be declaration"); } } } Block parse_block () throws ParseError { var begin = get_location (); expect (TokenType.OPEN_BRACE); var block = new Block (get_src (begin)); parse_statements (block); if (!accept (TokenType.CLOSE_BRACE)) { // only report error if it's not a secondary error if (context.report.get_errors () == 0) { Report.error (get_current_src (), "expected `}'"); } } block.source_reference.end = get_last_src ().end; return block; } Statement parse_empty_statement () throws ParseError { var begin = get_location (); expect (TokenType.SEMICOLON); return new EmptyStatement (get_src (begin)); } void parse_local_variable_declarations (Block block) throws ParseError { var begin = get_location (); DataType variable_type; bool is_dynamic = accept (TokenType.DYNAMIC); if (accept (TokenType.UNOWNED) && accept (TokenType.VAR)) { variable_type = new VarType (false); variable_type.nullable = accept (TokenType.INTERR); variable_type.is_dynamic = is_dynamic; } else { rollback (begin); is_dynamic = accept (TokenType.DYNAMIC); if (accept (TokenType.VAR)) { variable_type = new VarType (); variable_type.nullable = accept (TokenType.INTERR); variable_type.is_dynamic = is_dynamic; } else { rollback (begin); variable_type = parse_type (true, true); } } bool is_first = true; do { if (variable_type is VarType && variable_type.value_owned && accept (TokenType.OPEN_PARENS)) { // tuple begin = get_location (); string[] identifiers = {}; do { identifiers += parse_identifier (); } while (accept (TokenType.COMMA)); expect (TokenType.CLOSE_PARENS); expect (TokenType.ASSIGN); var tuple = parse_expression (); var tuple_local = new LocalVariable (null, CodeNode.get_temp_name (), tuple, get_src (begin)); block.add_statement (new DeclarationStatement (tuple_local, tuple_local.source_reference)); for (int i = 0; i < identifiers.length; i++) { var temp_access = new MemberAccess.simple (tuple_local.name, tuple_local.source_reference); var ea = new ElementAccess (temp_access, tuple_local.source_reference); ea.append_index (new IntegerLiteral (i.to_string ())); var local = new LocalVariable (null, identifiers[i], ea, tuple_local.source_reference); block.add_statement (new DeclarationStatement (local, local.source_reference)); } continue; } if (!is_first) { begin = get_location (); } else { is_first = false; } DataType type_copy = null; if (variable_type != null) { type_copy = variable_type.copy (); } var local = parse_local_variable (type_copy); block.add_statement (new DeclarationStatement (local, get_src (begin))); } while (accept (TokenType.COMMA)); expect (TokenType.SEMICOLON); } LocalVariable parse_local_variable (DataType? variable_type, bool expect_initializer = false) throws ParseError { var begin = get_location (); string id = parse_identifier (); var type = parse_inline_array_type (variable_type); var src = get_src (begin); Expression initializer = null; if (accept (TokenType.ASSIGN)) { initializer = parse_expression (); } else if (expect_initializer) { report_parse_error (new ParseError.SYNTAX ("expected initializer")); prev (); initializer = new InvalidExpression (); } return new LocalVariable (type, id, initializer, src); } void parse_local_constant_declarations (Block block) throws ParseError { var begin = get_location (); expect (TokenType.CONST); var constant_type = parse_type (false, false); // constant arrays don't own their element unowned ArrayType? array_type = constant_type as ArrayType; if (array_type != null) { array_type.element_type.value_owned = false; } bool is_first = true; do { if (!is_first) { begin = get_location (); } else { is_first = false; } DataType type_copy = constant_type.copy (); var local = parse_local_constant (type_copy); block.add_statement (new DeclarationStatement (local, get_src (begin))); block.add_local_constant (local); local.active = false; } while (accept (TokenType.COMMA)); expect (TokenType.SEMICOLON); } Constant parse_local_constant (DataType constant_type) throws ParseError { var begin = get_location (); string id = parse_identifier (); var type = parse_inline_array_type (constant_type); var src = get_src (begin); expect (TokenType.ASSIGN); var initializer = parse_expression (); if (type.value_owned) { Report.error (src, "`owned' is not allowed on constants"); } return new Constant (id, type, initializer, src); } void parse_local_function_declaration (Block block) throws ParseError { var begin = get_location (); var type = parse_type (true, false); var sym = parse_symbol_name (); expect (TokenType.OPEN_PARENS); List params = new ArrayList (); if (current () != TokenType.CLOSE_PARENS) { do { params.add (parse_parameter ()); } while (accept (TokenType.COMMA)); } expect (TokenType.CLOSE_PARENS); var src = get_src (begin); var d = new Delegate ("_LocalFunc%i_".printf (next_local_func_id++), type, src); foreach (var param in params) { d.add_parameter (param); } context.root.add_delegate (d); var lambda = new LambdaExpression.with_statement_body (parse_block (), src); foreach (var p in params) { var param = new Parameter (p.name, null, p.source_reference); param.direction = p.direction; lambda.add_parameter (param); } if (!context.experimental) { Report.warning (src, "local functions are experimental"); } var local = new LocalVariable (new DelegateType (d), sym.name, lambda, src); block.add_statement (new DeclarationStatement (local, src)); } Statement parse_expression_statement () throws ParseError { var begin = get_location (); var expr = parse_statement_expression (); var src = get_src (begin); expect (TokenType.SEMICOLON); return new ExpressionStatement (expr, src); } Expression parse_statement_expression () throws ParseError { // invocation expression, assignment, // or pre/post increment/decrement expression var expr = parse_expression (); return expr; } Statement parse_if_statement () throws ParseError { var begin = get_location (); expect (TokenType.IF); expect (TokenType.OPEN_PARENS); var condition = parse_expression (); expect (TokenType.CLOSE_PARENS); var src = get_src (begin); var true_stmt = parse_embedded_statement ("if", false); Block false_stmt = null; if (accept (TokenType.ELSE)) { false_stmt = parse_embedded_statement ("else", false); } return new IfStatement (condition, true_stmt, false_stmt, src); } Statement parse_switch_statement () throws ParseError { var begin = get_location (); expect (TokenType.SWITCH); expect (TokenType.OPEN_PARENS); var condition = parse_expression (); expect (TokenType.CLOSE_PARENS); var stmt = new SwitchStatement (condition, get_src (begin)); expect (TokenType.OPEN_BRACE); while (current () != TokenType.CLOSE_BRACE) { stmt.add_section ((SwitchSection) parse_switch_section_statement ()); } expect (TokenType.CLOSE_BRACE); return stmt; } Statement parse_switch_section_statement () throws ParseError { var begin = get_location (); var section = new SwitchSection (get_src (begin)); do { if (accept (TokenType.CASE)) { section.add_label (new SwitchLabel (parse_expression (), get_src (begin))); while (current () == TokenType.COMMA) { expect (TokenType.COMMA); section.add_label (new SwitchLabel (parse_expression (), get_src (begin))); } expect (TokenType.COLON); } else if (accept (TokenType.DEFAULT)) { section.add_label (new SwitchLabel.with_default (get_src (begin))); expect (TokenType.COLON); } else { throw new ParseError.SYNTAX ("expected `case' or `default' switch label"); } } while (current () == TokenType.CASE || current () == TokenType.DEFAULT); parse_statements (section); return section; } Statement parse_while_statement () throws ParseError { var begin = get_location (); expect (TokenType.WHILE); expect (TokenType.OPEN_PARENS); var condition = parse_expression (); expect (TokenType.CLOSE_PARENS); var body = parse_embedded_statement ("while"); return new WhileStatement (condition, body, get_src (begin)); } Statement parse_do_statement () throws ParseError { var begin = get_location (); expect (TokenType.DO); var body = parse_embedded_statement ("do"); expect (TokenType.WHILE); expect (TokenType.OPEN_PARENS); var condition = parse_expression (); expect (TokenType.CLOSE_PARENS); expect (TokenType.SEMICOLON); return new DoStatement (condition, body, get_src (begin)); } Statement parse_for_statement () throws ParseError { var begin = get_location (); Block block = null; expect (TokenType.FOR); expect (TokenType.OPEN_PARENS); var initializer_list = new ArrayList (); if (!accept (TokenType.SEMICOLON)) { bool is_expr; switch (current ()) { case TokenType.VAR: is_expr = false; break; case TokenType.OP_INC: case TokenType.OP_DEC: is_expr = true; break; default: is_expr = is_expression (); break; } if (is_expr) { do { initializer_list.add (parse_statement_expression ()); } while (accept (TokenType.COMMA)); expect (TokenType.SEMICOLON); } else { // variable declaration in initializer var decl_begin = get_location (); block = new Block (get_src (decl_begin)); parse_local_variable_declarations (block); block.source_reference.end = get_last_src ().end; } } Expression condition = null; if (current () != TokenType.SEMICOLON) { condition = parse_expression (); } expect (TokenType.SEMICOLON); var iterator_list = new ArrayList (); if (current () != TokenType.CLOSE_PARENS) { do { iterator_list.add (parse_statement_expression ()); } while (accept (TokenType.COMMA)); } expect (TokenType.CLOSE_PARENS); var src = get_src (begin); var body = parse_embedded_statement ("for"); var stmt = new ForStatement (condition, body, src); foreach (Expression init in initializer_list) { stmt.add_initializer (init); } foreach (Expression iter in iterator_list) { stmt.add_iterator (iter); } if (block != null) { block.add_statement (stmt); return block; } else { return stmt; } } Statement parse_foreach_statement () throws ParseError { var begin = get_location (); expect (TokenType.FOREACH); expect (TokenType.OPEN_PARENS); var var_or_type = get_location (); DataType type; bool is_dynamic = accept (TokenType.DYNAMIC); if (accept (TokenType.UNOWNED) && accept (TokenType.VAR)) { type = new VarType (false); type.nullable = accept (TokenType.INTERR); type.is_dynamic = is_dynamic; } else { rollback (var_or_type); is_dynamic = accept (TokenType.DYNAMIC); if (accept (TokenType.VAR)) { type = new VarType (); type.nullable = accept (TokenType.INTERR); type.is_dynamic = is_dynamic; } else { rollback (var_or_type); type = parse_type (true, true); if (accept (TokenType.IN)) { Report.error (type.source_reference, "syntax error, expected `unowned var', `var' or type"); throw new ParseError.SYNTAX ("expected `unowned var', `var' or type"); } } } string id = parse_identifier (); expect (TokenType.IN); var collection = parse_expression (); expect (TokenType.CLOSE_PARENS); var src = get_src (begin); var body = parse_embedded_statement ("foreach"); return new ForeachStatement (type, id, collection, body, src); } Statement parse_break_statement () throws ParseError { var begin = get_location (); expect (TokenType.BREAK); var src = get_src (begin); expect (TokenType.SEMICOLON); return new BreakStatement (src); } Statement parse_continue_statement () throws ParseError { var begin = get_location (); expect (TokenType.CONTINUE); var src = get_src (begin); expect (TokenType.SEMICOLON); return new ContinueStatement (src); } Statement parse_return_statement () throws ParseError { var begin = get_location (); expect (TokenType.RETURN); Expression expr = null; if (current () != TokenType.SEMICOLON) { expr = parse_expression (); } var src = get_src (begin); expect (TokenType.SEMICOLON); return new ReturnStatement (expr, src); } Statement parse_yield_statement () throws ParseError { var begin = get_location (); expect (TokenType.YIELD); var token = current (); if (token != TokenType.SEMICOLON) { // yield expression prev (); if (token == TokenType.RETURN) { throw new ParseError.SYNTAX ("expected `return yield'"); } return parse_expression_statement (); } var src = get_src (begin); expect (TokenType.SEMICOLON); return new YieldStatement (src); } Statement parse_throw_statement () throws ParseError { var begin = get_location (); expect (TokenType.THROW); var expr = parse_expression (); var src = get_src (begin); expect (TokenType.SEMICOLON); return new ThrowStatement (expr, src); } Statement parse_try_statement () throws ParseError { var begin = get_location (); expect (TokenType.TRY); var try_block = parse_block (); Block finally_clause = null; var catch_clauses = new ArrayList (); if (current () == TokenType.CATCH) { parse_catch_clauses (catch_clauses); if (current () == TokenType.FINALLY) { finally_clause = parse_finally_clause (); } } else if (current () == TokenType.FINALLY) { finally_clause = parse_finally_clause (); } else { report_parse_error (new ParseError.SYNTAX ("expected `catch' or `finally'")); } var stmt = new TryStatement (try_block, finally_clause, get_src (begin)); foreach (CatchClause clause in catch_clauses) { stmt.add_catch_clause (clause); } return stmt; } void parse_catch_clauses (List catch_clauses) throws ParseError { while (current () == TokenType.CATCH) { var begin = get_location (); expect (TokenType.CATCH); DataType type = null; string id = null; if (accept (TokenType.OPEN_PARENS)) { type = parse_type (true, true); id = parse_identifier (); expect (TokenType.CLOSE_PARENS); } var src = get_src (begin); var block = parse_block (); catch_clauses.add (new CatchClause (type, id, block, src)); } } Block parse_finally_clause () throws ParseError { expect (TokenType.FINALLY); var block = parse_block (); return block; } Statement parse_lock_statement () throws ParseError { var begin = get_location (); expect (TokenType.LOCK); expect (TokenType.OPEN_PARENS); var expr = parse_expression (); expect (TokenType.CLOSE_PARENS); var src = get_src (begin); Block? stmt = null; if (current () != TokenType.SEMICOLON) { stmt = parse_embedded_statement ("lock", false); } return new LockStatement (expr, stmt, src); } Statement parse_unlock_statement () throws ParseError { var begin = get_location (); expect (TokenType.UNLOCK); expect (TokenType.OPEN_PARENS); var expr = parse_expression (); expect (TokenType.CLOSE_PARENS); var src = get_src (begin); expect (TokenType.SEMICOLON); return new UnlockStatement (expr, src); } Statement parse_delete_statement () throws ParseError { var begin = get_location (); expect (TokenType.DELETE); var expr = parse_expression (); var src = get_src (begin); expect (TokenType.SEMICOLON); return new DeleteStatement (expr, src); } Statement? parse_with_statement () throws ParseError { var begin = get_location (); expect (TokenType.WITH); expect (TokenType.OPEN_PARENS); var expr_or_decl = get_location (); LocalVariable? local = null; // Try "with (expr)" Expression expr; try { expr = parse_expression (); } catch { expr = null; } if (expr == null || !accept (TokenType.CLOSE_PARENS)) { // Try "with (var identifier = expr)" rollback (expr_or_decl); DataType variable_type; bool is_dynamic = accept (TokenType.DYNAMIC); if (accept (TokenType.UNOWNED) && accept (TokenType.VAR)) { variable_type = new VarType (false); variable_type.nullable = accept (TokenType.INTERR); variable_type.is_dynamic = is_dynamic; } else { rollback (expr_or_decl); is_dynamic = accept (TokenType.DYNAMIC); if (accept (TokenType.VAR)) { variable_type = new VarType (); variable_type.nullable = accept (TokenType.INTERR); variable_type.is_dynamic = is_dynamic; } else { variable_type = parse_type (true, true); } } local = parse_local_variable (variable_type, true); expr = local.initializer; expect (TokenType.CLOSE_PARENS); } var src = get_src (begin); if (!context.experimental) { Report.warning (src, "`with' statements are experimental"); } var body = parse_embedded_statement ("with", false); return new WithStatement (local, expr, body, src); } string parse_attribute_value () throws ParseError { switch (current ()) { case TokenType.NULL: case TokenType.TRUE: case TokenType.FALSE: case TokenType.INTEGER_LITERAL: case TokenType.REAL_LITERAL: case TokenType.STRING_LITERAL: next (); return get_last_string (); case TokenType.MINUS: next (); switch (current ()) { case TokenType.INTEGER_LITERAL: case TokenType.REAL_LITERAL: next (); return "-" + get_last_string (); default: throw new ParseError.SYNTAX ("expected number"); } default: throw new ParseError.SYNTAX ("expected literal"); } } List? parse_attributes () throws ParseError { if (current () != TokenType.OPEN_BRACKET) { return null; } var attrs = new ArrayList (); while (accept (TokenType.OPEN_BRACKET)) { do { var begin = get_location (); string id = parse_identifier (); var attr = new Attribute (id, get_src (begin)); if (accept (TokenType.OPEN_PARENS)) { if (current () != TokenType.CLOSE_PARENS) { do { id = parse_identifier (); expect (TokenType.ASSIGN); attr.add_argument (id, parse_attribute_value ()); } while (accept (TokenType.COMMA)); } expect (TokenType.CLOSE_PARENS); } attrs.add (attr); } while (accept (TokenType.COMMA)); expect (TokenType.CLOSE_BRACKET); } return attrs; } void set_attributes (CodeNode node, List? attributes) { if (attributes != null) { foreach (Attribute attr in (List) attributes) { if (node.get_attribute (attr.name) != null) { Report.error (attr.source_reference, "duplicate attribute `%s'", attr.name); } node.attributes.append (attr); } } } void parse_main_block (Symbol parent) throws ParseError { var begin = get_location (); var method = new Method.main_block (new SourceReference (scanner.source_file, begin, begin)); method.access = SymbolAccessibility.PUBLIC; method.binding = MemberBinding.STATIC; method.body = new Block (get_src (begin)); parse_statements (method.body); if (current () != TokenType.EOF) { Report.error (get_current_src (), "expected end of file"); } method.body.source_reference.end = get_current_src ().end; if (!context.experimental) { Report.warning (method.source_reference, "main blocks are experimental"); } parent.add_method (method); } void parse_declaration (Symbol parent, bool root = false) throws ParseError { comment = scanner.pop_comment (); var attrs = parse_attributes (); var begin = get_location (); TokenType last_keyword = current (); while (is_declaration_keyword (current ())) { last_keyword = current (); next (); } switch (current ()) { case TokenType.CONSTRUCT: if (context.profile == Profile.GOBJECT) { rollback (begin); if (!(parent is TypeSymbol)) { throw new ParseError.SYNTAX ("unexpected `construct' declaration"); } parse_constructor_declaration (parent, attrs); return; } break; case TokenType.TILDE: rollback (begin); if (!(parent is TypeSymbol)) { throw new ParseError.SYNTAX ("unexpected `destructor' declaration"); } parse_destructor_declaration (parent, attrs); return; case TokenType.OPEN_BRACE: case TokenType.SEMICOLON: case TokenType.IF: case TokenType.SWITCH: case TokenType.WHILE: case TokenType.DO: case TokenType.FOR: case TokenType.FOREACH: case TokenType.BREAK: case TokenType.CONTINUE: case TokenType.RETURN: case TokenType.YIELD: case TokenType.THROW: case TokenType.TRY: case TokenType.LOCK: case TokenType.UNLOCK: case TokenType.DELETE: case TokenType.VAR: case TokenType.OP_INC: case TokenType.OP_DEC: case TokenType.BASE: case TokenType.THIS: case TokenType.STAR: case TokenType.NEW: // statement if (attrs != null) { // no attributes allowed before statements throw new ParseError.SYNTAX ("expected statement"); } if (!root) { throw new ParseError.SYNTAX ("statements outside blocks allowed only in root namespace"); } rollback (begin); parse_main_block (parent); return; default: if (root) { bool is_expr = is_expression (); if (is_expr) { rollback (begin); parse_main_block (parent); return; } } skip_type (); switch (current ()) { case TokenType.OPEN_BRACE: case TokenType.SEMICOLON: case TokenType.COLON: rollback (begin); switch (last_keyword) { case TokenType.CLASS: parse_class_declaration (parent, attrs); return; case TokenType.ENUM: parse_enum_declaration (parent, attrs); return; case TokenType.ERRORDOMAIN: parse_errordomain_declaration (parent, attrs); return; case TokenType.INTERFACE: parse_interface_declaration (parent, attrs); return; case TokenType.NAMESPACE: parse_namespace_declaration (parent, attrs); return; case TokenType.STRUCT: parse_struct_declaration (parent, attrs); return; default: break; } break; case TokenType.OPEN_PARENS: rollback (begin); if (!(parent is TypeSymbol)) { throw new ParseError.SYNTAX ("unexpected `constructor' declaration"); } parse_creation_method_declaration (parent, attrs); return; default: skip_type (); // might contain type parameter list switch (current ()) { case TokenType.OPEN_PARENS: rollback (begin); switch (last_keyword) { case TokenType.DELEGATE: parse_delegate_declaration (parent, attrs); return; case TokenType.SIGNAL: if (!(parent is ObjectTypeSymbol)) { throw new ParseError.SYNTAX ("unexpected `signal' declaration"); } parse_signal_declaration (parent, attrs); return; default: parse_method_declaration (parent, attrs); return; } case TokenType.ASSIGN: case TokenType.COMMA: case TokenType.SEMICOLON: rollback (begin); switch (last_keyword) { case TokenType.CONST: parse_constant_declaration (parent, attrs); return; default: parse_field_declaration (parent, attrs); return; } case TokenType.OPEN_BRACE: case TokenType.THROWS: rollback (begin); if (!(parent is TypeSymbol)) { throw new ParseError.SYNTAX ("unexpected `property' declaration"); } parse_property_declaration (parent, attrs); return; default: break; } break; } break; } rollback (begin); throw new ParseError.SYNTAX ("expected declaration"); } void parse_declarations (Symbol parent, bool root = false) throws ParseError { if (!root) { expect (TokenType.OPEN_BRACE); } while (current () != TokenType.CLOSE_BRACE && current () != TokenType.EOF) { try { parse_declaration (parent, (parent == context.root)); } catch (ParseError e) { report_parse_error (e); int r; do { r = recover (); if (r == RecoveryState.STATEMENT_BEGIN) { next (); } else { break; } } while (true); if (r == RecoveryState.EOF) { return; } } } if (!root) { if (!accept (TokenType.CLOSE_BRACE)) { // only report error if it's not a secondary error if (context.report.get_errors () == 0) { Report.error (get_current_src (), "expected `}'"); } } } } enum RecoveryState { EOF, DECLARATION_BEGIN, STATEMENT_BEGIN } RecoveryState recover () { while (current () != TokenType.EOF) { switch (current ()) { case TokenType.ABSTRACT: case TokenType.CLASS: case TokenType.CONST: case TokenType.CONSTRUCT: case TokenType.DELEGATE: case TokenType.ENUM: case TokenType.ERRORDOMAIN: case TokenType.EXTERN: case TokenType.INLINE: case TokenType.INTERFACE: case TokenType.INTERNAL: case TokenType.NAMESPACE: case TokenType.NEW: case TokenType.OVERRIDE: case TokenType.PARTIAL: case TokenType.PRIVATE: case TokenType.PROTECTED: case TokenType.PUBLIC: case TokenType.SEALED: case TokenType.SIGNAL: case TokenType.STATIC: case TokenType.STRUCT: case TokenType.VIRTUAL: case TokenType.VOLATILE: return RecoveryState.DECLARATION_BEGIN; case TokenType.BREAK: case TokenType.CONTINUE: case TokenType.DELETE: case TokenType.DO: case TokenType.FOR: case TokenType.FOREACH: case TokenType.IF: case TokenType.LOCK: case TokenType.RETURN: case TokenType.SWITCH: case TokenType.THROW: case TokenType.TRY: case TokenType.UNLOCK: case TokenType.VAR: case TokenType.WHILE: case TokenType.WITH: case TokenType.YIELD: return RecoveryState.STATEMENT_BEGIN; default: next (); break; } } return RecoveryState.EOF; } void parse_namespace_declaration (Symbol parent, List? attrs) throws ParseError { var begin = get_location (); expect (TokenType.NAMESPACE); var sym = parse_symbol_name (); var ns = new Namespace (sym.name, get_src (begin)); if (comment != null) { ns.add_comment (comment); comment = null; } set_attributes (ns, attrs); expect (TokenType.OPEN_BRACE); var old_using_directives = scanner.source_file.current_using_directives; parse_using_directives (ns); parse_declarations (ns, true); scanner.source_file.current_using_directives = old_using_directives; if (!accept (TokenType.CLOSE_BRACE)) { // only report error if it's not a secondary error if (context.report.get_errors () == 0) { Report.error (get_current_src (), "expected `}'"); } } Symbol result = ns; while (sym != null) { sym = sym.inner; Symbol next = (sym != null ? new Namespace (sym.name, ns.source_reference) : parent); next.add_namespace ((Namespace) result); result = next; } } void parse_using_directives (Namespace ns) throws ParseError { while (accept (TokenType.USING)) { do { var begin = get_location (); var sym = parse_symbol_name (); var ns_ref = new UsingDirective (sym, get_src (begin)); scanner.source_file.add_using_directive (ns_ref); ns.add_using_directive (ns_ref); } while (accept (TokenType.COMMA)); expect (TokenType.SEMICOLON); } } void parse_class_declaration (Symbol parent, List? attrs, bool partial_reparse = false) throws ParseError { var begin = get_location (); var access = parse_access_modifier (); var flags = parse_type_declaration_modifiers (); expect (TokenType.CLASS); var sym = parse_symbol_name (); var type_param_list = parse_type_parameter_list (); var base_types = new ArrayList (); if (accept (TokenType.COLON)) { do { base_types.add (parse_type (true, false)); } while (accept (TokenType.COMMA)); } var cl = new Class (sym.name, get_src (begin), comment); cl.access = access; if (ModifierFlags.ABSTRACT in flags) { cl.is_abstract = true; } if (ModifierFlags.SEALED in flags) { cl.is_sealed = true; } if (ModifierFlags.PARTIAL in flags) { if (!context.experimental) { Report.warning (cl.source_reference, "`partial' classes are experimental"); } cl.is_partial = true; } if (ModifierFlags.EXTERN in flags) { cl.is_extern = true; } Class? old_cl = null; if (partial_reparse) { var names = new ArrayList (); while (sym.inner != null) { sym = sym.inner; names.insert (0, sym.name); } Symbol p = parent; while (p != null && p != context.root) { names.insert (0, p.name); p = p.parent_symbol; } p = context.root; foreach (var name in names) { p = p.scope.lookup (name); if (p == null) { break; } } if (p != null) { old_cl = p.scope.lookup (cl.name) as Class; parent = p; } } if (old_cl != null && old_cl.is_partial) { if (cl.is_partial != old_cl.is_partial) { Report.error (cl.source_reference, "conflicting partial and not partial declarations of `%s'", cl.name); cl.error = true; } if (cl.access != old_cl.access) { Report.error (cl.source_reference, "partial declarations of `%s' have conflicting accessiblity modifiers", cl.name); cl.error = true; } if (cl.is_abstract != old_cl.is_abstract) { Report.error (cl.source_reference, "partial declarations of `%s' have conflicting abstract modifiers", cl.name); cl.error = true; } if (cl.is_sealed != old_cl.is_sealed) { Report.error (cl.source_reference, "partial declarations of `%s' have conflicting sealed modifiers", cl.name); cl.error = true; } if (cl.error) { Report.notice (old_cl.source_reference, "previous declaration of `%s' was here", old_cl.name); return; } cl = old_cl; } set_attributes (cl, attrs); foreach (TypeParameter type_param in type_param_list) { cl.add_type_parameter (type_param); } foreach (DataType base_type in base_types) { cl.add_base_type (base_type); } parse_declarations (cl); if (old_cl != null && old_cl.is_partial) { return; } // ensure there is always a default construction method if (scanner.source_file.file_type == SourceFileType.SOURCE && cl.default_construction_method == null) { var m = new CreationMethod (cl.name, null, cl.source_reference); m.access = (cl.is_abstract ? SymbolAccessibility.PROTECTED : SymbolAccessibility.PUBLIC); m.body = new Block (cl.source_reference); cl.add_method (m); } if (partial_reparse) { parent.add_class (cl); return; } Symbol result = cl; while (sym != null) { sym = sym.inner; Symbol next = (sym != null ? new Namespace (sym.name, cl.source_reference) : parent); if (result is Namespace) { next.add_namespace ((Namespace) result); } else if (!partial_reparse && cl.is_partial) { PartialInfo info = { parent, begin, attrs }; partials += info; } else if (!cl.is_partial) { next.add_class ((Class) result); } result = next; } } void parse_constant_declaration (Symbol parent, List? attrs) throws ParseError { var begin = get_location (); var access = parse_access_modifier (); var flags = parse_member_declaration_modifiers (); expect (TokenType.CONST); var type = parse_type (false, false); string id = parse_identifier (); type = parse_inline_array_type (type); var src = get_src (begin); // constant arrays don't own their element unowned ArrayType? array_type = type as ArrayType; if (array_type != null) { array_type.element_type.value_owned = false; } Expression? initializer = null; if (accept (TokenType.ASSIGN)) { initializer = parse_expression (); } expect (TokenType.SEMICOLON); var c = new Constant (id, type, initializer, src, comment); c.access = access; if (ModifierFlags.EXTERN in flags) { c.is_extern = true; } if (ModifierFlags.NEW in flags) { c.hides = true; } set_attributes (c, attrs); if (ModifierFlags.STATIC in flags) { Report.warning (c.source_reference, "the modifier `static' is not applicable to constants"); } if (type.value_owned) { Report.error (c.source_reference, "`owned' is not allowed on constants"); } parent.add_constant (c); } void parse_field_declaration (Symbol parent, List? attrs) throws ParseError { var begin = get_location (); var access = parse_access_modifier ((parent is Struct) ? SymbolAccessibility.PUBLIC : SymbolAccessibility.PRIVATE); var flags = parse_member_declaration_modifiers (); var type = parse_type (true, true); do { string id = parse_identifier (); var ftype = parse_inline_array_type (type.copy ()); var f = new Field (id, ftype, null, get_src (begin), comment); f.access = access; set_attributes (f, attrs); if (ModifierFlags.STATIC in flags && ModifierFlags.CLASS in flags) { Report.error (f.source_reference, "only one of `static' or `class' may be specified"); } else if (ModifierFlags.STATIC in flags) { f.binding = MemberBinding.STATIC; } else if (ModifierFlags.CLASS in flags) { f.binding = MemberBinding.CLASS; } else if (parent is Namespace) { // default to static member binding in namespace f.binding = MemberBinding.STATIC; } if (!parent.external_package && parent is Struct && f.access != SymbolAccessibility.PUBLIC && f.binding == MemberBinding.INSTANCE) { Report.warning (f.source_reference, "accessibility of struct fields can only be `public`"); } if (ModifierFlags.ABSTRACT in flags || ModifierFlags.VIRTUAL in flags || ModifierFlags.OVERRIDE in flags) { Report.error (f.source_reference, "abstract, virtual, and override modifiers are not applicable to fields"); } if (ModifierFlags.EXTERN in flags) { f.is_extern = true; } if (ModifierFlags.NEW in flags) { f.hides = true; } if (accept (TokenType.ASSIGN)) { f.initializer = parse_expression (); } parent.add_field (f); } while (accept (TokenType.COMMA)); expect (TokenType.SEMICOLON); } InitializerList parse_initializer () throws ParseError { var begin = get_location (); expect (TokenType.OPEN_BRACE); var initializer = new InitializerList (get_src (begin)); while (current () != TokenType.CLOSE_BRACE) { try { initializer.append (parse_argument ()); } catch (ParseError e) { if (current () == TokenType.CLOSE_BRACE) { prev (); report_parse_error (new ParseError.SYNTAX ("incomplete initializer")); } else if (context.keep_going) { report_parse_error (e); } else { throw e; } } if (!accept (TokenType.COMMA)) { break; } } expect (TokenType.CLOSE_BRACE); initializer.source_reference = get_src (begin); return initializer; } void parse_method_declaration (Symbol parent, List? attrs) throws ParseError { var begin = get_location (); var access = parse_access_modifier (); var flags = parse_member_declaration_modifiers (); var type = parse_type (true, false); var sym = parse_symbol_name (); var type_param_list = parse_type_parameter_list (); var method = new Method (sym.name, type, get_src (begin), comment); if (sym.inner != null) { method.base_interface_type = new UnresolvedType.from_symbol (sym.inner, sym.inner.source_reference); } method.access = access; set_attributes (method, attrs); foreach (TypeParameter type_param in type_param_list) { method.add_type_parameter (type_param); } if (ModifierFlags.STATIC in flags && ModifierFlags.CLASS in flags) { Report.error (method.source_reference, "only one of `static' or `class' may be specified"); } else if (ModifierFlags.STATIC in flags) { method.binding = MemberBinding.STATIC; } else if (ModifierFlags.CLASS in flags) { method.binding = MemberBinding.CLASS; } else if (parent is Namespace) { // default to static member binding in namespace method.binding = MemberBinding.STATIC; } if (ModifierFlags.ASYNC in flags) { method.coroutine = true; } if (ModifierFlags.NEW in flags) { method.hides = true; } if (method.binding == MemberBinding.INSTANCE) { if (ModifierFlags.ABSTRACT in flags) { method.is_abstract = true; } if (ModifierFlags.VIRTUAL in flags) { method.is_virtual = true; } if (ModifierFlags.OVERRIDE in flags) { method.overrides = true; } if ((method.is_abstract && method.is_virtual) || (method.is_abstract && method.overrides) || (method.is_virtual && method.overrides)) { throw new ParseError.SYNTAX ("only one of `abstract', `virtual', or `override' may be specified"); } } else { if (ModifierFlags.ABSTRACT in flags || ModifierFlags.VIRTUAL in flags || ModifierFlags.OVERRIDE in flags) { throw new ParseError.SYNTAX ("the modifiers `abstract', `virtual', and `override' are not valid for %s methods", (ModifierFlags.CLASS in flags) ? "class" : "static"); } } if (ModifierFlags.INLINE in flags) { method.is_inline = true; } if (ModifierFlags.EXTERN in flags) { method.is_extern = true; } expect (TokenType.OPEN_PARENS); if (current () != TokenType.CLOSE_PARENS) { do { var param = parse_parameter (); method.add_parameter (param); } while (accept (TokenType.COMMA)); } expect (TokenType.CLOSE_PARENS); if (accept (TokenType.THROWS)) { do { method.add_error_type (parse_type (true, false)); } while (accept (TokenType.COMMA)); } while (accept (TokenType.REQUIRES)) { expect (TokenType.OPEN_PARENS); method.add_precondition (parse_expression ()); expect (TokenType.CLOSE_PARENS); } while (accept (TokenType.ENSURES)) { expect (TokenType.OPEN_PARENS); method.add_postcondition (parse_expression ()); expect (TokenType.CLOSE_PARENS); } if (!accept (TokenType.SEMICOLON)) { method.body = parse_block (); method.external = false; } parent.add_method (method); } void parse_property_declaration (Symbol parent, List? attrs) throws ParseError { var begin = get_location (); var access = parse_access_modifier (); var flags = parse_member_declaration_modifiers (); var type = parse_type (true, true); string id = parse_identifier (); var prop = new Property (id, type, null, null, get_src (begin), comment); prop.access = access; set_attributes (prop, attrs); if (ModifierFlags.STATIC in flags && ModifierFlags.CLASS in flags) { Report.error (prop.source_reference, "only one of `static' or `class' may be specified"); } else if (ModifierFlags.STATIC in flags) { prop.binding = MemberBinding.STATIC; } else if (ModifierFlags.CLASS in flags) { prop.binding = MemberBinding.CLASS; } if (ModifierFlags.ABSTRACT in flags) { prop.is_abstract = true; } if (ModifierFlags.VIRTUAL in flags) { prop.is_virtual = true; } if (ModifierFlags.OVERRIDE in flags) { prop.overrides = true; } if (ModifierFlags.NEW in flags) { prop.hides = true; } if (ModifierFlags.ASYNC in flags) { Report.error (prop.source_reference, "async properties are not supported yet"); } if (ModifierFlags.EXTERN in flags) { prop.is_extern = true; } if ((prop.is_abstract && prop.is_virtual) || (prop.is_abstract && prop.overrides) || (prop.is_virtual && prop.overrides)) { throw new ParseError.SYNTAX ("only one of `abstract', `virtual', or `override' may be specified"); } if (accept (TokenType.THROWS)) { do { parse_type (true, false); } while (accept (TokenType.COMMA)); Report.error (prop.source_reference, "properties throwing errors are not supported yet"); } expect (TokenType.OPEN_BRACE); while (current () != TokenType.CLOSE_BRACE) { if (accept (TokenType.DEFAULT)) { if (prop.initializer != null) { throw new ParseError.SYNTAX ("property default value already defined"); } expect (TokenType.ASSIGN); prop.initializer = parse_expression (); expect (TokenType.SEMICOLON); } else { comment = scanner.pop_comment (); var accessor_begin = get_location (); var accessor_attrs = parse_attributes (); var accessor_access = parse_access_modifier (SymbolAccessibility.PUBLIC); var value_type = type.copy (); if (accept (TokenType.OWNED)) { value_type.value_owned = true; } else { value_type.value_owned = false; if (accept (TokenType.UNOWNED)) { Report.warning (get_last_src (), "property getters are `unowned' by default"); } } if (accept (TokenType.GET)) { if (prop.get_accessor != null) { throw new ParseError.SYNTAX ("property get accessor already defined"); } Block block = null; if (!accept (TokenType.SEMICOLON)) { block = parse_block (); prop.external = false; } prop.get_accessor = new PropertyAccessor (true, false, false, value_type, block, get_src (accessor_begin), comment); set_attributes (prop.get_accessor, accessor_attrs); prop.get_accessor.access = accessor_access; } else { bool writable, _construct; if (accept (TokenType.SET)) { writable = true; _construct = (context.profile == Profile.GOBJECT) && accept (TokenType.CONSTRUCT); } else if (context.profile == Profile.GOBJECT && accept (TokenType.CONSTRUCT)) { _construct = true; writable = accept (TokenType.SET); } else { throw new ParseError.SYNTAX ("expected get, set, or construct"); } if (prop.set_accessor != null) { throw new ParseError.SYNTAX ("property set accessor already defined"); } Block block = null; if (!accept (TokenType.SEMICOLON)) { block = parse_block (); prop.external = false; } prop.set_accessor = new PropertyAccessor (false, writable, _construct, value_type, block, get_src (accessor_begin), comment); set_attributes (prop.set_accessor, accessor_attrs); prop.set_accessor.access = accessor_access; } } } expect (TokenType.CLOSE_BRACE); parent.add_property (prop); } void parse_signal_declaration (Symbol parent, List? attrs) throws ParseError { var begin = get_location (); var access = parse_access_modifier (); var flags = parse_member_declaration_modifiers (); expect (TokenType.SIGNAL); var type = parse_type (true, false); string id = parse_identifier (); var sig = new Signal (id, type, get_src (begin), comment); sig.access = access; set_attributes (sig, attrs); if (ModifierFlags.STATIC in flags) { throw new ParseError.SYNTAX ("`static' modifier not allowed on signals"); } else if (ModifierFlags.CLASS in flags) { throw new ParseError.SYNTAX ("`class' modifier not allowed on signals"); } if (ModifierFlags.VIRTUAL in flags) { sig.is_virtual = true; } if (ModifierFlags.NEW in flags) { sig.hides = true; } expect (TokenType.OPEN_PARENS); if (current () != TokenType.CLOSE_PARENS) { do { var param = parse_parameter (); sig.add_parameter (param); } while (accept (TokenType.COMMA)); } expect (TokenType.CLOSE_PARENS); if (!accept (TokenType.SEMICOLON)) { sig.body = parse_block (); } parent.add_signal (sig); } void parse_constructor_declaration (Symbol parent, List? attrs) throws ParseError { var begin = get_location (); var flags = parse_member_declaration_modifiers (); expect (TokenType.CONSTRUCT); if (ModifierFlags.NEW in flags) { throw new ParseError.SYNTAX ("`new' modifier not allowed on constructor"); } var c = new Constructor (get_src (begin)); if (ModifierFlags.STATIC in flags && ModifierFlags.CLASS in flags) { Report.error (c.source_reference, "only one of `static' or `class' may be specified"); } else if (ModifierFlags.STATIC in flags) { c.binding = MemberBinding.STATIC; } else if (ModifierFlags.CLASS in flags) { c.binding = MemberBinding.CLASS; } c.body = parse_block (); parent.add_constructor (c); } void parse_destructor_declaration (Symbol parent, List? attrs) throws ParseError { var begin = get_location (); var flags = parse_member_declaration_modifiers (); expect (TokenType.TILDE); string identifier = parse_identifier (); expect (TokenType.OPEN_PARENS); expect (TokenType.CLOSE_PARENS); if (ModifierFlags.NEW in flags) { throw new ParseError.SYNTAX ("`new' modifier not allowed on destructor"); } var d = new Destructor (get_src (begin)); if (identifier != parent.name) { Report.error (d.source_reference, "destructor and parent symbol name do not match"); } if (ModifierFlags.STATIC in flags && ModifierFlags.CLASS in flags) { Report.error (d.source_reference, "only one of `static' or `class' may be specified"); } else if (ModifierFlags.STATIC in flags) { d.binding = MemberBinding.STATIC; } else if (ModifierFlags.CLASS in flags) { d.binding = MemberBinding.CLASS; } d.body = parse_block (); parent.add_destructor (d); } void parse_struct_declaration (Symbol parent, List? attrs) throws ParseError { var begin = get_location (); var access = parse_access_modifier (); var flags = parse_type_declaration_modifiers (); expect (TokenType.STRUCT); var sym = parse_symbol_name (); var type_param_list = parse_type_parameter_list (); DataType base_type = null; if (accept (TokenType.COLON)) { base_type = parse_type (true, false); } var st = new Struct (sym.name, get_src (begin), comment); st.access = access; if (ModifierFlags.EXTERN in flags) { st.is_extern = true; } set_attributes (st, attrs); foreach (TypeParameter type_param in type_param_list) { st.add_type_parameter (type_param); } if (base_type != null) { st.base_type = base_type; } parse_declarations (st); Symbol result = st; while (sym != null) { sym = sym.inner; Symbol next = (sym != null ? new Namespace (sym.name, st.source_reference) : parent); if (result is Namespace) { next.add_namespace ((Namespace) result); } else { next.add_struct ((Struct) result); } result = next; } } void parse_interface_declaration (Symbol parent, List? attrs) throws ParseError { var begin = get_location (); var access = parse_access_modifier (); var flags = parse_type_declaration_modifiers (); expect (TokenType.INTERFACE); var sym = parse_symbol_name (); var type_param_list = parse_type_parameter_list (); var base_types = new ArrayList (); if (accept (TokenType.COLON)) { do { var type = parse_type (true, false); base_types.add (type); } while (accept (TokenType.COMMA)); } var iface = new Interface (sym.name, get_src (begin), comment); iface.access = access; if (ModifierFlags.EXTERN in flags) { iface.is_extern = true; } set_attributes (iface, attrs); foreach (TypeParameter type_param in type_param_list) { iface.add_type_parameter (type_param); } foreach (DataType base_type in base_types) { iface.add_prerequisite (base_type); } parse_declarations (iface); Symbol result = iface; while (sym != null) { sym = sym.inner; Symbol next = (sym != null ? new Namespace (sym.name, iface.source_reference) : parent); if (result is Namespace) { next.add_namespace ((Namespace) result); } else { next.add_interface ((Interface) result); } result = next; } } void parse_enum_declaration (Symbol parent, List? attrs) throws ParseError { var begin = get_location (); var access = parse_access_modifier (); var flags = parse_type_declaration_modifiers (); expect (TokenType.ENUM); var sym = parse_symbol_name (); var en = new Enum (sym.name, get_src (begin), comment); en.access = access; if (ModifierFlags.EXTERN in flags) { en.is_extern = true; } set_attributes (en, attrs); expect (TokenType.OPEN_BRACE); var inner_begin = get_location (); try { // enum methods while (current () != TokenType.CLOSE_BRACE) { parse_declaration (en); } } catch (ParseError e) { rollback (inner_begin); } do { if (current () == TokenType.CLOSE_BRACE) { break; } var value_attrs = parse_attributes (); var value_begin = get_location (); string id = parse_identifier (); if (comment == null) { comment = scanner.pop_comment (); } Expression value = null; if (accept (TokenType.ASSIGN)) { value = parse_expression (); } var ev = new EnumValue (id, value, get_src (value_begin), comment); ev.access = SymbolAccessibility.PUBLIC; set_attributes (ev, value_attrs); en.add_value (ev); comment = null; } while (accept (TokenType.COMMA)); if (accept (TokenType.SEMICOLON)) { // enum methods while (current () != TokenType.CLOSE_BRACE) { parse_declaration (en); } } expect (TokenType.CLOSE_BRACE); Symbol result = en; while (sym != null) { sym = sym.inner; Symbol next = (sym != null ? new Namespace (sym.name, en.source_reference) : parent); if (result is Namespace) { next.add_namespace ((Namespace) result); } else { next.add_enum ((Enum) result); } result = next; } } void parse_errordomain_declaration (Symbol parent, List? attrs) throws ParseError { var begin = get_location (); var access = parse_access_modifier (); var flags = parse_type_declaration_modifiers (); expect (TokenType.ERRORDOMAIN); var sym = parse_symbol_name (); var ed = new ErrorDomain (sym.name, get_src (begin), comment); ed.access = access; if (ModifierFlags.EXTERN in flags) { ed.is_extern = true; } set_attributes (ed, attrs); expect (TokenType.OPEN_BRACE); var inner_begin = get_location (); try { // errordomain methods while (current () != TokenType.CLOSE_BRACE) { parse_declaration (ed); } } catch (ParseError e) { rollback (inner_begin); } do { if (current () == TokenType.CLOSE_BRACE) { break; } var code_attrs = parse_attributes (); var code_begin = get_location (); string id = parse_identifier (); if (comment == null) { comment = scanner.pop_comment (); } var ec = new ErrorCode (id, get_src (code_begin), comment); ec.access = SymbolAccessibility.PUBLIC; set_attributes (ec, code_attrs); if (accept (TokenType.ASSIGN)) { ec.value = parse_expression (); } ed.add_code (ec); comment = null; } while (accept (TokenType.COMMA)); if (accept (TokenType.SEMICOLON)) { // errordomain methods while (current () != TokenType.CLOSE_BRACE) { parse_declaration (ed); } } expect (TokenType.CLOSE_BRACE); Symbol result = ed; while (sym != null) { sym = sym.inner; Symbol next = (sym != null ? new Namespace (sym.name, ed.source_reference) : parent); if (result is Namespace) { next.add_namespace ((Namespace) result); } else { next.add_error_domain ((ErrorDomain) result); } result = next; } } SymbolAccessibility parse_access_modifier (SymbolAccessibility default_access = SymbolAccessibility.PRIVATE) { switch (current ()) { case TokenType.PRIVATE: next (); return SymbolAccessibility.PRIVATE; case TokenType.PROTECTED: next (); return SymbolAccessibility.PROTECTED; case TokenType.INTERNAL: next (); return SymbolAccessibility.INTERNAL; case TokenType.PUBLIC: next (); return SymbolAccessibility.PUBLIC; default: return default_access; } } ModifierFlags parse_type_declaration_modifiers () { ModifierFlags flags = 0; while (true) { switch (current ()) { case TokenType.ABSTRACT: next (); flags |= ModifierFlags.ABSTRACT; break; case TokenType.EXTERN: next (); flags |= ModifierFlags.EXTERN; break; case TokenType.PARTIAL: next (); flags |= ModifierFlags.PARTIAL; break; case TokenType.SEALED: next (); flags |= ModifierFlags.SEALED; break; default: return flags; } } } ModifierFlags parse_member_declaration_modifiers () { ModifierFlags flags = 0; while (true) { switch (current ()) { case TokenType.ABSTRACT: next (); flags |= ModifierFlags.ABSTRACT; break; case TokenType.ASYNC: next (); flags |= ModifierFlags.ASYNC; break; case TokenType.CLASS: next (); flags |= ModifierFlags.CLASS; break; case TokenType.EXTERN: next (); flags |= ModifierFlags.EXTERN; break; case TokenType.INLINE: next (); flags |= ModifierFlags.INLINE; break; case TokenType.NEW: next (); flags |= ModifierFlags.NEW; break; case TokenType.OVERRIDE: next (); flags |= ModifierFlags.OVERRIDE; break; case TokenType.SEALED: next (); flags |= ModifierFlags.SEALED; break; case TokenType.STATIC: next (); flags |= ModifierFlags.STATIC; break; case TokenType.VIRTUAL: next (); flags |= ModifierFlags.VIRTUAL; break; default: return flags; } } } Parameter parse_parameter () throws ParseError { var attrs = parse_attributes (); var begin = get_location (); if (accept (TokenType.ELLIPSIS)) { // varargs return new Parameter.with_ellipsis (get_src (begin)); } bool params_array = accept (TokenType.PARAMS); var direction = ParameterDirection.IN; if (accept (TokenType.OUT)) { direction = ParameterDirection.OUT; } else if (accept (TokenType.REF)) { direction = ParameterDirection.REF; } DataType type; if (direction == ParameterDirection.IN) { // in parameters are unowned by default type = parse_type (false, false); } else if (direction == ParameterDirection.REF) { // ref parameters own the value by default type = parse_type (true, true); } else { // out parameters own the value by default type = parse_type (true, false); } string id = parse_identifier (); type = parse_inline_array_type (type); var param = new Parameter (id, type, get_src (begin)); set_attributes (param, attrs); param.direction = direction; param.params_array = params_array; if (accept (TokenType.ASSIGN)) { param.initializer = parse_expression (); } return param; } void parse_creation_method_declaration (Symbol parent, List? attrs) throws ParseError { var begin = get_location (); var access = parse_access_modifier (); var flags = parse_member_declaration_modifiers (); var sym = parse_symbol_name (); if (ModifierFlags.NEW in flags) { throw new ParseError.SYNTAX ("`new' modifier not allowed on creation method"); } CreationMethod method; if (sym.inner == null) { method = new CreationMethod (sym.name, null, get_src (begin), comment); } else { method = new CreationMethod (sym.inner.name, sym.name, get_src (begin), comment); } if (ModifierFlags.EXTERN in flags) { method.is_extern = true; } if (ModifierFlags.ABSTRACT in flags || ModifierFlags.VIRTUAL in flags || ModifierFlags.OVERRIDE in flags) { Report.error (method.source_reference, "abstract, virtual, and override modifiers are not applicable to creation methods"); } if (ModifierFlags.ASYNC in flags) { method.coroutine = true; } expect (TokenType.OPEN_PARENS); if (current () != TokenType.CLOSE_PARENS) { do { var param = parse_parameter (); method.add_parameter (param); } while (accept (TokenType.COMMA)); } expect (TokenType.CLOSE_PARENS); if (accept (TokenType.THROWS)) { do { method.add_error_type (parse_type (true, false)); } while (accept (TokenType.COMMA)); } while (accept (TokenType.REQUIRES)) { expect (TokenType.OPEN_PARENS); method.add_precondition (parse_expression ()); expect (TokenType.CLOSE_PARENS); } while (accept (TokenType.ENSURES)) { expect (TokenType.OPEN_PARENS); method.add_postcondition (parse_expression ()); expect (TokenType.CLOSE_PARENS); } method.access = access; set_attributes (method, attrs); if (!accept (TokenType.SEMICOLON)) { method.body = parse_block (); method.external = false; } parent.add_method (method); } void parse_delegate_declaration (Symbol parent, List? attrs) throws ParseError { var begin = get_location (); var access = parse_access_modifier (); var flags = parse_member_declaration_modifiers (); expect (TokenType.DELEGATE); if (ModifierFlags.NEW in flags) { throw new ParseError.SYNTAX ("`new' modifier not allowed on delegates"); } var type = parse_type (true, false); var sym = parse_symbol_name (); var type_param_list = parse_type_parameter_list (); var d = new Delegate (sym.name, type, get_src (begin), comment); d.access = access; set_attributes (d, attrs); if (ModifierFlags.STATIC in flags) { if (!context.deprecated) { // TODO enable warning in future releases Report.warning (get_last_src (), "deprecated syntax, use [CCode (has_target = false)]"); } d.has_target = false; } if (ModifierFlags.EXTERN in flags) { d.is_extern = true; } if (!d.get_attribute_bool ("CCode", "has_typedef", true)) { if (!d.external) { Report.error (get_last_src (), "Delegates without definition must be external"); } d.anonymous = true; } foreach (TypeParameter type_param in type_param_list) { d.add_type_parameter (type_param); } expect (TokenType.OPEN_PARENS); if (current () != TokenType.CLOSE_PARENS) { do { var param = parse_parameter (); d.add_parameter (param); } while (accept (TokenType.COMMA)); } expect (TokenType.CLOSE_PARENS); if (accept (TokenType.THROWS)) { do { d.add_error_type (parse_type (true, false)); } while (accept (TokenType.COMMA)); } expect (TokenType.SEMICOLON); Symbol result = d; while (sym != null) { sym = sym.inner; Symbol next = (sym != null ? new Namespace (sym.name, d.source_reference) : parent); if (result is Namespace) { next.add_namespace ((Namespace) result); } else { next.add_delegate ((Delegate) result); } result = next; } } List parse_type_parameter_list () throws ParseError { if (accept (TokenType.OP_LT)) { var list = new ArrayList (); do { var begin = get_location (); string id = parse_identifier (); list.add (new TypeParameter (id, get_src (begin))); } while (accept (TokenType.COMMA)); expect (TokenType.OP_GT); return list; } else { if (_empty_type_parameter_list == null) { _empty_type_parameter_list = new ArrayList (); } return _empty_type_parameter_list; } } void skip_type_argument_list () throws ParseError { if (accept (TokenType.OP_LT)) { do { skip_type (); } while (accept (TokenType.COMMA)); expect (TokenType.OP_GT); } } // try to parse type argument list List? parse_type_argument_list (bool maybe_expression) throws ParseError { var begin = get_location (); if (accept (TokenType.OP_LT)) { var list = new ArrayList (); do { switch (current ()) { case TokenType.VOID: case TokenType.DYNAMIC: case TokenType.UNOWNED: case TokenType.WEAK: case TokenType.IDENTIFIER: var type = parse_type (true, true); list.add (type); break; default: rollback (begin); return null; } } while (accept (TokenType.COMMA)); if (!accept (TokenType.OP_GT)) { rollback (begin); return null; } if (maybe_expression) { // check follower to decide whether to keep type argument list switch (current ()) { case TokenType.OPEN_PARENS: case TokenType.CLOSE_PARENS: case TokenType.CLOSE_BRACKET: case TokenType.OPEN_BRACE: case TokenType.COLON: case TokenType.SEMICOLON: case TokenType.COMMA: case TokenType.DOT: case TokenType.INTERR: case TokenType.OP_EQ: case TokenType.OP_NE: // keep type argument list break; default: // interpret tokens as expression rollback (begin); return null; } } return list; } return null; } MemberAccess parse_member_name (Expression? base_expr = null) throws ParseError { var begin = get_location (); MemberAccess expr = null; bool first = true; do { string id = parse_identifier (); // The first member access can be global:: qualified bool qualified = false; if (first && id == "global" && accept (TokenType.DOUBLE_COLON)) { id = parse_identifier (); qualified = true; } List type_arg_list = parse_type_argument_list (false); expr = new MemberAccess (expr != null ? expr : base_expr, id, get_src (begin)); expr.qualified = qualified; if (type_arg_list != null) { foreach (DataType type_arg in type_arg_list) { expr.add_type_argument (type_arg); } } first = false; } while (accept (TokenType.DOT)); return expr; } bool is_declaration_keyword (TokenType type) { switch (type) { case TokenType.ABSTRACT: case TokenType.ASYNC: case TokenType.CLASS: case TokenType.CONST: case TokenType.DELEGATE: case TokenType.ENUM: case TokenType.ERRORDOMAIN: case TokenType.EXTERN: case TokenType.INLINE: case TokenType.INTERFACE: case TokenType.INTERNAL: case TokenType.NAMESPACE: case TokenType.NEW: case TokenType.OVERRIDE: case TokenType.PARTIAL: case TokenType.PRIVATE: case TokenType.PROTECTED: case TokenType.PUBLIC: case TokenType.SEALED: case TokenType.SIGNAL: case TokenType.STATIC: case TokenType.STRUCT: case TokenType.VIRTUAL: case TokenType.VOLATILE: return true; default: return false; } } } public errordomain Vala.ParseError { FAILED, SYNTAX }