1 /* valabinaryexpression.vala
3 * Copyright (C) 2006-2009 Jürg Billeter
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 * Jürg Billeter <j@bitron.ch>
26 * Represents an expression with two operands in the source code.
28 * Supports +, -, *, /, %, <<, >>, <, >, <=, >=, ==, !=, &, |, ^, &&, ||.
30 public class Vala
.BinaryExpression
: Expression
{
32 * The binary operator.
34 public BinaryOperator operator
{ get; set; }
39 public Expression left
{
45 _left
.parent_node
= this
;
52 public Expression right
{
58 _right
.parent_node
= this
;
62 private Expression _left
;
63 private Expression _right
;
66 * Creates a new binary expression.
68 * @param op binary operator
69 * @param left left operand
70 * @param right right operand
71 * @param source reference to source code
72 * @return newly created binary expression
74 public BinaryExpression (BinaryOperator op
, Expression _left
, Expression _right
, SourceReference? source
= null) {
78 source_reference
= source
;
81 public override void accept (CodeVisitor visitor
) {
82 visitor
.visit_binary_expression (this
);
84 visitor
.visit_expression (this
);
87 public override void accept_children (CodeVisitor visitor
) {
88 left
.accept (visitor
);
89 right
.accept (visitor
);
92 public override void replace_expression (Expression old_node
, Expression new_node
) {
93 if (left
== old_node
) {
96 if (right
== old_node
) {
101 private string get_operator_string () {
103 case BinaryOperator
.PLUS
: return "+";
104 case BinaryOperator
.MINUS
: return "-";
105 case BinaryOperator
.MUL
: return "*";
106 case BinaryOperator
.DIV
: return "/";
107 case BinaryOperator
.MOD
: return "%";
108 case BinaryOperator
.SHIFT_LEFT
: return "<<";
109 case BinaryOperator
.SHIFT_RIGHT
: return ">>";
110 case BinaryOperator
.LESS_THAN
: return "<";
111 case BinaryOperator
.GREATER_THAN
: return ">";
112 case BinaryOperator
.LESS_THAN_OR_EQUAL
: return "<=";
113 case BinaryOperator
.GREATER_THAN_OR_EQUAL
: return ">=";
114 case BinaryOperator
.EQUALITY
: return "==";
115 case BinaryOperator
.INEQUALITY
: return "!+";
116 case BinaryOperator
.BITWISE_AND
: return "&";
117 case BinaryOperator
.BITWISE_OR
: return "|";
118 case BinaryOperator
.BITWISE_XOR
: return "^";
119 case BinaryOperator
.AND
: return "&&";
120 case BinaryOperator
.OR
: return "||";
121 case BinaryOperator
.IN
: return "in";
124 assert_not_reached ();
127 public override string to_string () {
128 return _left
.to_string () + get_operator_string () + _right
.to_string ();
131 public override bool is_constant () {
132 return left
.is_constant () && right
.is_constant ();
135 public override bool is_pure () {
136 return left
.is_pure () && right
.is_pure ();
139 public override bool is_non_null () {
140 return left
.is_non_null () && right
.is_non_null ();
143 public override bool check (SemanticAnalyzer analyzer
) {
150 // some expressions are not in a block,
151 // for example, expressions in method contracts
152 if (analyzer
.current_symbol is Block
153 && (operator
== BinaryOperator
.AND
|| operator
== BinaryOperator
.OR
)) {
154 // convert conditional expression into if statement
155 // required for flow analysis and exception handling
157 var local
= new
LocalVariable (analyzer
.bool_type
.copy (), get_temp_name (), null, source_reference
);
158 var decl
= new
DeclarationStatement (local
, source_reference
);
159 decl
.check (analyzer
);
161 var right_stmt
= new
ExpressionStatement (new
Assignment (new MemberAccess
.simple (local
.name
, right
.source_reference
), right
, AssignmentOperator
.SIMPLE
, right
.source_reference
), right
.source_reference
);
163 var stmt
= new
ExpressionStatement (new
Assignment (new MemberAccess
.simple (local
.name
, left
.source_reference
), new
BooleanLiteral ((operator
== BinaryOperator
.OR
), left
.source_reference
), AssignmentOperator
.SIMPLE
, left
.source_reference
), left
.source_reference
);
165 var true_block
= new
Block (source_reference
);
166 var false_block
= new
Block (source_reference
);
168 if (operator
== BinaryOperator
.AND
) {
169 true_block
.add_statement (right_stmt
);
170 false_block
.add_statement (stmt
);
172 true_block
.add_statement (stmt
);
173 false_block
.add_statement (right_stmt
);
176 var if_stmt
= new
IfStatement (left
, true_block
, false_block
, source_reference
);
178 insert_statement (analyzer
.insert_block
, decl
);
179 insert_statement (analyzer
.insert_block
, if_stmt
);
181 if (!if_stmt
.check (analyzer
)) {
186 var ma
= new MemberAccess
.simple (local
.name
, source_reference
);
187 ma
.target_type
= target_type
;
190 parent_node
.replace_expression (this
, ma
);
195 if (!left
.check (analyzer
) || !right
.check (analyzer
)) {
196 /* if there were any errors in inner expressions, skip type check */
201 if (left
.value_type
== null) {
202 Report
.error (left
.source_reference
, "invalid left operand");
207 if (operator
!= BinaryOperator
.IN
&& right
.value_type
== null) {
208 Report
.error (right
.source_reference
, "invalid right operand");
213 if (left
.value_type
.data_type
== analyzer
.string_type
.data_type
214 && operator
== BinaryOperator
.PLUS
) {
215 // string concatenation
217 if (right
.value_type
== null || right
.value_type
.data_type
!= analyzer
.string_type
.data_type
) {
219 Report
.error (source_reference
, "Operands must be strings");
223 value_type
= analyzer
.string_type
.copy ();
224 if (left
.is_constant () && right
.is_constant ()) {
225 value_type
.value_owned
= false;
227 value_type
.value_owned
= true;
229 } else if (left
.value_type is ArrayType
&& operator
== BinaryOperator
.PLUS
) {
230 // array concatenation
232 var array_type
= (ArrayType
) left
.value_type
;
234 if (right
.value_type
== null || !right
.value_type
.compatible (array_type
.element_type
)) {
236 Report
.error (source_reference
, "Incompatible operand");
240 value_type
= array_type
.copy ();
241 value_type
.value_owned
= true;
242 } else if (operator
== BinaryOperator
.PLUS
243 || operator
== BinaryOperator
.MINUS
244 || operator
== BinaryOperator
.MUL
245 || operator
== BinaryOperator
.DIV
) {
246 // check for pointer arithmetic
247 if (left
.value_type is PointerType
) {
248 var offset_type
= right
.value_type
.data_type as Struct
;
249 if (offset_type
!= null && offset_type
.is_integer_type ()) {
250 if (operator
== BinaryOperator
.PLUS
251 || operator
== BinaryOperator
.MINUS
) {
252 // pointer arithmetic: pointer +/- offset
253 value_type
= left
.value_type
.copy ();
255 } else if (right
.value_type is PointerType
) {
256 // pointer arithmetic: pointer - pointer
257 value_type
= analyzer
.size_t_type
;
261 if (value_type
== null) {
262 value_type
= analyzer
.get_arithmetic_result_type (left
.value_type
, right
.value_type
);
265 if (value_type
== null) {
267 Report
.error (source_reference
, "Arithmetic operation not supported for types `%s' and `%s'".printf (left
.value_type
.to_string (), right
.value_type
.to_string ()));
270 } else if (operator
== BinaryOperator
.MOD
271 || operator
== BinaryOperator
.SHIFT_LEFT
272 || operator
== BinaryOperator
.SHIFT_RIGHT
273 || operator
== BinaryOperator
.BITWISE_XOR
) {
274 value_type
= analyzer
.get_arithmetic_result_type (left
.value_type
, right
.value_type
);
276 if (value_type
== null) {
278 Report
.error (source_reference
, "Arithmetic operation not supported for types `%s' and `%s'".printf (left
.value_type
.to_string (), right
.value_type
.to_string ()));
281 } else if (operator
== BinaryOperator
.LESS_THAN
282 || operator
== BinaryOperator
.GREATER_THAN
283 || operator
== BinaryOperator
.LESS_THAN_OR_EQUAL
284 || operator
== BinaryOperator
.GREATER_THAN_OR_EQUAL
) {
285 if (left
.value_type
.compatible (analyzer
.string_type
)
286 && right
.value_type
.compatible (analyzer
.string_type
)) {
288 } else if (left
.value_type is PointerType
&& right
.value_type is PointerType
) {
289 // pointer arithmetic
291 var resulting_type
= analyzer
.get_arithmetic_result_type (left
.value_type
, right
.value_type
);
293 if (resulting_type
== null) {
295 Report
.error (source_reference
, "Relational operation not supported for types `%s' and `%s'".printf (left
.value_type
.to_string (), right
.value_type
.to_string ()));
300 value_type
= analyzer
.bool_type
;
301 } else if (operator
== BinaryOperator
.EQUALITY
302 || operator
== BinaryOperator
.INEQUALITY
) {
303 /* relational operation */
305 if (!right
.value_type
.compatible (left
.value_type
)
306 && !left
.value_type
.compatible (right
.value_type
)) {
307 Report
.error (source_reference
, "Equality operation: `%s' and `%s' are incompatible".printf (right
.value_type
.to_string (), left
.value_type
.to_string ()));
312 if (left
.value_type
.compatible (analyzer
.string_type
)
313 && right
.value_type
.compatible (analyzer
.string_type
)) {
317 value_type
= analyzer
.bool_type
;
318 } else if (operator
== BinaryOperator
.BITWISE_AND
319 || operator
== BinaryOperator
.BITWISE_OR
) {
320 // integer type or flags type
322 value_type
= left
.value_type
;
323 } else if (operator
== BinaryOperator
.AND
324 || operator
== BinaryOperator
.OR
) {
325 if (!left
.value_type
.compatible (analyzer
.bool_type
) || !right
.value_type
.compatible (analyzer
.bool_type
)) {
327 Report
.error (source_reference
, "Operands must be boolean");
330 value_type
= analyzer
.bool_type
;
331 } else if (operator
== BinaryOperator
.IN
) {
332 if (left
.value_type
.compatible (analyzer
.int_type
)
333 && right
.value_type
.compatible (analyzer
.int_type
)) {
336 // otherwise require a bool contains () method
337 var contains_method
= right
.value_type
.get_member ("contains") as Method
;
338 if (contains_method
== null) {
339 Report
.error (source_reference
, "`%s' does not have a `contains' method".printf (right
.value_type
.to_string ()));
343 if (contains_method
.get_parameters ().size
!= 1) {
344 Report
.error (source_reference
, "`%s' must have one parameter".printf (contains_method
.get_full_name ()));
348 if (!contains_method
.return_type
.compatible (analyzer
.bool_type
)) {
349 Report
.error (source_reference
, "`%s' must return a boolean value".printf (contains_method
.get_full_name ()));
354 var contains_call
= new
MethodCall (new
MemberAccess (right
, "contains"));
355 contains_call
.add_argument (left
);
356 parent_node
.replace_expression (this
, contains_call
);
357 return contains_call
.check (analyzer
);
360 value_type
= analyzer
.bool_type
;
363 assert_not_reached ();
369 public override void get_defined_variables (Collection
<LocalVariable
> collection
) {
370 left
.get_defined_variables (collection
);
371 right
.get_defined_variables (collection
);
374 public override void get_used_variables (Collection
<LocalVariable
> collection
) {
375 left
.get_used_variables (collection
);
376 right
.get_used_variables (collection
);
380 public enum Vala
.BinaryOperator
{
392 GREATER_THAN_OR_EQUAL
,