From: Jürg Billeter Date: Sat, 19 Dec 2009 11:02:05 +0000 (+0100) Subject: Initial support for array slices X-Git-Tag: 0.7.9~8 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=52d84048e075bba00f59992a373e1de954fa2b60;p=thirdparty%2Fvala.git 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. --- diff --git a/codegen/valaccodearraymodule.vala b/codegen/valaccodearraymodule.vala index e1ef010f9..45928c117 100644 --- a/codegen/valaccodearraymodule.vala +++ b/codegen/valaccodearraymodule.vala @@ -173,7 +173,7 @@ internal class Vala.CCodeArrayModule : CCodeMethodCallModule { List 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 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 a0808a060..35dff33a6 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 aa0d7c43c..2fee6b9b0 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 ea88c1705..ea11f545b 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 2d7df015d..bb27a61e9 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 17575dc73..57d5b2422 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 cf9c74e2a..a6a8f58d3 100644 --- a/vala/valacodevisitor.vala +++ b/vala/valacodevisitor.vala @@ -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. * diff --git a/vala/valacodewriter.vala b/vala/valacodewriter.vala index 10100db2b..3f2b0d980 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 ff97edf66..04e83038a 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 parse_expression_list () throws ParseError { diff --git a/vala/valasliceexpression.vala b/vala/valasliceexpression.vala new file mode 100644 index 000000000..7713f738d --- /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 + * Jürg Billeter + */ + +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 collection) { + container.get_defined_variables (collection); + start.get_defined_variables (collection); + stop.get_defined_variables (collection); + } + + public override void get_used_variables (Collection 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 de3169633..6b94e1260 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); }