[vala/wip/issue/327: 40/43] Added with statement declaration



commit 8f5b960e01673192c87f39d1f63ff30b56c7d5bf
Author: Nick Schrader <nick schrader mailbox org>
Date:   Sun Apr 5 20:32:01 2020 -0300

    Added with statement declaration

 vala/valaparser.vala        | 44 +++++++++++++++++++---
 vala/valawithstatement.vala | 90 +++++++++++++++++++++++++++++++++------------
 2 files changed, 105 insertions(+), 29 deletions(-)
---
diff --git a/vala/valaparser.vala b/vala/valaparser.vala
index 1f55405ed..016e3e7dd 100644
--- a/vala/valaparser.vala
+++ b/vala/valaparser.vala
@@ -2252,15 +2252,47 @@ public class Vala.Parser : CodeVisitor {
                return new DeleteStatement (expr, src);
        }
 
-       Statement parse_with_statement () throws ParseError {
+       inline void expect_or_throw (TokenType type) throws ParseError {
+               // Don't continue on any minor issue as the with-syntax is a fragile left recursion
+               if (!accept (type)) {
+                       throw new ParseError.SYNTAX(@"expected $(type.to_string ())");
+               }
+       }
+
+       Statement? parse_with_statement () throws ParseError {
                var begin = get_location ();
-               expect (TokenType.WITH);
-               expect (TokenType.OPEN_PARENS);
-               var expr = parse_expression ();
-               expect (TokenType.CLOSE_PARENS);
+               expect_or_throw (TokenType.WITH);
+               expect_or_throw (TokenType.OPEN_PARENS);
+               var expr_or_decl = get_location();
+
+               DataType? variable_type = null;
+               string? variable_name = null;
+
+               // Try "with (expr)"
+               Expression expr = parse_expression ();
+               if (!accept (TokenType.CLOSE_PARENS)) {
+                       // Try "with (var identifier = expr)"
+                       rollback (expr_or_decl);
+                       if (accept (TokenType.VAR)) {
+                               variable_name = parse_identifier ();
+                               expect_or_throw (TokenType.ASSIGN);
+                       } else {
+                               // Try "with (type identifier = expr)"
+                               variable_type = parse_type (true, true);
+                               variable_name = parse_identifier ();
+                               if (!accept (TokenType.ASSIGN)) {
+                                       // Fallback to "with (expr)"
+                                       rollback (expr_or_decl);
+                               }
+                       }
+
+                       expr = parse_expression ();
+                       expect_or_throw (TokenType.CLOSE_PARENS);
+               }
+
                var src = get_src (begin);
                var body = parse_embedded_statement ("with", false);
-               return new WithStatement (expr, body, src);
+               return new WithStatement (variable_type, variable_name, expr, body, src);
        }
 
        string parse_attribute_value () throws ParseError {
diff --git a/vala/valawithstatement.vala b/vala/valawithstatement.vala
index 43bd91d71..06dc12f51 100644
--- a/vala/valawithstatement.vala
+++ b/vala/valawithstatement.vala
@@ -36,6 +36,24 @@ public class Vala.WithStatement : Block {
                }
        }
 
+       /**
+        * Specifies the with-variable type.
+        */
+       public DataType? type_reference {
+               get { return _data_type; }
+               private set {
+                       _data_type = value;
+                       if (_data_type != null) {
+                               _data_type.parent_node = this;
+                       }
+               }
+       }
+
+       /**
+        * Specifies the with-variable name.
+        */
+       public string? with_variable_name { get; private set; }
+
        /**
         * Specifies the with-variable.
         */
@@ -56,11 +74,15 @@ public class Vala.WithStatement : Block {
 
        private Expression _expression;
        private Block _body;
+       private DataType? _data_type;
 
-       public WithStatement (Expression expression, Block body, SourceReference? source_reference = null) {
+       public WithStatement (DataType? type_reference, string? variable_name, Expression expression, 
+                       Block body, SourceReference? source_reference = null) {
                base (source_reference);
                this.expression = expression;
                this.body = body;
+               this.type_reference = type_reference;
+               this.with_variable_name = variable_name;
        }
 
        public override void accept (CodeVisitor visitor) {
@@ -71,6 +93,11 @@ public class Vala.WithStatement : Block {
                if (expression.symbol_reference == with_variable) {
                        expression.accept (visitor);
                }
+
+               if (type_reference != null) {
+                       type_reference.accept (visitor);
+               }
+
                if (body != null) {
                        body.accept (visitor);
                }
@@ -82,6 +109,12 @@ public class Vala.WithStatement : Block {
                }
        }
 
+       public override void replace_type (DataType old_type, DataType new_type) {
+               if (type_reference == old_type) {
+                       type_reference = new_type;
+               }
+       }
+
        bool is_object_or_value_type (DataType? type) {
                if (type == null) {
                        return false;
@@ -93,13 +126,30 @@ public class Vala.WithStatement : Block {
                }
        }
 
-       LocalVariable insert_local_variable_if_necessary () {
-               LocalVariable local_var = expression.symbol_reference as LocalVariable;
-               if (local_var == null) {
-                       local_var = new LocalVariable (expression.value_type, "_with_local%d_".printf 
(next_with_id++), expression, source_reference);
+       void insert_local_variable_if_necessary () {
+               var local_var = expression.symbol_reference as LocalVariable;
+               if (with_variable_name != null || local_var == null) {
+                       var n = with_variable_name ?? "_with_local%d_".printf (next_with_id++);
+                       local_var = new LocalVariable (type_reference, n, expression, source_reference);
                        body.insert_statement (0, new DeclarationStatement (local_var, source_reference));
                }
-               return local_var;
+               with_variable = local_var;
+       }
+
+       void change_scope_and_check_body (CodeContext context) {
+               var old_symbol = context.analyzer.current_symbol;
+               owner = context.analyzer.current_symbol.scope;
+               context.analyzer.current_symbol = this;
+               body.check (context);
+               context.analyzer.current_symbol = old_symbol;
+       }
+
+       bool is_type_reference_compatible () {
+               if (type_reference == null) {
+                       type_reference = expression.value_type.copy();
+               }
+
+               return expression.value_type.compatible(type_reference);
        }
 
        public override bool check (CodeContext context) {
@@ -108,25 +158,19 @@ public class Vala.WithStatement : Block {
                }
 
                checked = true;
-
-               expression.check (context);
-
-               if (!is_object_or_value_type (expression.value_type)) {
-                       error = true;
-                       Report.error (expression.source_reference, "Expression must be of an object or basic 
type");
-                       return false;
+               if (expression.check (context)) {
+                       if (!is_object_or_value_type (expression.value_type)) {
+                               error = true;
+                               Report.error (expression.source_reference, "With: Expression must be of an 
object or basic type");
+                       } else if (!is_type_reference_compatible ()) {
+                               error = true;
+                               Report.error (type_reference.source_reference, @"With: Cannot convert from 
`$(expression.value_type)' to `$(type_reference)'");
+                       } else {
+                               insert_local_variable_if_necessary ();
+                               change_scope_and_check_body (context);
+                       }
                }
 
-               with_variable = insert_local_variable_if_necessary ();
-
-               var old_symbol = context.analyzer.current_symbol;
-               owner = context.analyzer.current_symbol.scope;
-               context.analyzer.current_symbol = this;
-
-               body.check (context);
-
-               context.analyzer.current_symbol = old_symbol;
-
                return !error;
        }
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]