Commit 52d84048 authored by Jürg Billeter's avatar Jürg Billeter

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.
parent 1f718e4f
......@@ -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");
......
......@@ -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);
}
......
......@@ -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);
}
......
......@@ -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 () {
......
......@@ -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 () {
......
......@@ -125,6 +125,7 @@ libvalacore_la_VALASOURCES = \
valasignal.vala \
valasignaltype.vala \
valasizeofexpression.vala \
valasliceexpression.vala \
valasourcefile.vala \
valasourcelocation.vala \
valasourcereference.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.
*
......
......@@ -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");
}
......
......@@ -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 {
......
/* 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);
}
}
......@@ -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);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment