diff options
-rw-r--r-- | ChangeLog | 12 | ||||
-rw-r--r-- | compiler/valacompiler.vala | 9 | ||||
-rw-r--r-- | vala/Makefile.am | 3 | ||||
-rw-r--r-- | vala/valagenieparser.vala | 3210 | ||||
-rw-r--r-- | vala/valageniescanner.vala | 1017 | ||||
-rw-r--r-- | vala/valagenietokentype.vala | 293 | ||||
-rw-r--r-- | vala/valaparser.vala | 4 | ||||
-rw-r--r-- | vala/valasourcefile.vala | 17 |
8 files changed, 4551 insertions, 14 deletions
@@ -1,3 +1,15 @@ +2008-05-19 Jürg Billeter <j@bitron.ch> + + * vala/Makefile.am: + * vala/valagenieparser.vala: + * vala/valageniescanner.vala: + * vala/valagenietokentype.vala: + * vala/valaparser.vala: + * vala/valasourcefile.vala: + * compiler/valacompiler.vala: + + Add parser for Genie, patch by Jamie McCracken + 2008-05-18 Jürg Billeter <j@bitron.ch> * gobject/valaccodegenerator.vala: diff --git a/compiler/valacompiler.vala b/compiler/valacompiler.vala index 3131875cd..1360111e9 100644 --- a/compiler/valacompiler.vala +++ b/compiler/valacompiler.vala @@ -199,14 +199,14 @@ class Vala.Compiler : Object { foreach (string source in sources) { if (FileUtils.test (source, FileTest.EXISTS)) { var rpath = realpath (source); - if (source.has_suffix (".vala")) { + if (source.has_suffix (".vala") || source.has_suffix (".gs")) { context.add_source_file (new SourceFile (context, rpath)); } else if (source.has_suffix (".vapi")) { context.add_source_file (new SourceFile (context, rpath, true)); } else if (source.has_suffix (".c")) { context.add_c_source_file (rpath); } else { - Report.error (null, "%s is not a supported source file type. Only .vala, .vapi, and .c files are supported.".printf (source)); + Report.error (null, "%s is not a supported source file type. Only .vala, .vapi, .gs, and .c files are supported.".printf (source)); } } else { Report.error (null, "%s not found".printf (source)); @@ -220,7 +220,10 @@ class Vala.Compiler : Object { var parser = new Parser (); parser.parse (context); - + + var genie_parser = new Genie.Parser (); + genie_parser.parse (context); + if (Report.get_errors () > 0) { return quit (); } diff --git a/vala/Makefile.am b/vala/Makefile.am index 89aa6e801..958fe1f4c 100644 --- a/vala/Makefile.am +++ b/vala/Makefile.am @@ -70,6 +70,9 @@ libvalacore_la_VALASOURCES = \ valaforeachstatement.vala \ valaformalparameter.vala \ valaforstatement.vala \ + valagenieparser.vala \ + valageniescanner.vala \ + valagenietokentype.vala \ valaifstatement.vala \ valainitializerlist.vala \ valainstancecast.vala \ diff --git a/vala/valagenieparser.vala b/vala/valagenieparser.vala new file mode 100644 index 000000000..f2f2e90ef --- /dev/null +++ b/vala/valagenieparser.vala @@ -0,0 +1,3210 @@ +/* valagenieparser.vala + * + * Copyright (C) 2008 Jamie McCracken, Jürg Billeter + * Based on code by 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: + * Jamie McCracken jamiemcc gnome org + */ + +using GLib; +using Gee; + + +/** + * Code visitor parsing all Genie source files. + */ +public class Vala.Genie.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; + + string comment; + + string class_name; + + /* hack needed to know if any part of an expression is a lambda one */ + bool current_expr_is_lambda; + + const int BUFFER_SIZE = 32; + + struct TokenInfo { + public TokenType type; + public SourceLocation begin; + public SourceLocation end; + } + + enum ModifierFlags { + NONE, + ABSTRACT = 1 << 0, + CLASS = 1 << 1, + EXTERN = 1 << 2, + INLINE = 1 << 3, + OVERRIDE = 1 << 4, + STATIC = 1 << 5, + VIRTUAL = 1 << 6, + PRIVATE = 1 << 7 + } + + construct { + tokens = new TokenInfo[BUFFER_SIZE]; + class_name = null; + current_expr_is_lambda = false; + } + + /** + * Parses all .gs 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); + } + + public override void visit_source_file (SourceFile source_file) { + if (source_file.filename.has_suffix (".gs")) { + 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 = type; + tokens[index].begin = begin; + tokens[index].end = 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 TokenType current () { + return tokens[index].type; + } + + inline bool accept (TokenType type) { + if (current () == type) { + next (); + return true; + } + return false; + } + + inline bool accept_terminator () { + if (current () == TokenType.SEMICOLON || current () == TokenType.EOL) { + next (); + return true; + } + return false; + } + + inline bool accept_block () { + + bool has_term = accept_terminator (); + + if (accept (TokenType.INDENT)) { + prev(); + return true; + } + + if (has_term) { + prev (); + } + + return false; + } + + string get_error (string msg) { + var begin = get_location (); + next (); + Report.error (get_src (begin), "syntax error, " + msg); + return msg; + } + + inline bool expect (TokenType type) throws ParseError { + if (accept (type)) { + return true; + } + + TokenType cur = current (); + TokenType pre = tokens[index - 1].type; + + throw new ParseError.SYNTAX (get_error ("expected %s but got %s with previous %s".printf (type.to_string (), cur.to_string (), pre.to_string()))); + } + + inline bool expect_terminator () throws ParseError { + if (accept_terminator ()) { + return true; + } + + TokenType cur = current (); + + throw new ParseError.SYNTAX (get_error ("expected line end or semicolon but got %s".printf (cur.to_string()))); + } + + inline SourceLocation get_location () { + return tokens[index].begin; + } + + string get_last_string () { + int last_index = (index + BUFFER_SIZE - 1) % BUFFER_SIZE; + return ((string) tokens[last_index].begin.pos).ndup ((tokens[last_index].end.pos - tokens[last_index].begin.pos)); + } + + SourceReference get_src (SourceLocation begin) { + int last_index = (index + BUFFER_SIZE - 1) % BUFFER_SIZE; + + return new SourceReference (scanner.source_file, begin.line, begin.column, tokens[last_index].end.line, tokens[last_index].end.column); + } + + SourceReference get_src_com (SourceLocation begin) { + int last_index = (index + BUFFER_SIZE - 1) % BUFFER_SIZE; + + var src = new SourceReference.with_comment (scanner.source_file, begin.line, begin.column, tokens[last_index].end.line, tokens[last_index].end.column, comment); + comment = null; + return src; + } + + SourceReference get_current_src () { + return new SourceReference (scanner.source_file, tokens[index].begin.line, tokens[index].begin.column, tokens[index].end.line, tokens[index].end.column); + } + + SourceReference get_last_src () { + int last_index = (index + BUFFER_SIZE - 1) % BUFFER_SIZE; + + return new SourceReference (scanner.source_file, tokens[last_index].begin.line, tokens[last_index].begin.column, tokens[last_index].end.line, tokens[last_index].end.column); + } + + void rollback (SourceLocation location) { + while (tokens[index].begin.pos != location.pos) { + prev (); + } + } + + inline SymbolAccessibility get_access (string s) { + if (s[0] == '_') { + return SymbolAccessibility.PRIVATE; + } + + return SymbolAccessibility.PUBLIC; + } + + 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.ASSERT: + case TokenType.BREAK: + case TokenType.CLASS: + case TokenType.CONST: + case TokenType.CONTINUE: + case TokenType.DEDENT: + case TokenType.DEF: + case TokenType.DEFAULT: + case TokenType.DELEGATE: + case TokenType.DELETE: + case TokenType.DO: + case TokenType.DOWNTO: + case TokenType.DYNAMIC: + case TokenType.ELSE: + case TokenType.EOL: + case TokenType.ENUM: + case TokenType.ENSURES: + case TokenType.ERRORDOMAIN: + case TokenType.EVENT: + case TokenType.EXCEPT: + case TokenType.EXTERN: + case TokenType.FALSE: + case TokenType.FINAL: + case TokenType.FINALLY: + case TokenType.FOR: + case TokenType.FOREACH: + case TokenType.GET: + case TokenType.IDENTIFIER: + case TokenType.IF: + case TokenType.IN: + case TokenType.INDENT: + case TokenType.INIT: + case TokenType.INLINE: + case TokenType.INTERFACE: + case TokenType.IS: + case TokenType.ISA: + case TokenType.LOCK: + case TokenType.NAMESPACE: + case TokenType.NEW: + case TokenType.NULL: + case TokenType.OF: + case TokenType.OUT: + case TokenType.OVERRIDE: + case TokenType.PASS: + case TokenType.PRINT: + case TokenType.PRIVATE: + case TokenType.PROP: + case TokenType.RAISE: + case TokenType.RAISES: + case TokenType.REF: + case TokenType.REQUIRES: + case TokenType.RETURN: + case TokenType.SET: + case TokenType.SIZEOF: + case TokenType.STATIC: + case TokenType.STRUCT: + case TokenType.SUPER: + case TokenType.THIS: + case TokenType.TO: + case TokenType.TRUE: + case TokenType.TRY: + case TokenType.TYPEOF: + case TokenType.USES: + case TokenType.VAR: + case TokenType.VIRTUAL: + case TokenType.VOID: + case TokenType.VOLATILE: + case TokenType.WEAK: + case TokenType.WHEN: + case TokenType.WHILE: + next (); + return; + } + + throw new ParseError.SYNTAX (get_error ("expected identifier")); + } + + string parse_identifier () throws ParseError { + skip_identifier (); + 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 (); + return new CharacterLiteral (get_last_string (), get_src (begin)); + case TokenType.STRING_LITERAL: + next (); + return new StringLiteral (get_last_string (), get_src (begin)); + case TokenType.NULL: + next (); + return new NullLiteral (get_src (begin)); + } + + throw new ParseError.SYNTAX (get_error ("expected literal")); + } + + public void parse_file (SourceFile source_file) { + scanner = new Scanner (source_file); + + index = -1; + size = 0; + + next (); + + try { + parse_using_directives (); + parse_declarations (context.root, true); + } catch (ParseError e) { + // already reported + } + + scanner = null; + } + + void skip_symbol_name () throws ParseError { + do { + skip_identifier (); + } while (accept (TokenType.DOT)); + } + + UnresolvedSymbol parse_symbol_name () throws ParseError { + var begin = get_location (); + UnresolvedSymbol sym = null; + do { + string name = parse_identifier (); + sym = new UnresolvedSymbol (sym, name, get_src (begin)); + } while (accept (TokenType.DOT)); + return sym; + } + + void skip_type () throws ParseError { + if (accept (TokenType.VOID)) { + while (accept (TokenType.STAR)) { + } + return; + } + accept (TokenType.DYNAMIC); + + accept (TokenType.WEAK); + skip_symbol_name (); + skip_type_argument_list (); + while (accept (TokenType.OPEN_BRACKET)) { + do { + if (current () != TokenType.COMMA && current () != TokenType.CLOSE_BRACKET) { + parse_expression (); + } + } while (accept (TokenType.COMMA)); + expect (TokenType.CLOSE_BRACKET); + } + accept (TokenType.OP_NEG); + accept (TokenType.INTERR); + accept (TokenType.HASH); + } + + DataType parse_type (bool owned_by_default = true) throws ParseError { + var begin = get_location (); + + if (accept (TokenType.VOID)) { + DataType type = new VoidType (); + while (accept (TokenType.STAR)) { + type = new PointerType (type); + } + return type; + } + + bool is_dynamic = accept (TokenType.DYNAMIC); + bool value_owned = owned_by_default; + if (owned_by_default) { + value_owned = !accept (TokenType.WEAK); + } + + var sym = parse_symbol_name (); + Gee.List<DataType> type_arg_list = parse_type_argument_list (false); + + DataType 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); + } + + while (accept (TokenType.OPEN_BRACKET)) { + int array_rank = 0; + do { + array_rank++; + // support for stack-allocated arrays + // also required for decision between expression and declaration statement + if (current () != TokenType.COMMA && current () != TokenType.CLOSE_BRACKET) { + parse_expression (); + } + } + while (accept (TokenType.COMMA)); + expect (TokenType.CLOSE_BRACKET); + + type.value_owned = true; + type = new ArrayType (type, array_rank, get_src (begin)); + type.nullable = accept (TokenType.INTERR); + } + + if (!owned_by_default) { + value_owned = accept (TokenType.HASH); + } + + type.is_dynamic = is_dynamic; + type.value_owned = value_owned; + return type; + } + + Gee.List<Expression> parse_argument_list () throws ParseError { + var list = new ArrayList<Expression> (); + if (current () != TokenType.CLOSE_PARENS) { + do { + list.add (parse_expression ()); + } while (accept (TokenType.COMMA)); + } + return list; + } + + 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.NULL: + expr = parse_literal (); + break; + case TokenType.ASSERT: + return parse_assert_expression (); + case TokenType.OPEN_PARENS: + expr = parse_tuple (); + break; + case TokenType.THIS: + expr = parse_this_access (); + break; + case TokenType.SUPER: + expr = parse_base_access (); + break; + case TokenType.NEW: + expr = parse_object_or_array_creation_expression (); + break; + case TokenType.PRINT: + return parse_print_expression (); + case TokenType.SIZEOF: + expr = parse_sizeof_expression (); + break; + case TokenType.TYPEOF: + expr = parse_typeof_expression (); + break; + default: + expr = parse_simple_name (); + break; + } + + if (expr == null) { + // workaround for current limitation of exception handling + throw new ParseError.SYNTAX ("syntax error in primary expression"); + } + + // process primary expressions that start with an inner primary expression + bool found = true; + while (found) { + switch (current ()) { + 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_invocation_expression (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; + } + + if (expr == null) { + // workaround for current limitation of exception handling + throw new ParseError.SYNTAX ("syntax error in primary expression"); + } + } + + return expr; + } + + Expression parse_simple_name () throws ParseError { + var begin = get_location (); + string id = parse_identifier (); + Gee.List<DataType> type_arg_list = parse_type_argument_list (true); + var expr = new MemberAccess (null, 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_tuple () throws ParseError { + var begin = get_location (); + expect (TokenType.OPEN_PARENS); + var expr_list = new ArrayList<Expression> (); + 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 (); + foreach (Expression expr in expr_list) { + tuple.add_expression (expr); + } + return tuple; + } + return new ParenthesizedExpression (expr_list.get (0), get_src (begin)); + } + + Expression parse_member_access (SourceLocation begin, Expression inner) throws ParseError { + expect (TokenType.DOT); + string id = parse_identifier (); + Gee.List<DataType> type_arg_list = parse_type_argument_list (true); + var expr = new MemberAccess (inner, id, get_src (begin)); + if (type_arg_list != null) { + foreach (DataType type_arg in type_arg_list) { + expr.add_type_argument (type_arg); + } + } + return expr; + } + + Expression parse_pointer_member_access (SourceLocation begin, Expression inner) throws ParseError { + expect (TokenType.OP_PTR); + string id = parse_identifier (); + Gee.List<DataType> 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; + } + + + Gee.List<Expression> parse_print_argument_list () throws ParseError { + var list = new ArrayList<Expression> (); + var i = 0; + var begin = get_location (); + + if (current () != TokenType.CLOSE_PARENS) { + do { + var p_expr = parse_expression (); + if (i == 0) { + i++; + + if (p_expr != null) { + string s = "\"\\n\""; + var rhs = new StringLiteral (s, get_src (begin)); + p_expr = new BinaryExpression (BinaryOperator.PLUS, p_expr, rhs, get_src (begin)); + } + + } + list.add (p_expr); + + } while (accept (TokenType.COMMA)); + } + return list; + } + + Expression parse_print_expression () throws ParseError { + var begin = get_location (); + + expect (TokenType.PRINT); + accept (TokenType.OPEN_PARENS); + + var expr = new MemberAccess (null, "print", get_src (begin)); + + var arg_list = parse_print_argument_list (); + + accept (TokenType.CLOSE_PARENS); + + var print_expr = new InvocationExpression (expr, get_src (begin)); + + foreach (Expression arg in arg_list) { + print_expr.add_argument (arg); + } + + return print_expr; + + } + + Expression parse_assert_expression () throws ParseError { + var begin = get_location (); + + expect (TokenType.ASSERT); + accept (TokenType.OPEN_PARENS); + + var expr = new MemberAccess (null, "assert", get_src (begin)); + + var arg_list = parse_argument_list (); + + accept (TokenType.CLOSE_PARENS); + + var assert_expr = new InvocationExpression (expr, get_src (begin)); + + foreach (Expression arg in arg_list) { + assert_expr.add_argument (arg); + } + + return assert_expr; + + } + + Expression parse_invocation_expression (SourceLocation begin, Expression inner) throws ParseError { + expect (TokenType.OPEN_PARENS); + var arg_list = parse_argument_list (); + expect (TokenType.CLOSE_PARENS); + var init_list = parse_object_initializer (); + + if (init_list.size > 0 && inner is MemberAccess) { + // struct creation expression + var member = (MemberAccess) inner; + member.creation_member = true; + + var expr = new ObjectCreationExpression (member, get_src (begin)); + 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 InvocationExpression (inner, get_src (begin)); + foreach (Expression arg in arg_list) { + expr.add_argument (arg); + } + return expr; + } + } + + Expression parse_element_access (SourceLocation begin, Expression inner) throws ParseError { + expect (TokenType.OPEN_BRACKET); + var index_list = parse_expression_list (); + expect (TokenType.CLOSE_BRACKET); + + var expr = new ElementAccess (inner, get_src (begin)); + foreach (Expression index in index_list) { + expr.append_index (index); + } + return expr; + } + + Gee.List<Expression> parse_expression_list () throws ParseError { + var list = new ArrayList<Expression> (); + 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.SUPER); + 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); + var member = parse_member_name (); + if (accept (TokenType.OPEN_PARENS)) { + var expr = parse_object_creation_expression (begin, member); + return expr; + } else if (accept (TokenType.OPEN_BRACKET)) { + var expr = parse_array_creation_expression (begin, member); + return expr; + } else { + throw new ParseError.SYNTAX (get_error ("expected ( or [")); + } + } + + Expression parse_object_creation_expression (SourceLocation begin, MemberAccess member) throws ParseError { + member.creation_member = true; + var arg_list = parse_argument_list (); + expect (TokenType.CLOSE_PARENS); + var init_list = parse_object_initializer (); + + var expr = new ObjectCreationExpression (member, get_src (begin)); + 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 (SourceLocation begin, MemberAccess member) throws ParseError { + bool size_specified = false; + Gee.List<Expression> size_specifier_list; + bool first = true; + DataType element_type = UnresolvedType.new_from_expression (member); + do { + if (!first) { + // array of arrays: new T[][42] + element_type = new ArrayType (element_type, size_specifier_list.size, element_type.source_reference); + } else { + first = false; + } + + size_specifier_list = new ArrayList<Expression> (); + do { + Expression size = null; + if (current () != TokenType.CLOSE_BRACKET && current () != TokenType.COMMA) { + size = parse_expression (); + size_specified = true; + } + size_specifier_list.add (size); + } while (accept (TokenType.COMMA)); + expect (TokenType.CLOSE_BRACKET); + } while (accept (TokenType.OPEN_BRACKET)); + + InitializerList initializer = null; + if (current () == TokenType.OPEN_BRACE) { + initializer = parse_initializer (); + } + var expr = new ArrayCreationExpression (element_type, size_specifier_list.size, initializer, get_src (begin)); + if (size_specified) { + foreach (Expression size in size_specifier_list) { + expr.append_size (size); + } + } + return expr; + } + + Gee.List<MemberInitializer> parse_object_initializer () throws ParseError { + var list = new ArrayList<MemberInitializer> (); + if (accept (TokenType.OPEN_BRACE)) { + do { + list.add (parse_member_initializer ()); + } while (accept (TokenType.COMMA)); + expect (TokenType.CLOSE_BRACE); + } + return list; + } + + MemberInitializer parse_member_initializer () throws ParseError { + var begin = get_location (); + string id = parse_identifier (); + expect (TokenType.ASSIGN); + var expr = parse_expression (); + + return new MemberInitializer (id, expr, get_src (begin)); + } + + Expression parse_sizeof_expression () throws ParseError { + var begin = get_location (); + expect (TokenType.SIZEOF); + expect (TokenType.OPEN_PARENS); + var type = parse_type (); + 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 (); + 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; + case TokenType.REF: return UnaryOperator.REF; + case TokenType.OUT: return UnaryOperator.OUT; + 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 (); + return new UnaryExpression (operator, op, get_src (begin)); + } + switch (current ()) { + case TokenType.HASH: + next (); + var op = parse_unary_expression (); + return new ReferenceTransferExpression (op, get_src (begin)); + case TokenType.OPEN_PARENS: + next (); + switch (current ()) { + case TokenType.VOID: + case TokenType.DYNAMIC: + case TokenType.WEAK: + case TokenType.IDENTIFIER: + var type = parse_type (); + 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.NULL: + case TokenType.THIS: + case TokenType.SUPER: + case TokenType.NEW: + case TokenType.SIZEOF: + case TokenType.TYPEOF: + case TokenType.IDENTIFIER: + if (!type.value_owned) { + Report.warning (get_src (begin), "obsolete syntax, weak type modifier unused in cast expressions"); + } + var inner = parse_unary_expression (); + return new CastExpression (inner, type, get_src (begin), false); + } + } + 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)); + } + + 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.IS: + next(); + if (current () == TokenType.OP_NEG) { + prev (); + return BinaryOperator.INEQUALITY; + } + prev (); + 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_relational_expression () throws ParseError { + var begin = get_location (); + var left = parse_shift_expression (); + 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: + next (); + var right = parse_shift_expression (); + left = new BinaryExpression (operator, left, right, get_src (begin)); + 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_shift_expression (); + left = new BinaryExpression (operator, left, right, get_src (begin)); + } else { + prev (); + found = false; + } + break; + default: + switch (current ()) { + case TokenType.ISA: + next (); + var type = parse_type (); + left = new TypeCheck (left, type, get_src (begin)); + break; + case TokenType.AS: + next (); + var type = parse_type (); + left = new CastExpression (left, type, get_src (begin), true); + break; + default: + found = false; + break; + } + break; + } + } + return left; + } + + Expression parse_equality_expression () throws ParseError { + var begin = get_location (); + var left = parse_relational_expression (); + bool found = true; + while (found) { + var operator = get_binary_operator (current ()); + switch (operator) { + case BinaryOperator.INEQUALITY: + case BinaryOperator.EQUALITY: + if ((operator == BinaryOperator.INEQUALITY) && (current () == TokenType.IS)) { + next (); + } + next (); + var right = parse_relational_expression (); + left = new BinaryExpression (operator, left, right, get_src (begin)); + break; + default: + found = false; + break; + } + } + return left; + } + + Expression parse_and_expression () throws ParseError { + var begin = get_location (); + var left = parse_equality_expression (); + while (accept (TokenType.BITWISE_AND)) { + var right = parse_equality_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_conditional_expression () throws ParseError { + var begin = get_location (); + var condition = parse_conditional_or_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; + } + } + + Expression parse_lambda_expression () throws ParseError { + var begin = get_location (); + Gee.List<string> params = new ArrayList<string> (); + + expect (TokenType.DEF); + + if (accept (TokenType.OPEN_PARENS)) { + if (current () != TokenType.CLOSE_PARENS) { + do { + params.add (parse_identifier ()); + } while (accept (TokenType.COMMA)); + } + expect (TokenType.CLOSE_PARENS); + } else { + params.add (parse_identifier ()); + } + + + LambdaExpression lambda; + if (accept_block ()) { + 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)); + expect_terminator (); + + } + + + foreach (string 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 (current () == TokenType.DEF) { + var lambda = parse_lambda_expression (); + current_expr_is_lambda = true; + return lambda; + } + + 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)); + if (expr == null) { + // workaround for current limitation of exception handling + throw new ParseError.SYNTAX ("syntax error in assignment"); + } + } 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)); + if (expr == null) { + // workaround for current limitation of exception handling + throw new ParseError.SYNTAX ("syntax error in assignment"); + } + } else { + prev (); + break; + } + } else { + break; + } + } + + return expr; + } + + void parse_statements (Block block) throws ParseError { + while (current () != TokenType.DEDENT + && current () != TokenType.WHEN + && current () != TokenType.DEFAULT) { + try { + Statement stmt; + bool is_decl = false; + comment = scanner.pop_comment (); + switch (current ()) { + + /* skip over requires and ensures as we handled them in method declaration */ + case TokenType.REQUIRES: + case TokenType.ENSURES: + var begin = get_location (); + next (); + + if (accept (TokenType.EOL) && accept (TokenType.INDENT)) { + while (current () != TokenType.DEDENT) { + next(); + } + + expect (TokenType.DEDENT); + } else { + while (current () != TokenType.EOL) { + next(); + } + + expect (TokenType.EOL); + } + + stmt = new EmptyStatement (get_src_com (begin)); + break; + + + case TokenType.INDENT: + stmt = parse_block (); + break; + case TokenType.SEMICOLON: + case TokenType.PASS: + stmt = parse_empty_statement (); + break; + case TokenType.PRINT: + case TokenType.ASSERT: + stmt = parse_expression_statement (); + break; + case TokenType.IF: + stmt = parse_if_statement (); + break; + case TokenType.CASE: + stmt = parse_switch_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.RAISE: + stmt = parse_throw_statement (); + break; + case TokenType.TRY: + stmt = parse_try_statement (); + break; + case TokenType.LOCK: + stmt = parse_lock_statement (); + break; + case TokenType.DELETE: + stmt = parse_delete_statement (); + break; + case TokenType.VAR: + is_decl = true; + parse_local_variable_declarations (block); + break; + + + case TokenType.OP_INC: + case TokenType.OP_DEC: + case TokenType.SUPER: + case TokenType.THIS: + case TokenType.OPEN_PARENS: + 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; + parse_local_variable_declarations (block); + } + break; + } + + if (!is_decl) { + if (stmt == null) { + // workaround for current limitation of exception handling + throw new ParseError.SYNTAX ("syntax error in statement"); + } + block.add_statement (stmt); + } + } catch (ParseError 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 { + var begin = get_location (); + + // decide between declaration and expression statement + skip_type (); + 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: + rollback (begin); + return true; + } + + rollback (begin); + return false; + } + + Block parse_embedded_statement () throws ParseError { + if (current () == TokenType.INDENT) { + var block = parse_block (); + return block; + } + + comment = scanner.pop_comment (); + + var block = new Block (); + var stmt = parse_embedded_statement_without_block (); + if (stmt == null) { + // workaround for current limitation of exception handling + throw new ParseError.SYNTAX ("syntax error in embedded statement"); + } + block.add_statement (stmt); + return block; + + } + + Statement parse_embedded_statement_without_block () throws ParseError { + switch (current ()) { + case TokenType.PASS: + case TokenType.SEMICOLON: return parse_empty_statement (); + case TokenType.IF: return parse_if_statement (); + case TokenType.CASE: return parse_switch_statement (); + case TokenType.WHILE: return parse_while_statement (); + case TokenType.DO: return parse_do_statement (); + case TokenType.FOR: return parse_for_statement (); + case TokenType.FOREACH: return parse_foreach_statement (); + case TokenType.BREAK: return parse_break_statement (); + case TokenType.CONTINUE: return parse_continue_statement (); + case TokenType.RETURN: return parse_return_statement (); + case TokenType.RAISE: return parse_throw_statement (); + case TokenType.TRY: return parse_try_statement (); + case TokenType.LOCK: return parse_lock_statement (); + case TokenType.DELETE: return parse_delete_statement (); + default: return parse_expression_statement (); + } + } + + Block parse_block () throws ParseError { + var begin = get_location (); + Gee.List<Statement> list = new ArrayList<Statement> (); + expect (TokenType.INDENT); + var block = new Block (get_src_com (begin)); + parse_statements (block); + if (!accept (TokenType.DEDENT)) { + // only report error if it's not a secondary error + if (Report.get_errors () == 0) { + Report.error (get_current_src (), "tab indentation is incorrect"); + } + } + + return block; + } + + Statement parse_empty_statement () throws ParseError { + var begin = get_location (); + + accept (TokenType.PASS); + accept (TokenType.SEMICOLON); + expect_terminator (); + + return new EmptyStatement (get_src_com (begin)); + } + + void add_local_var_variable (Block block, string id) throws ParseError { + DataType type_copy = null; + var local = parse_local_variable (type_copy, id); + block.add_statement (new DeclarationStatement (local, local.source_reference)); + } + + void parse_local_variable_declarations (Block block) throws ParseError { + var begin = get_location (); + + if (accept (TokenType.VAR)) { + /* support block vars */ + if (accept (TokenType.EOL) && accept (TokenType.INDENT)) { + while (current () != TokenType.DEDENT) { + var s = parse_identifier (); + add_local_var_variable (block, s); + accept (TokenType.EOL); + accept (TokenType.SEMICOLON); + } + + expect (TokenType.DEDENT); + } else { + var s = parse_identifier (); + add_local_var_variable (block, s); + expect_terminator (); + } + + return; + } + + var id_list = new ArrayList<string> (); + DataType variable_type = null; + + do { + id_list.add (parse_identifier ()); + } while (accept (TokenType.COMMA)); + + expect (TokenType.COLON); + + variable_type = parse_type (); + + foreach (string id in id_list) { + DataType type_copy = null; + if (variable_type != null) { + type_copy = variable_type.copy (); + } + var local = parse_local_variable (type_copy, id); + block.add_statement (new DeclarationStatement (local, local.source_reference)); + } + + expect_terminator (); + } + + LocalVariable parse_local_variable (DataType? variable_type, string id) throws ParseError { + var begin = get_location (); + Expression initializer = null; + if (accept (TokenType.ASSIGN)) { + initializer = parse_variable_initializer (); + } + return new LocalVariable (variable_type, id, initializer, get_src_com (begin)); + } + + Statement parse_expression_statement () throws ParseError { + var begin = get_location (); + var expr = parse_statement_expression (); + + if (current_expr_is_lambda) { + current_expr_is_lambda = false; + } else { + expect_terminator (); + } + + return new ExpressionStatement (expr, get_src_com (begin)); + } + + 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); + + var condition = parse_expression (); + + if (!accept (TokenType.DO)) { + expect (TokenType.EOL); + } else { + accept (TokenType.EOL); + } + + var src = get_src_com (begin); + var true_stmt = parse_embedded_statement (); + Block false_stmt = null; + if (accept (TokenType.ELSE)) { + false_stmt = parse_embedded_statement (); + } + return new IfStatement (condition, true_stmt, false_stmt, src); + } + + Statement parse_switch_statement () throws ParseError { + var begin = get_location (); + expect (TokenType.CASE); + var condition = parse_expression (); + + expect (TokenType.EOL); + + var stmt = new SwitchStatement (condition, get_src_com (begin)); + expect (TokenType.INDENT); + while (current () != TokenType.DEDENT) { + var section = new SwitchSection (get_src_com (begin)); + + if (accept (TokenType.WHEN)) { + do { + section.add_label (new SwitchLabel (parse_expression (), get_src_com (begin))); + } + while (accept (TokenType.COMMA)); + } else { + expect (TokenType.DEFAULT); + section.add_label (new SwitchLabel.with_default (get_src_com (begin))); + } + + if (!accept (TokenType.EOL)) { + expect (TokenType.DO); + } + + parse_statements (section); + + /* add break statement for each block */ + var break_stmt = new BreakStatement (get_src_com (begin)); + section.add_statement (break_stmt); + + stmt.add_section (section); + } + expect (TokenType.DEDENT); + return stmt; + } + + Statement parse_while_statement () throws ParseError { + var begin = get_location (); + expect (TokenType.WHILE); + var condition = parse_expression (); + + if (!accept (TokenType.DO)) { + expect (TokenType.EOL); + } else { + accept (TokenType.EOL); + } + + var body = parse_embedded_statement (); + return new WhileStatement (condition, body, get_src_com (begin)); + } + + Statement parse_do_statement () throws ParseError { + var begin = get_location (); + expect (TokenType.DO); + expect (TokenType.EOL); + var body = parse_embedded_statement (); + expect (TokenType.WHILE); + + var condition = parse_expression (); + + expect_terminator (); + + return new DoStatement (body, condition, get_src_com (begin)); + } + + + Statement parse_for_statement () throws ParseError { + var begin = get_location (); + Block block = null; + Expression initializer = null; + Expression condition = null; + Expression iterator = null; + bool is_expr; + string id; + + expect (TokenType.FOR); + + switch (current ()) { + case TokenType.VAR: + is_expr = false; + break; + default: + + bool local_is_expr = is_expression (); + is_expr = local_is_expr; + break; + } + + if (is_expr) { + initializer = parse_statement_expression (); + } else { + block = new Block (get_src (begin)); + DataType variable_type; + if (accept (TokenType.VAR)) { + variable_type = null; + id = parse_identifier (); + } else { + id = parse_identifier (); + expect (TokenType.COLON); + variable_type = parse_type (); + } + + DataType type_copy = null; + if (variable_type != null) { + type_copy = variable_type.copy (); + } + var local = parse_local_variable (type_copy, id); + + block.add_statement (new DeclarationStatement (local, local.source_reference)); + } + + + + if (accept (TokenType.TO)) { + /* create expression for condition and incrementing iterator */ + var to_begin = get_location (); + var to_src = get_src (to_begin); + var left = new MemberAccess (null, id, to_src); + var right = parse_primary_expression (); + + condition = new BinaryExpression (BinaryOperator.LESS_THAN_OR_EQUAL, left, right, to_src); + + iterator = new PostfixExpression (left, true, to_src); + } else { + expect (TokenType.DOWNTO); + var downto_begin = get_location (); + var downto_src = get_src (downto_begin); + /* create expression for condition and decrementing iterator */ + var left = new MemberAccess (null, id, downto_src); + var right = parse_primary_expression (); + + condition = new BinaryExpression (BinaryOperator.GREATER_THAN_OR_EQUAL, left, right, downto_src); + + iterator = new PostfixExpression (left, false, downto_src); + } + + expect (TokenType.EOL); + + var src = get_src_com (begin); + var body = parse_embedded_statement (); + var stmt = new ForStatement (condition, body, src); + + if (initializer != null) stmt.add_initializer (initializer); + + stmt.add_iterator (iterator); + + + if (block != null) { + block.add_statement (stmt); + return block; + } else { + return stmt; + } + } + + Statement parse_foreach_statement () throws ParseError { + var begin = get_location (); + DataType type = null; + string id = null; + + expect (TokenType.FOREACH); + + if (accept (TokenType.VAR)) { + id = parse_identifier (); + } else { + id = parse_identifier (); + expect (TokenType.COLON); + type = parse_type (); + } + + expect (TokenType.IN); + var collection = parse_expression (); + expect (TokenType.EOL); + var src = get_src_com (begin); + var body = parse_embedded_statement (); + return new ForeachStatement (type, id, collection, body, src); + } + + Statement parse_break_statement () throws ParseError { + var begin = get_location (); + expect (TokenType.BREAK); + expect_terminator (); + return new BreakStatement (get_src_com (begin)); + } + + Statement parse_continue_statement () throws ParseError { + var begin = get_location (); + expect (TokenType.CONTINUE); + expect_terminator (); + return new ContinueStatement (get_src_com (begin)); + } + + Statement parse_return_statement () throws ParseError { + var begin = get_location (); + expect (TokenType.RETURN); + Expression expr = null; + if (current () != TokenType.SEMICOLON && current () != TokenType.EOL) { + expr = parse_expression (); + } + expect_terminator (); + return new ReturnStatement (expr, get_src_com (begin)); + } + + Statement parse_throw_statement () throws ParseError { + var begin = get_location (); + expect (TokenType.RAISE); + var expr = parse_expression (); + expect_terminator (); + return new ThrowStatement (expr, get_src_com (begin)); + } + + Statement parse_try_statement () throws ParseError { + var begin = get_location (); + expect (TokenType.TRY); + expect (TokenType.EOL); + var try_block = parse_block (); + Block finally_clause = null; + var catch_clauses = new ArrayList<CatchClause> (); + if (current () == TokenType.EXCEPT) { + parse_catch_clauses (catch_clauses); + if (current () == TokenType.FINALLY) { + finally_clause = parse_finally_clause (); + } + } else { + finally_clause = parse_finally_clause (); + } + var stmt = new TryStatement (try_block, finally_clause, get_src_com (begin)); + foreach (CatchClause clause in catch_clauses) { + stmt.add_catch_clause (clause); + } + return stmt; + } + + void parse_catch_clauses (Gee.List<CatchClause> catch_clauses) throws ParseError { + while (accept (TokenType.EXCEPT)) { + var begin = get_location (); + DataType type = null; + string id = null; + if (!accept (TokenType.EOL)) { + id = parse_identifier (); + expect (TokenType.COLON); + type = parse_type (); + expect (TokenType.EOL); + + } + var block = parse_block (); + catch_clauses.add (new CatchClause (type, id, block, get_src (begin))); + } + } + + Block parse_finally_clause () throws ParseError { + expect (TokenType.FINALLY); + accept_block (); + 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 stmt = parse_embedded_statement (); + return new LockStatement (expr, stmt, get_src_com (begin)); + } + + Statement parse_delete_statement () throws ParseError { + var begin = get_location (); + expect (TokenType.DELETE); + var expr = parse_expression (); + expect_terminator (); + return new DeleteStatement (expr, get_src_com (begin)); + } + + Gee.List<Attribute>? parse_attributes () throws ParseError { + if (current () != TokenType.OPEN_BRACKET) { + return null; + } + var attrs = new ArrayList<Attribute> (); + 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 { + begin = get_location (); + string id = parse_identifier (); + expect (TokenType.ASSIGN); + var expr = parse_expression (); + attr.add_argument (new NamedArgument (id, expr, get_src (begin))); + } 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, Gee.List<Attribute>? attributes) { + if (attributes != null) { + foreach (Attribute attr in (Gee.List<Attribute>) attributes) { + node.attributes.append (attr); + } + } + } + + Symbol parse_declaration () throws ParseError { + comment = scanner.pop_comment (); + var attrs = parse_attributes (); + + switch (current ()) { + case TokenType.CONST: + return parse_constant_declaration (attrs); + case TokenType.CONSTRUCT: + return parse_creation_method_declaration (attrs); + case TokenType.CLASS: + return parse_class_declaration (attrs); + case TokenType.INIT: + return parse_constructor_declaration (attrs); + case TokenType.DELEGATE: + return parse_delegate_declaration (attrs); + case TokenType.DEF: + return parse_method_declaration (attrs); + case TokenType.ENUM: + return parse_enum_declaration (attrs); + case TokenType.ERRORDOMAIN: + return parse_errordomain_declaration (attrs); + case TokenType.FINAL: + return parse_destructor_declaration (attrs); + case TokenType.INTERFACE: + return parse_interface_declaration (attrs); + case TokenType.NAMESPACE: + return parse_namespace_declaration (attrs); + case TokenType.PROP: + return parse_property_declaration (attrs); + case TokenType.EVENT: + return parse_signal_declaration (attrs); + case TokenType.STRUCT: + return parse_struct_declaration (attrs); + default: + var begin = get_location (); + while (current () != TokenType.EOL && current () != TokenType.SEMICOLON && current () != TokenType.EOF) { + if (current () == TokenType.COLON) { + rollback (begin); + return parse_field_declaration (attrs); + } else { + next (); + } + } + rollback (begin); + + break; + } + + TokenType cur = current (); + TokenType pre = tokens[index-1].type; + + throw new ParseError.SYNTAX (get_error ("expected declaration but got %s with previous %s".printf (cur.to_string (), pre.to_string()))); + } + + void parse_declarations (Symbol parent, bool root = false) throws ParseError { + if (!root) { + expect (TokenType.INDENT); + } + while (current () != TokenType.DEDENT && current () != TokenType.EOF) { + try { + if (parent is Namespace) { + parse_namespace_member ((Namespace) parent); + } else if (parent is Class) { + parse_class_member ((Class) parent); + } else if (parent is Struct) { + parse_struct_member ((Struct) parent); + } else if (parent is Interface) { + parse_interface_member ((Interface) parent); + } + } catch (ParseError e) { + int r; + while (true) { + r = recover (); + if (r == RecoveryState.STATEMENT_BEGIN) { + next (); + } else { + break; + } + } + if (r == RecoveryState.EOF) { + return; + } + } + } + if (!root) { + if (!accept (TokenType.DEDENT)) { + // only report error if it's not a secondary error + if (Report.get_errors () == 0) { + Report.error (get_current_src (), "expected dedent"); + } + } + } + } + + enum RecoveryState { + EOF, + DECLARATION_BEGIN, + STATEMENT_BEGIN + } + + RecoveryState recover () { + while (current () != TokenType.EOF) { + switch (current ()) { + case TokenType.CLASS: + case TokenType.CONST: + case TokenType.CONSTRUCT: + case TokenType.INIT: + case TokenType.DEF: + case TokenType.DELEGATE: + case TokenType.ENUM: + case TokenType.ERRORDOMAIN: + case TokenType.FINAL: + case TokenType.INTERFACE: + case TokenType.NAMESPACE: + case TokenType.PROP: + case TokenType.EVENT: + case TokenType.STRUCT: + return RecoveryState.DECLARATION_BEGIN; + case TokenType.BREAK: + case TokenType.CASE: + 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.RAISE: + case TokenType.TRY: + case TokenType.VAR: + case TokenType.WHILE: + return RecoveryState.STATEMENT_BEGIN; + default: + next (); + break; + } + } + return RecoveryState.EOF; + } + + Namespace parse_namespace_declaration (Gee.List<Attribute>? attrs) throws ParseError { + var begin = get_location (); + expect (TokenType.NAMESPACE); + var sym = parse_symbol_name (); + var ns = new Namespace (sym.name, get_src_com (begin)); + set_attributes (ns, attrs); + expect (TokenType.EOL); + parse_declarations (ns); + return ns; + } + + void parse_namespace_member (Namespace ns) throws ParseError { + var sym = parse_declaration (); + if (sym is Namespace) { + ns.add_namespace ((Namespace) sym); + } else if (sym is Class) { + ns.add_class ((Class) sym); + } else if (sym is Interface) { + ns.add_interface ((Interface) sym); + } else if (sym is Struct) { + ns.add_struct ((Struct) sym); + } else if (sym is Enum) { + ns.add_enum ((Enum) sym); + } else if (sym is ErrorDomain) { + ns.add_error_domain ((ErrorDomain) sym); + } else if (sym is Delegate) { + ns.add_delegate ((Delegate) sym); + } else if (sym is Method) { + var method = (Method) sym; + method.binding = MemberBinding.STATIC; + ns.add_method (method); + } else if (sym is Field) { + var field = (Field) sym; + field.binding = MemberBinding.STATIC; + ns.add_field (field); + } else if (sym is Constant) { + ns.add_constant ((Constant) sym); + } else if (sym == null) { + // workaround for current limitation of exception handling + throw new ParseError.SYNTAX ("syntax error in declaration"); + } else { + Report.error (sym.source_reference, "unexpected declaration in namespace"); + } + scanner.source_file.add_node (sym); + } + + + void add_uses_clause () throws ParseError { + var begin = get_location (); + var sym = parse_symbol_name (); + var ns_ref = new NamespaceReference (sym.name, get_src (begin)); + + scanner.source_file.add_using_directive (ns_ref); + } + + void parse_using_directives () throws ParseError { + while (accept (TokenType.USES)) { + var begin = get_location (); + + if (accept_block ()) { + expect (TokenType.INDENT); + + while (current () != TokenType.DEDENT && current () != TokenType.EOF) { + add_uses_clause (); + expect (TokenType.EOL); + } + + expect (TokenType.DEDENT); + } else { + do { + add_uses_clause (); + } while (accept (TokenType.COMMA)); + + expect_terminator (); + } + } + } + + Symbol parse_class_declaration (Gee.List<Attribute>? attrs) throws ParseError { + var begin = get_location (); + expect (TokenType.CLASS); + + var flags = parse_type_declaration_modifiers (); + + var sym = parse_symbol_name (); + var type_param_list = parse_type_parameter_list (); + var base_types = new ArrayList<DataType> (); + if (accept (TokenType.COLON)) { + do { + base_types.add (parse_type ()); + } while (accept (TokenType.COMMA)); + } + + accept (TokenType.EOL); + + var cl = new Class (sym.name, get_src_com (begin)); + + if (ModifierFlags.PRIVATE in flags) { + cl.access = SymbolAccessibility.PRIVATE; + } else { + /* class must always be Public unless its name starts wtih underscore */ + if (sym.name[0] == '_') { + cl.access = SymbolAccessibility.PRIVATE; + } else { + cl.access = SymbolAccessibility.PUBLIC; + } + } + + if (ModifierFlags.ABSTRACT in flags) { + cl.is_abstract = true; + } + if (ModifierFlags.STATIC in flags) { + cl.is_static = true; + } + 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); + } + + class_name = cl.name; + + parse_declarations (cl); + + // ensure there is always a default construction method + if (!scanner.source_file.external_package + && !cl.is_static + && cl.default_construction_method == null) { + var m = new CreationMethod (cl.name, null, cl.source_reference); + m.binding = MemberBinding.STATIC; + m.access = SymbolAccessibility.PUBLIC; + m.body = new Block (cl.source_reference); + cl.add_method (m); + } + + Symbol result = cl; + while (sym.inner != null) { + sym = sym.inner; + var ns = new Namespace (sym.name, cl.source_reference); + if (result is Namespace) { + ns.add_namespace ((Namespace) result); + } else { + ns.add_class ((Class) result); + scanner.source_file.add_node (result); + } + result = ns; + } + return result; + } + + void parse_class_member (Class cl) throws ParseError { + var sym = parse_declaration (); + if (sym is Class) { + cl.add_class ((Class) sym); + } else if (sym is Struct) { + cl.add_struct ((Struct) sym); + } else if (sym is Enum) { + cl.add_enum ((Enum) sym); + } else if (sym is Delegate) { + cl.add_delegate ((Delegate) sym); + } else if (sym is Method) { + cl.add_method ((Method) sym); + } else if (sym is Vala.Signal) { + cl.add_signal ((Vala.Signal) sym); + } else if (sym is Field) { + cl.add_field ((Field) sym); + } else if (sym is Constant) { + cl.add_constant ((Constant) sym); + } else if (sym is Property) { + cl.add_property ((Property) sym); + } else if (sym is Constructor) { + var c = (Constructor) sym; + if (c.binding == MemberBinding.INSTANCE) { + cl.constructor = c; + } else if (c.binding == MemberBinding.CLASS) { + cl.class_constructor = c; + } else { + cl.static_constructor = c; + } + } else if (sym is Destructor) { + cl.destructor = (Destructor) sym; + } else if (sym == null) { + // workaround for current limitation of exception handling + throw new ParseError.SYNTAX ("syntax error in declaration"); + } else { + Report.error (sym.source_reference, "unexpected declaration in class"); + } + } + + Constant parse_constant_declaration (Gee.List<Attribute>? attrs) throws ParseError { + var begin = get_location (); + + expect (TokenType.CONST); + + parse_member_declaration_modifiers (); + + string id = parse_identifier (); + expect (TokenType.COLON); + var type = parse_type (false); + + Expression initializer = null; + if (accept (TokenType.ASSIGN)) { + initializer = parse_variable_initializer (); + } + expect_terminator (); + + var c = new Constant (id, type, initializer, get_src_com (begin)); + c.access = get_access (id); + set_attributes (c, attrs); + return c; + } + + Field parse_field_declaration (Gee.List<Attribute>? attrs) throws ParseError { + var begin = get_location (); + string id = parse_identifier (); + expect (TokenType.COLON); + + var flags = parse_member_declaration_modifiers (); + + var type = parse_type (); + + var f = new Field (id, type, null, get_src_com (begin)); + + if (ModifierFlags.PRIVATE in flags) { + f.access = SymbolAccessibility.PRIVATE; + } else { + f.access = get_access (id); + } + + set_attributes (f, attrs); + + if (accept (TokenType.ASSIGN)) { + f.initializer = parse_expression (); + } + + + if (ModifierFlags.STATIC in flags) { + f.binding = MemberBinding.STATIC; + } else if (ModifierFlags.CLASS in flags) { + f.binding = MemberBinding.CLASS; + } + + expect_terminator (); + + return f; + } + + InitializerList parse_initializer () throws ParseError { + var begin = get_location (); + expect (TokenType.OPEN_BRACE); + var initializer = new InitializerList (get_src (begin)); + if (current () != TokenType.DEDENT) { + do { + initializer.append (parse_variable_initializer ()); + } while (accept (TokenType.COMMA)); + } + expect (TokenType.CLOSE_BRACE); + return initializer; + } + + Expression parse_variable_initializer () throws ParseError { + if (current () == TokenType.OPEN_BRACE) { + var expr = parse_initializer (); + return expr; + } else { + var expr = parse_expression (); + return expr; + } + } + + Method parse_method_declaration (Gee.List<Attribute>? attrs) throws ParseError { + var begin = get_location (); + DataType type = new VoidType (); + expect (TokenType.DEF); + var flags = parse_member_declaration_modifiers (); + + string id = parse_identifier (); + + var params = new ArrayList<FormalParameter> (); + expect (TokenType.OPEN_PARENS); + + if (current () != TokenType.CLOSE_PARENS) { + do { + var param = parse_parameter (); + params.add (param); + } while (accept (TokenType.COMMA)); + } + + expect (TokenType.CLOSE_PARENS); + + + /* deal with return value */ + if (accept (TokenType.COLON)) { + type = parse_type (); + parse_type_parameter_list (); + } + + + var method = new Method (id, type, get_src_com (begin)); + if (ModifierFlags.PRIVATE in flags) { + method.access = SymbolAccessibility.PRIVATE; + } else { + method.access = get_access (id); + } + + + set_attributes (method, attrs); + + foreach (FormalParameter param in params) { + method.add_parameter (param); + } + + if (accept (TokenType.RAISES)) { + do { + method.add_error_domain (parse_type ()); + } while (accept (TokenType.COMMA)); + } + + + if (ModifierFlags.STATIC in flags || id == "main") { + method.binding = MemberBinding.STATIC; + } + 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 (ModifierFlags.INLINE in flags) { + method.is_inline = true; + } + + expect (TokenType.EOL); + + var body_location = get_location (); + + + /* "requires" and "ensures" if present will be at start of the method body */ + if (accept (TokenType.INDENT)) { + if (accept (TokenType.REQUIRES)) { + + if (accept (TokenType.EOL) && accept (TokenType.INDENT)) { + while (current() != TokenType.DEDENT) { + method.add_precondition (parse_expression ()); + expect (TokenType.EOL); + } + + expect (TokenType.DEDENT); + accept_terminator (); + } else { + + method.add_precondition (parse_expression ()); + expect_terminator (); + + } + + } + + if (accept (TokenType.ENSURES)) { + if (accept (TokenType.EOL) && accept (TokenType.INDENT)) { + while (current() != TokenType.DEDENT) { + method.add_postcondition (parse_expression ()); + expect (TokenType.EOL); + } + + expect (TokenType.DEDENT); + accept_terminator (); + } else { + method.add_postcondition (parse_expression ()); + expect_terminator (); + } + } + } + + rollback (body_location); + + + if (accept_block ()) { + method.body = parse_block (); + } + return method; + } + + Property parse_property_declaration (Gee.List<Attribute>? attrs) throws ParseError { + var begin = get_location (); + var readonly = false; + + expect (TokenType.PROP); + + var flags = parse_member_declaration_modifiers (); + + readonly = accept (TokenType.READONLY); + + string id = parse_identifier (); + expect (TokenType.COLON); + + bool is_weak = accept (TokenType.WEAK); + var type = parse_type (false); + + var prop = new Property (id, type, null, null, get_src_com (begin)); + if (ModifierFlags.PRIVATE in flags) { + prop.access = SymbolAccessibility.PRIVATE; + } else { + prop.access = get_access (id); + } + + set_attributes (prop, attrs); + 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 (accept (TokenType.ASSIGN)) { + prop.default_expression = parse_expression (); + } + + + if (accept_block ()) { + expect (TokenType.INDENT); + while (current () != TokenType.DEDENT) { + var accessor_begin = get_location (); + parse_attributes (); + var accessor_access = SymbolAccessibility.PUBLIC; + if (accept (TokenType.GET)) { + if (prop.get_accessor != null) { + throw new ParseError.SYNTAX (get_error ("property get accessor already defined")); + } + Block block = null; + if (accept_block ()) { + block = parse_block (); + } + prop.get_accessor = new PropertyAccessor (true, false, false, block, get_src (accessor_begin)); + prop.get_accessor.access = SymbolAccessibility.PUBLIC; + } else { + bool _construct; + if (accept (TokenType.SET)) { + if (readonly) { + throw new ParseError.SYNTAX (get_error ("set block not allowed for a read only property")); + } + _construct = accept (TokenType.CONSTRUCT); + } else if (accept (TokenType.CONSTRUCT)) { + _construct = true; + } else if (!accept (TokenType.EOL)) { + throw new ParseError.SYNTAX (get_error ("expected get, set, or construct")); + } + + if (prop.set_accessor != null) { + throw new ParseError.SYNTAX (get_error ("property set accessor already defined")); + } + + Block block = null; + if (accept_block ()) { + block = parse_block (); + } + prop.set_accessor = new PropertyAccessor (false, !readonly, _construct, block, get_src (accessor_begin)); + prop.set_accessor.access = SymbolAccessibility.PUBLIC; + } + } + accept (TokenType.EOL); + expect (TokenType.DEDENT); + } else { + prop.get_accessor = new PropertyAccessor (true, false, false, null, get_src (begin)); + prop.get_accessor.access = SymbolAccessibility.PUBLIC; + + if (!readonly) { + prop.set_accessor = new PropertyAccessor (false, true, false, null, get_src (begin)); + prop.set_accessor.access = SymbolAccessibility.PUBLIC; + + } + + expect_terminator (); + } + + if (!prop.is_abstract && !scanner.source_file.external_package) { + var needs_var = (readonly && (prop.get_accessor != null && prop.get_accessor.body == null)); + + if (!needs_var) { + needs_var = (prop.get_accessor != null && prop.get_accessor.body == null) || (prop.set_accessor != null && prop.set_accessor.body == null); + } + + if (needs_var) { + /* automatic property accessor body generation */ + var field_type = prop.property_type.copy (); + field_type.value_owned = !is_weak; + prop.field = new Field ("_%s".printf (prop.name), field_type, prop.default_expression, prop.source_reference); + prop.field.access = SymbolAccessibility.PRIVATE; + } + } + + return prop; + } + + Vala.Signal parse_signal_declaration (Gee.List<Attribute>? attrs) throws ParseError { + var begin = get_location (); + DataType type; + + expect (TokenType.EVENT); + var flags = parse_member_declaration_modifiers (); + string id = parse_identifier (); + + + var params = new ArrayList<FormalParameter> (); + + expect (TokenType.OPEN_PARENS); + if (current () != TokenType.CLOSE_PARENS) { + do { + var param = parse_parameter (); + params.add (param); + } while (accept (TokenType.COMMA)); + } + expect (TokenType.CLOSE_PARENS); + + if (accept (TokenType.COLON)) { + type = parse_type (); + } else { + type = new VoidType (); + } + + var sig = new Vala.Signal (id, type, get_src_com (begin)); + if (ModifierFlags.PRIVATE in flags) { + sig.access = SymbolAccessibility.PRIVATE; + } else { + sig.access = get_access (id); + } + + set_attributes (sig, attrs); + + foreach (FormalParameter formal_param in params) { + sig.add_parameter (formal_param); + } + + expect_terminator (); + return sig; + } + + Constructor parse_constructor_declaration (Gee.List<Attribute>? attrs) throws ParseError { + var begin = get_location (); + + expect (TokenType.INIT); + var flags = parse_member_declaration_modifiers (); + + var c = new Constructor (get_src_com (begin)); + if (ModifierFlags.STATIC in flags) { + c.binding = MemberBinding.STATIC; + } else if (ModifierFlags.CLASS in flags) { + c.binding = MemberBinding.CLASS; + } + + accept_block (); + c.body = parse_block (); + return c; + } + + Destructor parse_destructor_declaration (Gee.List<Attribute>? attrs) throws ParseError { + var begin = get_location (); + expect (TokenType.FINAL); + var d = new Destructor (get_src_com (begin)); + accept_block (); + d.body = parse_block (); + return d; + } + + Symbol parse_struct_declaration (Gee.List<Attribute>? attrs) throws ParseError { + var begin = get_location (); + + expect (TokenType.STRUCT); + var flags = parse_type_declaration_modifiers (); + var sym = parse_symbol_name (); + var type_param_list = parse_type_parameter_list (); + var base_types = new ArrayList<DataType> (); + if (accept (TokenType.COLON)) { + do { + base_types.add (parse_type ()); + } while (accept (TokenType.COMMA)); + } + var st = new Struct (sym.name, get_src_com (begin)); + if (ModifierFlags.PRIVATE in flags) { + st.access = SymbolAccessibility.PRIVATE; + } else { + st.access = get_access (sym.name); + } + set_attributes (st, attrs); + foreach (TypeParameter type_param in type_param_list) { + st.add_type_parameter (type_param); + } + foreach (DataType base_type in base_types) { + st.add_base_type (base_type); + } + + expect (TokenType.EOL); + + parse_declarations (st); + + Symbol result = st; + while (sym.inner != null) { + sym = sym.inner; + var ns = new Namespace (sym.name, st.source_reference); + if (result is Namespace) { + ns.add_namespace ((Namespace) result); + } else { + ns.add_struct ((Struct) result); + scanner.source_file.add_node (result); + } + result = ns; + } + return result; + } + + void parse_struct_member (Struct st) throws ParseError { + var sym = parse_declaration (); + if (sym is Method) { + st.add_method ((Method) sym); + } else if (sym is Field) { + st.add_field ((Field) sym); + } else if (sym is Constant) { + st.add_constant ((Constant) sym); + } else if (sym == null) { + // workaround for current limitation of exception handling + throw new ParseError.SYNTAX ("syntax error in declaration"); + } else { + Report.error (sym.source_reference, "unexpected declaration in struct"); + } + } + + Symbol parse_interface_declaration (Gee.List<Attribute>? attrs) throws ParseError { + var begin = get_location (); + + expect (TokenType.INTERFACE); + var flags = parse_type_declaration_modifiers (); + var sym = parse_symbol_name (); + var type_param_list = parse_type_parameter_list (); + var base_types = new ArrayList<DataType> (); + if (accept (TokenType.COLON)) { + do { + base_types.add (parse_type ()); + } while (accept (TokenType.COMMA)); + } + var iface = new Interface (sym.name, get_src_com (begin)); + if (ModifierFlags.PRIVATE in flags) { + iface.access = SymbolAccessibility.PRIVATE; + } else { + iface.access = get_access (sym.name); + } + + 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); + } + + + expect (TokenType.EOL); + + parse_declarations (iface); + + + Symbol result = iface; + while (sym.inner != null) { + sym = sym.inner; + var ns = new Namespace (sym.name, iface.source_reference); + if (result is Namespace) { + ns.add_namespace ((Namespace) result); + } else { + ns.add_interface ((Interface) result); + scanner.source_file.add_node (result); + } + result = ns; + } + return result; + } + + void parse_interface_member (Interface iface) throws ParseError { + var sym = parse_declaration (); + if (sym is Class) { + iface.add_class ((Class) sym); + } else if (sym is Struct) { + iface.add_struct ((Struct) sym); + } else if (sym is Enum) { + iface.add_enum ((Enum) sym); + } else if (sym is Delegate) { + iface.add_delegate ((Delegate) sym); + } else if (sym is Method) { + iface.add_method ((Method) sym); + } else if (sym is Vala.Signal) { + iface.add_signal ((Vala.Signal) sym); + } else if (sym is Field) { + iface.add_field ((Field) sym); + } else if (sym is Property) { + iface.add_property ((Property) sym); + } else if (sym == null) { + // workaround for current limitation of exception handling + throw new ParseError.SYNTAX ("syntax error in declaration"); + } else { + Report.error (sym.source_reference, "unexpected declaration in interface"); + } + } + + Symbol parse_enum_declaration (Gee.List<Attribute>? attrs) throws ParseError { + var begin = get_location (); + expect (TokenType.ENUM); + var flags = parse_type_declaration_modifiers (); + + var sym = parse_symbol_name (); + var en = new Enum (sym.name, get_src_com (begin)); + if (ModifierFlags.PRIVATE in flags) { + en.access = SymbolAccessibility.PRIVATE; + } else { + en.access = get_access (sym.name); + } + set_attributes (en, attrs); + + expect (TokenType.EOL); + expect (TokenType.INDENT); + do { + if (current () == TokenType.DEDENT) { + // allow trailing comma + break; + } + var value_attrs = parse_attributes (); + var value_begin = get_location (); + string id = parse_identifier (); + + var ev = new EnumValue (id, get_src (value_begin)); + set_attributes (ev, value_attrs); + + if (accept (TokenType.ASSIGN)) { + ev.value = parse_expression (); + } + en.add_value (ev); + expect (TokenType.EOL); + } while (true); + + expect (TokenType.DEDENT); + + Symbol result = en; + while (sym.inner != null) { + sym = sym.inner; + var ns = new Namespace (sym.name, en.source_reference); + if (result is Namespace) { + ns.add_namespace ((Namespace) result); + } else { + ns.add_enum ((Enum) result); + scanner.source_file.add_node (result); + } + result = ns; + } + return result; + } + + Symbol parse_errordomain_declaration (Gee.List<Attribute>? attrs) throws ParseError { + var begin = get_location (); + expect (TokenType.ERRORDOMAIN); + var flags = parse_type_declaration_modifiers (); + + var sym = parse_symbol_name (); + var ed = new ErrorDomain (sym.name, get_src_com (begin)); + if (ModifierFlags.PRIVATE in flags) { + ed.access = SymbolAccessibility.PRIVATE; + } else { + ed.access = get_access (sym.name); + } + + set_attributes (ed, attrs); + + expect (TokenType.EOL); + expect (TokenType.INDENT); + + do { + if (current () == TokenType.DEDENT) { + // allow trailing comma + break; + } + var code_attrs = parse_attributes (); + string id = parse_identifier (); + + var ec = new ErrorCode (id); + set_attributes (ec, code_attrs); + if (accept (TokenType.ASSIGN)) { + ec.value = parse_expression (); + } + ed.add_code (ec); + accept (TokenType.EOL); + } while (true); + + + expect (TokenType.DEDENT); + + Symbol result = ed; + while (sym.inner != null) { + sym = sym.inner; + var ns = new Namespace (sym.name, ed.source_reference); + + if (result is Namespace) { + ns.add_namespace ((Namespace) result); + } else { + ns.add_error_domain ((ErrorDomain) result); + scanner.source_file.add_node (result); + } + result = ns; + } + return result; + } + + 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.STATIC: + next (); + flags |= ModifierFlags.STATIC; + break; + + case TokenType.PRIVATE: + next (); + flags |= ModifierFlags.PRIVATE; + break; + + default: + return flags; + } + } + return flags; + } + + ModifierFlags parse_member_declaration_modifiers () { + ModifierFlags flags = 0; + while (true) { + switch (current ()) { + case TokenType.ABSTRACT: + next (); + flags |= ModifierFlags.ABSTRACT; + 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.OVERRIDE: + next (); + flags |= ModifierFlags.OVERRIDE; + break; + case TokenType.STATIC: + next (); + flags |= ModifierFlags.STATIC; + break; + case TokenType.VIRTUAL: + next (); + flags |= ModifierFlags.VIRTUAL; + break; + case TokenType.PRIVATE: + next (); + flags |= ModifierFlags.PRIVATE; + break; + default: + return flags; + } + } + return flags; + } + + FormalParameter parse_parameter () throws ParseError { + var attrs = parse_attributes (); + var begin = get_location (); + if (accept (TokenType.ELLIPSIS)) { + // varargs + return new FormalParameter.with_ellipsis (get_src (begin)); + } + + var direction = ParameterDirection.IN; + if (accept (TokenType.OUT)) { + direction = ParameterDirection.OUT; + } else if (accept (TokenType.REF)) { + direction = ParameterDirection.REF; + } + + string id = parse_identifier (); + + expect (TokenType.COLON); + + DataType type; + if (direction == ParameterDirection.IN) { + type = parse_type (false); + } else { + type = parse_type (true); + } + + var param = new FormalParameter (id, type, get_src (begin)); + set_attributes (param, attrs); + param.direction = direction; + param.construct_parameter = false; + if (accept (TokenType.ASSIGN)) { + param.default_expression = parse_expression (); + } + return param; + } + + CreationMethod parse_creation_method_declaration (Gee.List<Attribute>? attrs) throws ParseError { + var begin = get_location (); + CreationMethod method; + + expect (TokenType.CONSTRUCT); + + var flags = parse_member_declaration_modifiers (); + + + if (accept (TokenType.OPEN_PARENS)) { + /* create default name using class name */ + method = new CreationMethod (class_name, null, get_src_com (begin)); + } else { + var sym = parse_symbol_name (); + if (sym.inner == null) { + + if (sym.name != class_name) { + method = new CreationMethod (class_name, sym.name, get_src_com (begin)); + } else { + method = new CreationMethod (sym.name, null, get_src_com (begin)); + } + } else { + method = new CreationMethod (sym.inner.name, sym.name, get_src_com (begin)); + } + 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.RAISES)) { + do { + method.add_error_domain (parse_type ()); + } while (accept (TokenType.COMMA)); + } + method.access = SymbolAccessibility.PUBLIC; + set_attributes (method, attrs); + method.binding = MemberBinding.STATIC; + + if (accept_block ()) { + method.body = parse_block (); + } + + return method; + } + + Symbol parse_delegate_declaration (Gee.List<Attribute>? attrs) throws ParseError { + var begin = get_location (); + DataType type; + + expect (TokenType.DELEGATE); + + var flags = parse_member_declaration_modifiers (); + + var sym = parse_symbol_name (); + + var type_param_list = parse_type_parameter_list (); + + + var params = new ArrayList<FormalParameter> (); + + expect (TokenType.OPEN_PARENS); + if (current () != TokenType.CLOSE_PARENS) { + do { + var param = parse_parameter (); + params.add (param); + } while (accept (TokenType.COMMA)); + } + expect (TokenType.CLOSE_PARENS); + + if (accept (TokenType.COLON)) { + type = parse_type (); + + } else { + type = new VoidType (); + } + + if (accept (TokenType.RAISES)) { + do { + parse_type (); + } while (accept (TokenType.COMMA)); + } + + expect_terminator (); + + var d = new Delegate (sym.name, type, get_src_com (begin)); + if (ModifierFlags.PRIVATE in flags) { + d.access = SymbolAccessibility.PRIVATE; + } else { + d.access = get_access (sym.name); + } + + set_attributes (d, attrs); + + foreach (TypeParameter type_param in type_param_list) { + d.add_type_parameter (type_param); + } + + foreach (FormalParameter formal_param in params) { + d.add_parameter (formal_param); + } + + if (!(ModifierFlags.STATIC in flags)) { + d.has_target = true; + } + + + Symbol result = d; + while (sym.inner != null) { + sym = sym.inner; + var ns = new Namespace (sym.name, d.source_reference); + + if (result is Namespace) { + ns.add_namespace ((Namespace) result); + } else { + ns.add_delegate ((Delegate) result); + scanner.source_file.add_node (result); + } + result = ns; + } + return result; + } + + Gee.List<TypeParameter> parse_type_parameter_list () throws ParseError { + var list = new ArrayList<TypeParameter> (); + if (accept (TokenType.OF)) { + do { + var begin = get_location (); + string id = parse_identifier (); + list.add (new TypeParameter (id, get_src (begin))); + } while (accept (TokenType.COMMA)); + + } + return list; + } + + void skip_type_argument_list () throws ParseError { + if (accept (TokenType.OF)) { + do { + skip_type (); + } while (accept (TokenType.COMMA)); + } + } + + // try to parse type argument list + Gee.List<DataType>? parse_type_argument_list (bool maybe_expression) throws ParseError { + var begin = get_location (); + if (accept (TokenType.OF)) { + var list = new ArrayList<DataType> (); + do { + switch (current ()) { + case TokenType.VOID: + case TokenType.DYNAMIC: + case TokenType.WEAK: + case TokenType.IDENTIFIER: + var type = parse_type (); + + list.add (type); + break; + default: + rollback (begin); + return null; + } + } while (accept (TokenType.COMMA)); + + return list; + } + return null; + } + + MemberAccess parse_member_name () throws ParseError { + var begin = get_location (); + MemberAccess expr = null; + do { + string id = parse_identifier (); + Gee.List<DataType> type_arg_list = parse_type_argument_list (false); + expr = new MemberAccess (expr, id, get_src (begin)); + if (type_arg_list != null) { + foreach (DataType type_arg in type_arg_list) { + expr.add_type_argument (type_arg); + } + } + } while (accept (TokenType.DOT)); + return expr; + } + + bool is_declaration_keyword (TokenType type) { + switch (type) { + case TokenType.CLASS: + case TokenType.CONST: + case TokenType.DEF: + case TokenType.DELEGATE: + case TokenType.ENUM: + case TokenType.ERRORDOMAIN: + case TokenType.EVENT: + case TokenType.FINAL: + case TokenType.INIT: + case TokenType.INTERFACE: + case TokenType.NAMESPACE: + case TokenType.OVERRIDE: + case TokenType.PROP: + case TokenType.STRUCT: + return true; + default: + return false; + } + } +} + diff --git a/vala/valageniescanner.vala b/vala/valageniescanner.vala new file mode 100644 index 000000000..080db740b --- /dev/null +++ b/vala/valageniescanner.vala @@ -0,0 +1,1017 @@ +/* valageniescanner.vala + * + * Copyright (C) 2008 Jamie McCracken, Jürg Billeter + * Based on code by 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: + * Jamie McCracken jamiemcc gnome org + */ + +using GLib; +using Gee; + +/** + * Lexical scanner for Genie source files. + */ +public class Vala.Genie.Scanner : Object { + public SourceFile source_file { get; construct; } + + char* begin; + char* current; + char* end; + + int line; + int column; + + int current_indent_level; + int indent_level; + int pending_dedents; + + TokenType last_token; + bool parse_started; + + string _comment; + + public Scanner (SourceFile source_file) { + this.source_file = source_file; + } + + construct { + begin = source_file.get_mapped_contents (); + end = begin + source_file.get_mapped_length (); + + current = begin; + + line = 1; + column = 1; + current_indent_level = 0; + indent_level = 0; + pending_dedents = 0; + + parse_started = false; + last_token = TokenType.NONE; + + } + + bool is_ident_char (char c) { + return (c.isalnum () || c == '_'); + } + + TokenType get_identifier_or_keyword (char* begin, int len) { + switch (len) { + case 2: + switch (begin[0]) { + case 'a': + if (matches (begin, "as")) return TokenType.AS; + break; + case 'd': + if (matches (begin, "do")) return TokenType.DO; + break; + case 'i': + switch (begin[1]) { + case 'f': + return TokenType.IF; + case 'n': + return TokenType.IN; + case 's': + return TokenType.IS; + } + break; + case 'o': + if (matches (begin, "of")) return TokenType.OF; + + if (matches (begin, "or")) return TokenType.OP_OR; + break; + case 't': + if (matches (begin, "to")) return TokenType.TO; + break; + } + break; + case 3: + switch (begin[0]) { + case 'a': + if (matches (begin, "and")) return TokenType.OP_AND; + break; + case 'd': + if (matches (begin, "def")) return TokenType.DEF; + break; + case 'f': + if (matches (begin, "for")) return TokenType.FOR; + break; + case 'g': + if (matches (begin, "get")) return TokenType.GET; + break; + case 'i': + if (matches (begin, "isa")) return TokenType.ISA; + break; + case 'n': + switch (begin[1]) { + case 'e': + if (matches (begin, "new")) return TokenType.NEW; + break; + case 'o': + if (matches (begin, "not")) return TokenType.OP_NEG; + break; + } + break; + case 'o': + if (matches (begin, "out")) return TokenType.OUT; + break; + case 'r': + if (matches (begin, "ref")) return TokenType.REF; + break; + case 's': + if (matches (begin, "set")) return TokenType.SET; + break; + case 't': + if (matches (begin, "try")) return TokenType.TRY; + break; + case 'v': + if (matches (begin, "var")) return TokenType.VAR; + break; + } + break; + case 4: + switch (begin[0]) { + case 'c': + if (matches (begin, "case")) return TokenType.CASE; + break; + case 'e': + switch (begin[1]) { + case 'l': + if (matches (begin, "else")) return TokenType.ELSE; + break; + case 'n': + if (matches (begin, "enum")) return TokenType.ENUM; + break; + } + break; + case 'i': + if (matches (begin, "init")) return TokenType.INIT; + break; + case 'l': + if (matches (begin, "lock")) return TokenType.LOCK; + break; + case 'n': + if (matches (begin, "null")) return TokenType.NULL; + break; + case 'p': + switch (begin[1]) { + case 'a': + if (matches (begin, "pass")) return TokenType.PASS; + break; + case 'r': + if (matches (begin, "prop")) return TokenType.PROP; + break; + } + break; + case 's': + if (matches (begin, "self")) return TokenType.THIS; + break; + case 't': + if (matches (begin, "true")) return TokenType.TRUE; + break; + case 'u': + if (matches (begin, "uses")) return TokenType.USES; + break; + case 'v': + if (matches (begin, "void")) return TokenType.VOID; + break; + case 'w': + switch (begin[1]) { + case 'e': + if (matches (begin, "weak")) return TokenType.WEAK; + break; + case 'h': + if (matches (begin, "when")) return TokenType.WHEN; + break; + } + break; + } + break; + case 5: + switch (begin[0]) { + case 'b': + if (matches (begin, "break")) return TokenType.BREAK; + break; + case 'c': + switch (begin[1]) { + case 'l': + if (matches (begin, "class")) return TokenType.CLASS; + break; + case 'o': + if (matches (begin, "const")) return TokenType.CONST; + break; + } + break; + case 'e': + if (matches (begin, "event")) return TokenType.EVENT; + break; + case 'f': + switch (begin[1]) { + case 'a': + if (matches (begin, "false")) return TokenType.FALSE; + break; + case 'i': + if (matches (begin, "final")) return TokenType.FINAL; + break; + } + break; + case 'p': + if (matches (begin, "print")) return TokenType.PRINT; + break; + case 's': + if (matches (begin, "super")) return TokenType.SUPER; + break; + case 'r': + if (matches (begin, "raise")) return TokenType.RAISE; + break; + case 'w': + if (matches (begin, "while")) return TokenType.WHILE; + break; + } + break; + case 6: + switch (begin[0]) { + case 'a': + if (matches (begin, "assert")) return TokenType.ASSERT; + break; + case 'd': + switch (begin[1]) { + case 'e': + if (matches (begin, "delete")) return TokenType.DELETE; + break; + case 'o': + if (matches (begin, "downto")) return TokenType.DOWNTO; + break; + } + break; + case 'e': + switch (begin[1]) { + case 'x': + switch (begin[2]) { + case 'c': + if (matches (begin, "except")) return TokenType.EXCEPT; + break; + case 't': + if (matches (begin, "extern")) return TokenType.EXTERN; + break; + } + break; + } + break; + case 'i': + if (matches (begin, "inline")) return TokenType.INLINE; + break; + case 'p': + if (matches (begin, "public")) return TokenType.PUBLIC; + break; + case 'r': + switch (begin[1]) { + case 'a': + if (matches (begin, "raises")) return TokenType.RAISES; + break; + case 'e': + if (matches (begin, "return")) return TokenType.RETURN; + break; + } + break; + case 's': + switch (begin[1]) { + case 'i': + if (matches (begin, "sizeof")) return TokenType.SIZEOF; + break; + case 't': + switch (begin[2]) { + case 'a': + if (matches (begin, "static")) return TokenType.STATIC; + break; + case 'r': + if (matches (begin, "struct")) return TokenType.STRUCT; + break; + } + break; + } + break; + case 't': + if (matches (begin, "typeof")) return TokenType.TYPEOF; + break; + } + break; + case 7: + switch (begin[0]) { + case 'd': + switch (begin[1]) { + case 'e': + if (matches (begin, "default")) return TokenType.DEFAULT; + break; + case 'y': + if (matches (begin, "dynamic")) return TokenType.DYNAMIC; + break; + } + break; + case 'e': + if (matches (begin, "ensures")) return TokenType.ENSURES; + break; + case 'f': + switch (begin[1]) { + case 'i': + if (matches (begin, "finally")) return TokenType.FINALLY; + break; + case 'o': + if (matches (begin, "foreach")) return TokenType.FOREACH; + break; + } + break; + case 'p': + if (matches (begin, "private")) return TokenType.PRIVATE; + break; + case 'v': + if (matches (begin, "virtual")) return TokenType.VIRTUAL; + break; + } + break; + case 8: + switch (begin[0]) { + case 'a': + if (matches (begin, "abstract")) return TokenType.ABSTRACT; + break; + case 'c': + if (matches (begin, "continue")) return TokenType.CONTINUE; + break; + case 'd': + if (matches (begin, "delegate")) return TokenType.DELEGATE; + break; + case 'o': + if (matches (begin, "override")) return TokenType.OVERRIDE; + break; + case 'r': + switch (begin[2]) { + case 'a': + if (matches (begin, "readonly")) return TokenType.READONLY; + break; + case 'q': + if (matches (begin, "requires")) return TokenType.REQUIRES; + break; + } + break; + case 'v': + if (matches (begin, "volatile")) return TokenType.VOLATILE; + break; + } + break; + case 9: + switch (begin[0]) { + case 'c': + if (matches (begin, "construct")) return TokenType.CONSTRUCT; + break; + case 'e': + if (matches (begin, "exception")) return TokenType.ERRORDOMAIN; + break; + case 'i': + if (matches (begin, "interface")) return TokenType.INTERFACE; + break; + case 'n': + if (matches (begin, "namespace")) return TokenType.NAMESPACE; + break; + case 'p': + if (matches (begin, "protected")) return TokenType.PROTECTED; + break; + case 'w': + if (matches (begin, "writeonly")) return TokenType.WRITEONLY; + break; + } + break; + + } + return TokenType.IDENTIFIER; + } + + public TokenType read_token (out SourceLocation token_begin, out SourceLocation token_end) { + /* emit dedents if outstanding before checking any other chars */ + + if (pending_dedents > 0) { + pending_dedents--; + indent_level--; + + + token_begin.pos = current; + token_begin.line = line; + token_begin.column = column; + + token_end.pos = current; + token_end.line = line; + token_end.column = column; + + last_token = TokenType.DEDENT; + + return TokenType.DEDENT; + } + + + /* scrub whitespace (excluding newlines) and comments */ + space (); + + /* handle line continuation */ + while (current < end && current[0] == '\\' && current[1] == '\n') { + current += 2; + } + + /* handle non-consecutive new line once parsing is underway - EOL */ + if (newline () && parse_started && last_token != TokenType.EOL && last_token != TokenType.SEMICOLON) { + token_begin.pos = current; + token_begin.line = line; + token_begin.column = column; + + token_end.pos = current; + token_end.line = line; + token_end.column = column; + + last_token = TokenType.EOL; + + return TokenType.EOL; + } + + + while (skip_newlines ()) { + token_begin.pos = current; + token_begin.line = line; + token_begin.column = column; + + current_indent_level = count_tabs (); + + /* if its an empty new line then ignore */ + if (current_indent_level == -1) { + continue; + } + + if (current_indent_level > indent_level) { + indent_level = current_indent_level; + + token_end.pos = current; + token_end.line = line; + token_end.column = column; + + last_token = TokenType.INDENT; + + return TokenType.INDENT; + } else if (current_indent_level < indent_level) { + indent_level--; + + pending_dedents = (indent_level - current_indent_level); + + token_end.pos = current; + token_end.line = line; + token_end.column = column; + + last_token = TokenType.DEDENT; + + return TokenType.DEDENT; + } + } + + TokenType type; + char* begin = current; + token_begin.pos = begin; + token_begin.line = line; + token_begin.column = column; + + int token_length_in_chars = -1; + + parse_started = true; + + if (current >= end) { + if (indent_level > 0) { + indent_level--; + + pending_dedents = indent_level; + + type = TokenType.DEDENT; + } else { + type = TokenType.EOF; + } + } else if (current[0].isalpha () || current[0] == '_') { + int len = 0; + while (current < end && is_ident_char (current[0])) { + current++; + len++; + } + type = get_identifier_or_keyword (begin, len); + } else if (current[0] == '@') { + int len = 0; + if (current[1] == '@') { + token_begin.pos += 2; // @@ is not part of the identifier + current += 2; + } else { + current++; + len = 1; + } + while (current < end && is_ident_char (current[0])) { + current++; + len++; + } + type = TokenType.IDENTIFIER; + } else if (current[0].isdigit ()) { + while (current < end && current[0].isdigit ()) { + current++; + } + type = TokenType.INTEGER_LITERAL; + if (current < end && current[0].tolower () == 'l') { + current++; + if (current < end && current[0].tolower () == 'l') { + current++; + } + } else if (current < end && current[0].tolower () == 'u') { + current++; + if (current < end && current[0].tolower () == 'l') { + current++; + if (current < end && current[0].tolower () == 'l') { + current++; + } + } + } else if (current < end && current[0] == '.') { + current++; + while (current < end && current[0].isdigit ()) { + current++; + } + if (current < end && current[0].tolower () == 'e') { + current++; + if (current < end && (current[0] == '+' || current[0] == '-')) { + current++; + } + while (current < end && current[0].isdigit ()) { + current++; + } + } + if (current < end && current[0].tolower () == 'f') { + current++; + } + type = TokenType.REAL_LITERAL; + } else if (current < end && current == begin + 1 + && begin[0] == '0' && begin[1] == 'x' && begin[2].isxdigit ()) { + // hexadecimal integer literal + current++; + while (current < end && current[0].isxdigit ()) { + current++; + } + } else if (current < end && is_ident_char (current[0])) { + // allow identifiers to start with a digit + // as long as they contain at least one char + while (current < end && is_ident_char (current[0])) { + current++; + } + type = TokenType.IDENTIFIER; + } + } else { + switch (current[0]) { + case '{': + type = TokenType.OPEN_BRACE; + current++; + break; + case '}': + type = TokenType.CLOSE_BRACE; + current++; + break; + case '(': + type = TokenType.OPEN_PARENS; + current++; + break; + case ')': + type = TokenType.CLOSE_PARENS; + current++; + break; + case '[': + type = TokenType.OPEN_BRACKET; + current++; + break; + case ']': + type = TokenType.CLOSE_BRACKET; + current++; + break; + case '.': + type = TokenType.DOT; + current++; + if (current < end - 1) { + if (current[0] == '.' && current[1] == '.') { + type = TokenType.ELLIPSIS; + current += 2; + } + } + break; + case ':': + type = TokenType.COLON; + current++; + break; + case ',': + type = TokenType.COMMA; + current++; + break; + case ';': + type = TokenType.SEMICOLON; + current++; + break; + case '#': + type = TokenType.HASH; + current++; + break; + case '?': + type = TokenType.INTERR; + current++; + break; + case '|': + type = TokenType.BITWISE_OR; + current++; + if (current < end) { + switch (current[0]) { + case '=': + type = TokenType.ASSIGN_BITWISE_OR; + current++; + break; + case '|': + type = TokenType.OP_OR; + current++; + break; + } + } + break; + case '&': + type = TokenType.BITWISE_AND; + current++; + if (current < end) { + switch (current[0]) { + case '=': + type = TokenType.ASSIGN_BITWISE_AND; + current++; + break; + case '&': + type = TokenType.OP_AND; + current++; + break; + } + } + break; + case '^': + type = TokenType.CARRET; + current++; + if (current < end && current[0] == '=') { + type = TokenType.ASSIGN_BITWISE_XOR; + current++; + } + break; + case '~': + type = TokenType.TILDE; + current++; + break; + case '=': + type = TokenType.ASSIGN; + current++; + if (current < end) { + switch (current[0]) { + case '=': + type = TokenType.OP_EQ; + current++; + break; + case '>': + type = TokenType.LAMBDA; + current++; + break; + } + } + break; + case '<': + type = TokenType.OP_LT; + current++; + if (current < end) { + switch (current[0]) { + case '=': + type = TokenType.OP_LE; + current++; + break; + case '<': + type = TokenType.OP_SHIFT_LEFT; + current++; + if (current < end && current[0] == '=') { + type = TokenType.ASSIGN_SHIFT_LEFT; + current++; + } + break; + } + } + break; + case '>': + type = TokenType.OP_GT; + current++; + if (current < end && current[0] == '=') { + type = TokenType.OP_GE; + current++; + } + break; + case '!': + type = TokenType.OP_NEG; + current++; + if (current < end && current[0] == '=') { + type = TokenType.OP_NE; + current++; + } + break; + case '+': + type = TokenType.PLUS; + current++; + if (current < end) { + switch (current[0]) { + case '=': + type = TokenType.ASSIGN_ADD; + current++; + break; + case '+': + type = TokenType.OP_INC; + current++; + break; + } + } + break; + case '-': + type = TokenType.MINUS; + current++; + if (current < end) { + switch (current[0]) { + case '=': + type = TokenType.ASSIGN_SUB; + current++; + break; + case '-': + type = TokenType.OP_DEC; + current++; + break; + case '>': + type = TokenType.OP_PTR; + current++; + break; + } + } + break; + case '*': + type = TokenType.STAR; + current++; + if (current < end && current[0] == '=') { + type = TokenType.ASSIGN_MUL; + current++; + } + break; + case '/': + type = TokenType.DIV; + current++; + if (current < end && current[0] == '=') { + type = TokenType.ASSIGN_DIV; + current++; + } + break; + case '%': + type = TokenType.PERCENT; + current++; + if (current < end && current[0] == '=') { + type = TokenType.ASSIGN_PERCENT; + current++; + } + break; + case '\'': + case '"': + if (begin[0] == '\'') { + type = TokenType.CHARACTER_LITERAL; + } else { + type = TokenType.STRING_LITERAL; + } + token_length_in_chars = 2; + current++; + while (current < end && current[0] != begin[0]) { + if (current[0] == '\\') { + current++; + token_length_in_chars++; + if (current < end && current[0] == 'x') { + // hexadecimal escape character + current++; + token_length_in_chars++; + while (current < end && current[0].isxdigit ()) { + current++; + token_length_in_chars++; + } + } else { + current++; + token_length_in_chars++; + } + } else if (current[0] == '\n') { + break; + } else { + unichar u = ((string) current).get_char_validated ((long) (end - current)); + if (u != (unichar) (-1)) { + current += u.to_utf8 (null); + token_length_in_chars++; + } else { + Report.error (new SourceReference (source_file, line, column + token_length_in_chars, line, column + token_length_in_chars), "invalid UTF-8 character"); + } + } + } + if (current < end && current[0] != '\n') { + current++; + } else { + Report.error (new SourceReference (source_file, line, column + token_length_in_chars, line, column + token_length_in_chars), "syntax error, expected %c".printf (begin[0])); + } + break; + default: + unichar u = ((string) current).get_char_validated ((long) (end - current)); + if (u != (unichar) (-1)) { + current += u.to_utf8 (null); + Report.error (new SourceReference (source_file, line, column, line, column), "syntax error, unexpected character"); + } else { + current++; + Report.error (new SourceReference (source_file, line, column, line, column), "invalid UTF-8 character"); + } + column++; + last_token = TokenType.STRING_LITERAL; + return read_token (out token_begin, out token_end); + } + } + + if (token_length_in_chars < 0) { + column += (int) (current - begin); + } else { + column += token_length_in_chars; + } + + token_end.pos = current; + token_end.line = line; + token_end.column = column - 1; + + last_token = type; + + return type; + } + + int count_tabs () + { + int tab_count = 0; + + while (current < end && current[0] == '\t') { + current++; + column++; + tab_count++; + } + + + /* ignore comments and whitspace and other lines that contain no code */ + + space (); + + if ((current < end) && (current[0] == '\n')) return -1; + + return tab_count; + } + + bool matches (char* begin, string keyword) { + char* keyword_array = keyword; + long len = keyword.len (); + for (int i = 0; i < len; i++) { + if (begin[i] != keyword_array[i]) { + return false; + } + } + return true; + } + + bool whitespace () { + bool found = false; + while (current < end && current[0].isspace () && current[0] != '\n' ) { + + found = true; + current++; + column++; + } + return found; + } + + inline bool newline () { + if (current[0] == '\n') { + return true; + } + + return false; + } + + bool skip_newlines () { + bool new_lines = false; + + while (newline ()) { + current++; + + line++; + column = 1; + current_indent_level = 0; + + new_lines = true; + } + + return new_lines; + } + + bool comment () { + if (current > end - 2 + || current[0] != '/' + || (current[1] != '/' && current[1] != '*')) { + return false; + } + + if (current[1] == '/') { + // single-line comment + current += 2; + char* begin = current; + // skip until end of line or end of file + while (current < end && current[0] != '\n') { + current++; + } + push_comment (((string) begin).ndup ((long) (current - begin)), line == 1); + + if (current[0] == '\n') { + current++; + line++; + column = 1; + current_indent_level = 0; + } + } else { + // delimited comment + current += 2; + char* begin = current; + int begin_line = line; + while (current < end - 1 + && (current[0] != '*' || current[1] != '/')) { + if (current[0] == '\n') { + line++; + column = 0; + } + current++; + column++; + } + if (current == end - 1) { + Report.error (new SourceReference (source_file, line, column, line, column), "syntax error, expected */"); + return true; + } + push_comment (((string) begin).ndup ((long) (current - begin)), begin_line == 1); + current += 2; + column += 2; + } + + return true; + } + + void space () { + while (whitespace () || comment ()) { + } + } + + void push_comment (string comment_item, bool file_comment) { + if (_comment == null) { + _comment = comment_item; + } else { + _comment = "%s\n%s".printf (_comment, comment_item); + } + if (file_comment) { + source_file.comment = _comment; + _comment = null; + } + } + + /** + * Clears and returns the content of the comment stack. + * + * @return saved comment + */ + public string? pop_comment () { + if (_comment == null) { + return null; + } + + var result = new StringBuilder (_comment); + _comment = null; + + weak string index; + while ((index = result.str.chr (-1, '\t')) != null) { + result.erase (result.str.pointer_to_offset (index), 1); + } + + return result.str; + } +} + diff --git a/vala/valagenietokentype.vala b/vala/valagenietokentype.vala new file mode 100644 index 000000000..7f95cf406 --- /dev/null +++ b/vala/valagenietokentype.vala @@ -0,0 +1,293 @@ +/* valagenietokentype.vala + * + * Copyright (C) 2008 Jamie McCracken, Jürg Billeter + * Based on code by 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: + * Jamie McCracken jamiemcc gnome org + */ + +using GLib; + +public enum Vala.Genie.TokenType { + NONE, + ABSTRACT, + AS, + ASSERT, + ASSIGN, + ASSIGN_ADD, + ASSIGN_BITWISE_AND, + ASSIGN_BITWISE_OR, + ASSIGN_BITWISE_XOR, + ASSIGN_DIV, + ASSIGN_MUL, + ASSIGN_PERCENT, + ASSIGN_SHIFT_LEFT, + ASSIGN_SUB, + BITWISE_AND, + BITWISE_OR, + BREAK, + CARRET, + CASE, + CHARACTER_LITERAL, + CLASS, + CLOSE_BRACE, + CLOSE_BRACKET, + CLOSE_PARENS, + COLON, + COMMA, + CONST, + CONSTRUCT, + CONTINUE, + DEDENT, + DEF, + DEFAULT, + DELEGATE, + DELETE, + DIV, + DO, + DOT, + DOWNTO, + DYNAMIC, + ELLIPSIS, + ELSE, + ENUM, + ENSURES, + ERRORDOMAIN, + EOF, + EOL, + EVENT, + EXCEPT, + EXTERN, + FALSE, + FINAL, + FINALLY, + FOR, + FOREACH, + GET, + HASH, + IDENTIFIER, + IF, + IN, + INDENT, + INIT, + INLINE, + INTEGER_LITERAL, + INTERFACE, + INTERR, + IS, + ISA, + LAMBDA, + LOCK, + MINUS, + NAMESPACE, + NEW, + NULL, + OF, + OUT, + OP_AND, + OP_DEC, + OP_EQ, + OP_GE, + OP_GT, + OP_INC, + OP_LE, + OP_LT, + OP_NE, + OP_NEG, + OP_OR, + OP_PTR, + OP_SHIFT_LEFT, + OPEN_BRACE, + OPEN_BRACKET, + OPEN_PARENS, + OVERRIDE, + PASS, + PERCENT, + PLUS, + PRINT, + PRIVATE, + PROP, + PROTECTED, + PUBLIC, + RAISE, + RAISES, + REAL_LITERAL, + READONLY, + REF, + REQUIRES, + RETURN, + SEMICOLON, + SET, + SIZEOF, + STAR, + STATIC, + STRING_LITERAL, + STRUCT, + SUPER, + THIS, + TILDE, + TO, + TRUE, + TRY, + TYPEOF, + USES, + VAR, + VIRTUAL, + VOID, + VOLATILE, + WEAK, + WHEN, + WHILE, + WRITEONLY; + + public weak string to_string () { + switch (this) { + case ABSTRACT: return "`abstract'"; + case AS: return "`as'"; + case ASSERT: return "`assert'"; + case ASSIGN: return "`='"; + case ASSIGN_ADD: return "`+='"; + case ASSIGN_BITWISE_AND: return "`&='"; + case ASSIGN_BITWISE_OR: return "`|='"; + case ASSIGN_BITWISE_XOR: return "`^='"; + case ASSIGN_DIV: return "`/='"; + case ASSIGN_MUL: return "`*='"; + case ASSIGN_PERCENT: return "`%='"; + case ASSIGN_SHIFT_LEFT: return "`<<='"; + case ASSIGN_SUB: return "`-='"; + case BITWISE_AND: return "`&'"; + case BITWISE_OR: return "`|'"; + case BREAK: return "`break'"; + case CARRET: return "`^'"; + case CASE: return "`case'"; + case CHARACTER_LITERAL: return "character literal"; + case CLASS: return "`class'"; + case CLOSE_BRACE: return "`}'"; + case CLOSE_BRACKET: return "`]'"; + case CLOSE_PARENS: return "`)'"; + case COLON: return "`:'"; + case COMMA: return "`,'"; + case CONST: return "`const'"; + case CONSTRUCT: return "`construct'"; + case CONTINUE: return "`continue'"; + case DEDENT: return "`dedent'"; + case DEF: return "`def'"; + case DEFAULT: return "`default'"; + case DELEGATE: return "`delegate'"; + case DELETE: return "`delete'"; + case DIV: return "`/'"; + case DO: return "`do'"; + case DOT: return "`.'"; + case DOWNTO: return "`downto'"; + case DYNAMIC: return "`dynamic'"; + case ELLIPSIS: return "`...'"; + case ELSE: return "`else'"; + case ENUM: return "`enum'"; + case ENSURES: return "`ensures'"; + case ERRORDOMAIN: return "`errordomain'"; + case EOF: return "end of file"; + case EOL: return "end of line"; + case EVENT: return "event"; + case EXCEPT: return "`except'"; + case EXTERN: return "`extern'"; + case FALSE: return "`false'"; + case FINAL: return "`final'"; + case FINALLY: return "`finally'"; + case FOR: return "`for'"; + case FOREACH: return "`foreach'"; + case GET: return "`get'"; + case HASH: return "`hash'"; + case IDENTIFIER: return "identifier"; + case IF: return "`if'"; + case IN: return "`in'"; + case INDENT: return "`tab indent'"; + case INIT: return "`init'"; + case INLINE: return "`inline'"; + case INTEGER_LITERAL: return "integer literal"; + case INTERFACE: return "`interface'"; + case INTERR: return "`?'"; + case IS: return "`is'"; + case ISA: return "`isa'"; + case LAMBDA: return "`=>'"; + case LOCK: return "`lock'"; + case MINUS: return "`-'"; + case NAMESPACE: return "`namespace'"; + case NEW: return "`new'"; + case NULL: return "`null'"; + case OF: return "`of'"; + case OUT: return "`out'"; + case OP_AND: return "`&&'"; + case OP_DEC: return "`--'"; + case OP_EQ: return "`=='"; + case OP_GE: return "`>='"; + case OP_GT: return "`>'"; + case OP_INC: return "`++'"; + case OP_LE: return "`<='"; + case OP_LT: return "`<'"; + case OP_NE: return "`!='"; + case OP_NEG: return "`!'"; + case OP_OR: return "`||'"; + case OP_PTR: return "`->'"; + case OP_SHIFT_LEFT: return "`<<'"; + case OPEN_BRACE: return "`{'"; + case OPEN_BRACKET: return "`['"; + case OPEN_PARENS: return "`('"; + case OVERRIDE: return "`override'"; + case PASS: return "`pass'"; + case PERCENT: return "`%'"; + case PLUS: return "`+'"; + case PRINT: return "`print'"; + case PRIVATE: return "`private'"; + case PROP: return "`prop'"; + case PROTECTED: return "`protected'"; + case PUBLIC: return "`public'"; + case RAISE: return "`raise'"; + case RAISES: return "`raises'"; + case READONLY: return "`readonly'"; + case REAL_LITERAL: return "real literal"; + case REF: return "`ref'"; + case REQUIRES: return "`requires'"; + case RETURN: return "`return'"; + case SEMICOLON: return "`;'"; + case SET: return "`set'"; + case SIZEOF: return "`sizeof'"; + case STAR: return "`*'"; + case STATIC: return "`static'"; + case STRING_LITERAL: return "string literal"; + case STRUCT: return "`struct'"; + case SUPER: return "`super'"; + case THIS: return "`self'"; + case TILDE: return "`~'"; + case TO: return "`to'"; + case TRUE: return "`true'"; + case TRY: return "`try'"; + case TYPEOF: return "`typeof'"; + case USES: return "`uses'"; + case VAR: return "`var'"; + case VIRTUAL: return "`virtual'"; + case VOID: return "`void'"; + case VOLATILE: return "`volatile'"; + case WEAK: return "`weak'"; + case WHEN: return "`when'"; + case WHILE: return "`while'"; + case WRITEONLY: return "`writeonly'"; + default: return "unknown token"; + } + } +} + diff --git a/vala/valaparser.vala b/vala/valaparser.vala index 9886fa535..959dc80b2 100644 --- a/vala/valaparser.vala +++ b/vala/valaparser.vala @@ -64,8 +64,8 @@ public class Vala.Parser : CodeVisitor { } /** - * Parse all source files in the specified code context and build a - * code tree. + * Parses all .vala and .vapi source files in the specified code + * context and builds a code tree. * * @param context a code context */ diff --git a/vala/valasourcefile.vala b/vala/valasourcefile.vala index f64e3dcbd..7287a7fa4 100644 --- a/vala/valasourcefile.vala +++ b/vala/valasourcefile.vala @@ -180,6 +180,11 @@ public class Vala.SourceFile : Object { return "%s/%s".printf (context.directory, get_subdir ()); } + private string get_basename () { + long dot = filename.pointer_to_offset (filename.rchr (-1, '.')); + return Path.get_basename (filename.substring (0, dot)); + } + /** * Returns the filename to use when generating C header files. * @@ -187,9 +192,7 @@ public class Vala.SourceFile : Object { */ public string get_cheader_filename () { if (cheader_filename == null) { - var basename = filename.ndup ((uint) (filename.len () - ".vala".len ())); - basename = Path.get_basename (basename); - cheader_filename = "%s%s.h".printf (get_destination_directory (), basename); + cheader_filename = "%s%s.h".printf (get_destination_directory (), get_basename ()); } return cheader_filename; } @@ -201,9 +204,7 @@ public class Vala.SourceFile : Object { */ public string get_csource_filename () { if (csource_filename == null) { - var basename = filename.ndup ((uint) (filename.len () - ".vala".len ())); - basename = Path.get_basename (basename); - csource_filename = "%s%s.c".printf (get_destination_directory (), basename); + csource_filename = "%s%s.c".printf (get_destination_directory (), get_basename ()); } return csource_filename; } @@ -216,9 +217,7 @@ public class Vala.SourceFile : Object { */ public string get_cinclude_filename () { if (cinclude_filename == null) { - var basename = filename.ndup ((uint) (filename.len () - ".vala".len ())); - basename = Path.get_basename (basename); - cinclude_filename = "%s%s.h".printf (get_subdir (), basename); + cinclude_filename = "%s%s.h".printf (get_subdir (), get_basename ()); } return cinclude_filename; } |