[vala] Initial support for array slices



commit 52d84048e075bba00f59992a373e1de954fa2b60
Author: Jürg Billeter <j bitron ch>
Date:   Sat Dec 19 12:02:05 2009 +0100

    Initial support for array slices
    
    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 |   43 +++++++++-
 codegen/valaccodegenerator.vala   |    4 +
 codegen/valaccodemodule.vala      |    4 +
 tests/basic-types/arrays.vala     |    6 ++
 tests/basic-types/strings.vala    |    6 ++
 vala/Makefile.am                  |    1 +
 vala/valacodevisitor.vala         |    8 ++
 vala/valacodewriter.vala          |    9 ++
 vala/valaparser.vala              |   17 +++-
 vala/valasliceexpression.vala     |  177 +++++++++++++++++++++++++++++++++++++
 vala/valasymbolresolver.vala      |    4 +
 11 files changed, 274 insertions(+), 5 deletions(-)
---
diff --git a/codegen/valaccodearraymodule.vala b/codegen/valaccodearraymodule.vala
index e1ef010..45928c1 100644
--- a/codegen/valaccodearraymodule.vala
+++ b/codegen/valaccodearraymodule.vala
@@ -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");
diff --git a/codegen/valaccodegenerator.vala b/codegen/valaccodegenerator.vala
index a0808a0..35dff33 100644
--- a/codegen/valaccodegenerator.vala
+++ b/codegen/valaccodegenerator.vala
@@ -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);
 	}
diff --git a/codegen/valaccodemodule.vala b/codegen/valaccodemodule.vala
index aa0d7c4..2fee6b9 100644
--- a/codegen/valaccodemodule.vala
+++ b/codegen/valaccodemodule.vala
@@ -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);
 	}
diff --git a/tests/basic-types/arrays.vala b/tests/basic-types/arrays.vala
index ea88c17..ea11f54 100644
--- a/tests/basic-types/arrays.vala
+++ b/tests/basic-types/arrays.vala
@@ -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 () {
diff --git a/tests/basic-types/strings.vala b/tests/basic-types/strings.vala
index 2d7df01..bb27a61 100644
--- a/tests/basic-types/strings.vala
+++ b/tests/basic-types/strings.vala
@@ -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 () {
diff --git a/vala/Makefile.am b/vala/Makefile.am
index 17575dc..57d5b24 100644
--- a/vala/Makefile.am
+++ b/vala/Makefile.am
@@ -125,6 +125,7 @@ libvalacore_la_VALASOURCES = \
 	valasignal.vala \
 	valasignaltype.vala \
 	valasizeofexpression.vala \
+	valasliceexpression.vala \
 	valasourcefile.vala \
 	valasourcelocation.vala \
 	valasourcereference.vala \
diff --git a/vala/valacodevisitor.vala b/vala/valacodevisitor.vala
index cf9c74e..a6a8f58 100644
--- a/vala/valacodevisitor.vala
+++ b/vala/valacodevisitor.vala
@@ -501,6 +501,14 @@ public abstract class Vala.CodeVisitor {
 	}
 
 	/**
+	 * 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.
 	 *
 	 * @param expr a base access expression
diff --git a/vala/valacodewriter.vala b/vala/valacodewriter.vala
index 10100db..3f2b0d9 100644
--- a/vala/valacodewriter.vala
+++ b/vala/valacodewriter.vala
@@ -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");
 	}
diff --git a/vala/valaparser.vala b/vala/valaparser.vala
index ff97edf..04e8303 100644
--- a/vala/valaparser.vala
+++ b/vala/valaparser.vala
@@ -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
index 0000000..7713f73
--- /dev/null
+++ b/vala/valasliceexpression.vala
@@ -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);
+	}
+}
diff --git a/vala/valasymbolresolver.vala b/vala/valasymbolresolver.vala
index de31696..6b94e12 100644
--- a/vala/valasymbolresolver.vala
+++ b/vala/valasymbolresolver.vala
@@ -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);
 	}



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