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