1/* valasliceexpression.vala
2 *
3 * Copyright (C) 2009 Robin Sonefors
4 * Copyright (C) 2009-2013 Jürg Billeter
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Lesser General Public License for more details.
15
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
19 *
20 * Author:
21 * 	Robin Sonefors <ozamosi@flukkost.nu>
22 * 	Jürg Billeter <j@bitron.ch>
23 */
24
25using GLib;
26
27/**
28 * Represents an array slice expression.
29 *
30 * {{{ foo[1:5] }}}
31 */
32public class Vala.SliceExpression : Expression {
33	public Expression container {
34		get {
35			return _container;
36		}
37		set {
38			_container = value;
39			_container.parent_node = this;
40		}
41	}
42
43	public Expression start {
44		get {
45			return _start;
46		}
47		private set {
48			_start = value;
49			_start.parent_node = this;
50		}
51	}
52
53	public Expression stop {
54		get {
55			return _stop;
56		}
57		private set {
58			_stop = value;
59			_stop.parent_node = this;
60		}
61	}
62
63	Expression _container;
64	Expression _start;
65	Expression _stop;
66
67	public SliceExpression (Expression container, Expression start, Expression stop, SourceReference? source_reference = null) {
68		this.container = container;
69		this.start = start;
70		this.stop = stop;
71		this.source_reference = source_reference;
72	}
73
74	public override void accept (CodeVisitor visitor) {
75		visitor.visit_slice_expression (this);
76
77		visitor.visit_expression (this);
78	}
79
80	public override void accept_children (CodeVisitor visitor) {
81		container.accept (visitor);
82
83		start.accept (visitor);
84		stop.accept (visitor);
85	}
86
87	public override void replace_expression (Expression old_node, Expression new_node) {
88		if (container == old_node) {
89			container = new_node;
90		}
91		if (start == old_node) {
92			start = new_node;
93		}
94		if (stop == old_node) {
95			stop = new_node;
96		}
97	}
98
99	public override bool is_pure () {
100		return false;
101	}
102
103	public override bool is_accessible (Symbol sym) {
104		return container.is_accessible (sym) && start.is_accessible (sym) && stop.is_accessible (sym);
105	}
106
107	public override bool check (CodeContext context) {
108		if (checked) {
109			return !error;
110		}
111
112		checked = true;
113
114		if (!container.check (context)) {
115			error = true;
116			return false;
117		}
118
119		if (container.value_type is ArrayType) {
120			unowned ArrayType array_type = (ArrayType) container.value_type;
121			start.target_type = array_type.length_type.copy ();
122			stop.target_type = array_type.length_type.copy ();
123		}
124
125		if (!start.check (context)) {
126			error = true;
127			return false;
128		}
129
130		if (!stop.check (context)) {
131			error = true;
132			return false;
133		}
134
135		if (container.value_type == null) {
136			error = true;
137			Report.error (container.source_reference, "Invalid container expression");
138			return false;
139		}
140
141		if (lvalue) {
142			error = true;
143			Report.error (container.source_reference, "Slice expressions cannot be used as lvalue");
144			return false;
145		}
146
147		if (container.value_type is ArrayType) {
148			value_type = container.value_type.copy ();
149			value_type.value_owned = false;
150
151			// inline allocated results are not compatible with non-constant start/stop expressions
152			unowned ArrayType array_type = (ArrayType) value_type;
153			array_type.fixed_length = false;
154			array_type.inline_allocated = false;
155			array_type.length = null;
156
157			value_type.check (context);
158
159			/* check if the index is of type integer */
160			if (!(start.value_type is IntegerType || start.value_type is EnumValueType)) {
161				error = true;
162				Report.error (start.source_reference, "Expression of integer type expected");
163			}
164			if (!(stop.value_type is IntegerType || stop.value_type is EnumValueType)) {
165				error = true;
166				Report.error (stop.source_reference, "Expression of integer type expected");
167			}
168		} else {
169			var slice_method = container.value_type.get_member ("slice") as Method;
170			if (slice_method != null) {
171				var slice_call = new MethodCall (new MemberAccess (container, "slice"));
172				slice_call.add_argument (start);
173				slice_call.add_argument (stop);
174				slice_call.target_type = this.target_type;
175				parent_node.replace_expression (this, slice_call);
176				return slice_call.check (context);
177			}
178
179			error = true;
180			Report.error (source_reference, "The expression `%s' does not denote an array".printf (container.value_type.to_string ()));
181		}
182
183		return !error;
184	}
185
186	public override void emit (CodeGenerator codegen) {
187		container.emit (codegen);
188
189		start.emit (codegen);
190		stop.emit (codegen);
191
192		codegen.visit_slice_expression (this);
193
194		codegen.visit_expression (this);
195	}
196
197	public override void get_defined_variables (Collection<Variable> collection) {
198		container.get_defined_variables (collection);
199		start.get_defined_variables (collection);
200		stop.get_defined_variables (collection);
201	}
202
203	public override void get_used_variables (Collection<Variable> collection) {
204		container.get_used_variables (collection);
205		start.get_used_variables (collection);
206		stop.get_used_variables (collection);
207	}
208}
209