]> git.ipfire.org Git - thirdparty/vala.git/commitdiff
Initial support for array slices 52d84048e075bba00f59992a373e1de954fa2b60
authorJürg Billeter <j@bitron.ch>
Sat, 19 Dec 2009 11:02:05 +0000 (12:02 +0100)
committerJürg Billeter <j@bitron.ch>
Sat, 19 Dec 2009 11:02:05 +0000 (12:02 +0100)
Add support for slice expressions such as array[1:5] to retrieve a
slice of length 4 starting at the second element of the array. Slice
expressions are also supported for strings and other types that provide
an appropriate slice method.

Based on patch by Robin Sonefors, fixes bug 571352.

codegen/valaccodearraymodule.vala
codegen/valaccodegenerator.vala
codegen/valaccodemodule.vala
tests/basic-types/arrays.vala
tests/basic-types/strings.vala
vala/Makefile.am
vala/valacodevisitor.vala
vala/valacodewriter.vala
vala/valaparser.vala
vala/valasliceexpression.vala [new file with mode: 0644]
vala/valasymbolresolver.vala

index e1ef010f9156d149f2c24bbd6de5fd86953bd3e1..45928c117ca003a6f5460f84edff622a3fd24e2d 100644 (file)
@@ -173,7 +173,7 @@ internal class Vala.CCodeArrayModule : CCodeMethodCallModule {
                        List<Expression> size = ((ArrayCreationExpression) array_expr).get_sizes ();
                        var length_expr = size[dim - 1];
                        return (CCodeExpression) get_ccodenode (length_expr);
-               } else if (array_expr is MethodCall || array_expr is CastExpression) {
+               } else if (array_expr is MethodCall || array_expr is CastExpression || array_expr is SliceExpression) {
                        List<CCodeExpression> size = array_expr.get_array_sizes ();
                        if (size.size >= dim) {
                                return size[dim - 1];
@@ -434,6 +434,47 @@ internal class Vala.CCodeArrayModule : CCodeMethodCallModule {
                }
        }
 
+       public override void visit_slice_expression (SliceExpression expr) {
+               expr.accept_children (codegen);
+
+               var ccontainer = (CCodeExpression) expr.container.ccodenode;
+               var cstart = (CCodeExpression) expr.start.ccodenode;
+               var cstop = (CCodeExpression) expr.stop.ccodenode;
+
+               var ccomma = new CCodeCommaExpression ();
+
+               var len_var = get_temp_variable (int_type);
+               len_var.source_reference = expr.source_reference;
+               temp_vars.insert (0, len_var);
+
+               var slice_var = get_temp_variable (expr.value_type, true, expr);
+               temp_vars.insert (0, slice_var);
+
+               if (!is_pure_ccode_expression (cstart)) {
+                       // avoid double evaluation of start
+                       var start_var = get_temp_variable (int_type);
+                       temp_vars.insert (0, start_var);
+
+                       var start_assignment = new CCodeAssignment (get_variable_cexpression (start_var.name), cstart);
+                       ccomma.append_expression (start_assignment);
+
+                       cstart = get_variable_cexpression (start_var.name);
+               }
+
+               var cstartpointer = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, ccontainer, cstart);
+               var slice_assignment = new CCodeAssignment (get_variable_cexpression (slice_var.name), cstartpointer);
+               ccomma.append_expression (slice_assignment);
+
+               var splicelen = new CCodeBinaryExpression (CCodeBinaryOperator.MINUS, cstop, cstart);
+               var len_assignment = new CCodeAssignment (get_variable_cexpression (len_var.name), splicelen);
+               ccomma.append_expression (len_assignment);
+
+               ccomma.append_expression (get_variable_cexpression (slice_var.name));
+
+               expr.ccodenode = ccomma;
+               expr.append_array_size (get_variable_cexpression (len_var.name));
+       }
+
        private CCodeForStatement get_struct_array_free_loop (Struct st) {
                var cbody = new CCodeBlock ();
                var cptrarray = new CCodeIdentifier ("array");
index a0808a060f3c193878c55b802d79c51d27156f45..35dff33a62b110edd50bc83a09af82360b4edeb1 100644 (file)
@@ -276,6 +276,10 @@ public class Vala.CCodeGenerator : CodeGenerator {
                head.visit_element_access (expr);
        }
 
+       public override void visit_slice_expression (SliceExpression expr) {
+               head.visit_slice_expression (expr);
+       }
+
        public override void visit_base_access (BaseAccess expr) {
                head.visit_base_access (expr);
        }
index aa0d7c43c4d0fd88850e10ad78df1e1636790d5e..2fee6b9b0f0bdd875af7acbd47d001b9949ad316 100644 (file)
@@ -255,6 +255,10 @@ public abstract class Vala.CCodeModule {
                next.visit_element_access (expr);
        }
 
+       public virtual void visit_slice_expression (SliceExpression expr) {
+               next.visit_slice_expression (expr);
+       }
+
        public virtual void visit_base_access (BaseAccess expr) {
                next.visit_base_access (expr);
        }
index ea88c17052a1c329da953e5a937831a5b0f8f345..ea11f545b41e11467e53a6e2cd6d6da8ffd7972d 100644 (file)
@@ -25,6 +25,12 @@ void test_integer_array () {
        assert (b.length == 2);
        assert (b[0] == 42);
        assert (b[1] == 23);
+
+       // slices
+       int[] c = a[1:3];
+       assert (c.length == 2);
+       assert (c[0] == 23);
+       assert (c[1] == 11);
 }
 
 void test_string_array () {
index 2d7df015d58585c8a35b60d517e36870e45fb8a6..bb27a61e969c57eb7ccd30a8a60cbf9a467134b8 100644 (file)
@@ -27,6 +27,12 @@ void test_string () {
        assert (!(s >= "i"));
        assert (s > "g");
        assert (!(s > "i"));
+
+       // slices
+       t = s[2:4];
+       assert (t.length == 2);
+       assert (t[0] == 'l');
+       assert (t[1] == 'l');
 }
 
 void main () {
index 17575dc7348bfa7021dbd5874ffcae3db039c1eb..57d5b2422025be8d85b5b8874586388c0495c6e6 100644 (file)
@@ -125,6 +125,7 @@ libvalacore_la_VALASOURCES = \
        valasignal.vala \
        valasignaltype.vala \
        valasizeofexpression.vala \
+       valasliceexpression.vala \
        valasourcefile.vala \
        valasourcelocation.vala \
        valasourcereference.vala \
index cf9c74e2aac2b6be0f2ff0b096558121ec32e409..a6a8f58d385ee8e7b29d1456dbefe5afa1493545 100644 (file)
@@ -500,6 +500,14 @@ public abstract class Vala.CodeVisitor {
        public virtual void visit_element_access (ElementAccess expr) {
        }
 
+       /**
+        * Visit operation called for array slice expressions.
+        *
+        * @param expr an array slice expression
+        */
+       public virtual void visit_slice_expression (SliceExpression expr) {
+       }
+
        /**
         * Visit operation called for base access expressions.
         *
index 10100db2ba635c1d33e4916665fce66dae0674c1..3f2b0d980bddb4ea949a0630d814e3f9a26456fc 100644 (file)
@@ -1374,6 +1374,15 @@ public class Vala.CodeWriter : CodeVisitor {
                write_string ("]");
        }
 
+       public override void visit_slice_expression (SliceExpression expr) {
+               expr.container.accept (this);
+               write_string ("[");
+               expr.start.accept (this);
+               write_string ("..");
+               expr.stop.accept (this);
+               write_string ("]");
+       }
+
        public override void visit_base_access (BaseAccess expr) {
                write_string ("base");
        }
index ff97edf66360ccd98d6a0046cb7aa5369eb3353a..04e83038a279b0dd55f947b414abd5a59a54482e 100644 (file)
@@ -723,13 +723,22 @@ public class Vala.Parser : CodeVisitor {
        Expression parse_element_access (SourceLocation begin, Expression inner) throws ParseError {
                expect (TokenType.OPEN_BRACKET);
                var index_list = parse_expression_list ();
+               Expression? stop = null;
+               if (index_list.size == 1 && accept (TokenType.COLON)) {
+                       // slice expression
+                       stop = parse_expression ();
+               }
                expect (TokenType.CLOSE_BRACKET);
 
-               var expr = new ElementAccess (inner, get_src (begin));
-               foreach (Expression index in index_list) {
-                       expr.append_index (index);
+               if (stop == null) {
+                       var expr = new ElementAccess (inner, get_src (begin));
+                       foreach (Expression index in index_list) {
+                               expr.append_index (index);
+                       }
+                       return expr;
+               } else {
+                       return new SliceExpression (inner, index_list[0], stop, get_src (begin));
                }
-               return expr;
        }
 
        List<Expression> parse_expression_list () throws ParseError {
diff --git a/vala/valasliceexpression.vala b/vala/valasliceexpression.vala
new file mode 100644 (file)
index 0000000..7713f73
--- /dev/null
@@ -0,0 +1,177 @@
+/* valasliceexpression.vala
+ *
+ * Copyright (C) 2009 Robin Sonefors
+ * Copyright (C) 2009 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:
+ *     Robin Sonefors <ozamosi@flukkost.nu>
+ *     Jürg Billeter <j@bitron.ch>
+ */
+
+using GLib;
+
+/**
+ * Represents an array slice expression e.g "a[1:5]".
+ */
+public class Vala.SliceExpression : Expression {
+       public Expression container {
+               get {
+                       return _container;
+               }
+               set {
+                       _container = value;
+                       _container.parent_node = this;
+               }
+       }
+
+       public Expression start {
+               get {
+                       return _start;
+               }
+               private set {
+                       _start = value;
+                       _start.parent_node = this;
+               }
+       }
+
+       public Expression stop {
+               get {
+                       return _stop;
+               }
+               private set {
+                       _stop = value;
+                       _stop.parent_node = this;
+               }
+       }
+
+       Expression _container;
+       Expression _start;
+       Expression _stop;
+
+       public SliceExpression (Expression container, Expression start, Expression stop, SourceReference? source_reference = null) {
+               this.container = container;
+               this.start = start;
+               this.stop = stop;
+               this.source_reference = source_reference;
+       }
+
+       public override void accept (CodeVisitor visitor) {
+               visitor.visit_slice_expression (this);
+
+               visitor.visit_expression (this);
+       }
+
+       public override void accept_children (CodeVisitor visitor) {
+               container.accept (visitor);
+
+               start.accept (visitor);
+               stop.accept (visitor);
+       }
+
+       public override void replace_expression (Expression old_node, Expression new_node) {
+               if (container == old_node) {
+                       container = new_node;
+               }
+               if (start == old_node) {
+                       start = new_node;
+               }
+               if (stop == old_node) {
+                       stop = new_node;
+               }
+       }
+
+       public override bool is_pure () {
+               return false;
+       }
+
+       public override bool check (SemanticAnalyzer analyzer) {
+               if (checked) {
+                       return !error;
+               }
+
+               checked = true;
+
+               if (!container.check (analyzer)) {
+                       error = true;
+                       return false;
+               }
+
+               if (!start.check (analyzer)) {
+                       error = true;
+                       return false;
+               }
+
+               if (!stop.check (analyzer)) {
+                       error = true;
+                       return false;
+               }
+
+               if (container.value_type == null) {
+                       error = true;
+                       Report.error (container.source_reference, "Invalid container expression");
+                       return false;
+               }
+
+               if (lvalue) {
+                       error = true;
+                       Report.error (container.source_reference, "Slice expressions cannot be used as lvalue");
+                       return false;
+               }
+
+               if (container.value_type is ArrayType) {
+                       value_type = container.value_type.copy ();
+                       value_type.value_owned = false;
+
+                       /* check if the index is of type integer */
+                       if (!(start.value_type is IntegerType || start.value_type is EnumValueType)) {
+                               error = true;
+                               Report.error (start.source_reference, "Expression of integer type expected");
+                       }
+                       if (!(stop.value_type is IntegerType || stop.value_type is EnumValueType)) {
+                               error = true;
+                               Report.error (stop.source_reference, "Expression of integer type expected");
+                       }
+               } else {
+                       var slice_method = container.value_type.get_member ("slice") as Method;
+                       if (slice_method != null) {
+                               var slice_call = new MethodCall (new MemberAccess (container, "slice"));
+                               slice_call.add_argument (start);
+                               slice_call.add_argument (stop);
+                               slice_call.target_type = this.target_type;
+                               parent_node.replace_expression (this, slice_call);
+                               return slice_call.check (analyzer);
+                       }
+
+                       error = true;
+                       Report.error (source_reference, "The expression `%s' does not denote an array".printf (container.value_type.to_string ()));
+               }
+
+               return !error;
+       }
+
+       public override void get_defined_variables (Collection<LocalVariable> collection) {
+               container.get_defined_variables (collection);
+               start.get_defined_variables (collection);
+               stop.get_defined_variables (collection);
+       }
+
+       public override void get_used_variables (Collection<LocalVariable> collection) {
+               container.get_used_variables (collection);
+               start.get_used_variables (collection);
+               stop.get_used_variables (collection);
+       }
+}
index de3169633a64d4edc7cb87379d44095f7f10ea4e..6b94e12603e86e967fff70d5ca1a5244c512a7bb 100644 (file)
@@ -444,6 +444,10 @@ public class Vala.SymbolResolver : CodeVisitor {
                expr.accept_children (this);
        }
 
+       public override void visit_slice_expression (SliceExpression expr) {
+               expr.accept_children (this);
+       }
+
        public override void visit_object_creation_expression (ObjectCreationExpression expr) {
                expr.accept_children (this);
        }