]> git.ipfire.org Git - thirdparty/vala.git/commitdiff
Add parser for Genie, patch by Jamie McCracken
authorJuerg Billeter <j@bitron.ch>
Mon, 19 May 2008 20:28:13 +0000 (20:28 +0000)
committerJürg Billeter <juergbi@src.gnome.org>
Mon, 19 May 2008 20:28:13 +0000 (20:28 +0000)
2008-05-19  Juerg 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

svn path=/trunk/; revision=1401

ChangeLog
compiler/valacompiler.vala
vala/Makefile.am
vala/valagenieparser.vala [new file with mode: 0644]
vala/valageniescanner.vala [new file with mode: 0644]
vala/valagenietokentype.vala [new file with mode: 0644]
vala/valaparser.vala
vala/valasourcefile.vala

index 2688505ee9915937bc8a74a8bc4bcacb4171edca..bd6568bfeb57b3b36b3315e085bfd9ebd011ef30 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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:
index 3131875cd8bad2fa4d5497b73f5bc1499982fc56..1360111e989ce5010a5a087954afe85b39c2205c 100644 (file)
@@ -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 ();
                }
index 89aa6e801461a2a0023754b74254138cb6459ad8..958fe1f4cdde4cccca5a81567709dd9e7563af67 100644 (file)
@@ -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 (file)
index 0000000..f2f2e90
--- /dev/null
@@ -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 (file)
index 0000000..080db74
--- /dev/null
@@ -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 (file)
index 0000000..7f95cf4
--- /dev/null
@@ -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";
+               }
+       }
+}
+
index 9886fa5355319db39e7cfdef28ce9f8ec3bc46f7..959dc80b2024e6d60ff404fb137945deadc767d0 100644 (file)
@@ -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
         */
index f64e3dcbd7555e4fd897db2ce8c7590841789dec..7287a7fa494ea39f11b1a09909436f4a0cc453dc 100644 (file)
@@ -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;
        }