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>
25 * Represents an expression with two operands in the source code.
27 * Supports +, -, *, /, %, <<, >>, <, >, <=, >=, ==, !=, &, |, ^, &&, ||.
29 public class Vala
.BinaryExpression
: Expression
{
31 * The binary operator.
33 public BinaryOperator operator
{ get; set; }
38 public Expression left
{
44 _left
.parent_node
= this
;
51 public Expression right
{
57 _right
.parent_node
= this
;
61 private Expression _left
;
62 private Expression _right
;
65 * Creates a new binary expression.
67 * @param op binary operator
68 * @param left left operand
69 * @param right right operand
70 * @param source reference to source code
71 * @return newly created binary expression
73 public BinaryExpression (BinaryOperator op
, Expression _left
, Expression _right
, SourceReference? source
= null) {
77 source_reference
= source
;
80 public override void accept (CodeVisitor visitor
) {
81 visitor
.visit_binary_expression (this
);
83 visitor
.visit_expression (this
);
86 public override void accept_children (CodeVisitor visitor
) {
87 left
.accept (visitor
);
88 right
.accept (visitor
);
91 public override void replace_expression (Expression old_node
, Expression new_node
) {
92 if (left
== old_node
) {
95 if (right
== old_node
) {
100 private string get_operator_string () {
102 case BinaryOperator
.PLUS
: return "+";
103 case BinaryOperator
.MINUS
: return "-";
104 case BinaryOperator
.MUL
: return "*";
105 case BinaryOperator
.DIV
: return "/";
106 case BinaryOperator
.MOD
: return "%";
107 case BinaryOperator
.SHIFT_LEFT
: return "<<";
108 case BinaryOperator
.SHIFT_RIGHT
: return ">>";
109 case BinaryOperator
.LESS_THAN
: return "<";
110 case BinaryOperator
.GREATER_THAN
: return ">";
111 case BinaryOperator
.LESS_THAN_OR_EQUAL
: return "<=";
112 case BinaryOperator
.GREATER_THAN_OR_EQUAL
: return ">=";
113 case BinaryOperator
.EQUALITY
: return "==";
114 case BinaryOperator
.INEQUALITY
: return "!+";
115 case BinaryOperator
.BITWISE_AND
: return "&";
116 case BinaryOperator
.BITWISE_OR
: return "|";
117 case BinaryOperator
.BITWISE_XOR
: return "^";
118 case BinaryOperator
.AND
: return "&&";
119 case BinaryOperator
.OR
: return "||";
120 case BinaryOperator
.IN
: return "in";
123 assert_not_reached ();
126 public override string to_string () {
127 return _left
.to_string () + get_operator_string () + _right
.to_string ();
130 public override bool is_constant () {
131 return left
.is_constant () && right
.is_constant ();
134 public override bool is_pure () {
135 return left
.is_pure () && right
.is_pure ();
138 public override bool is_non_null () {
139 return left
.is_non_null () && right
.is_non_null ();
143 CodeNode expr
= this
;
144 while (expr
!= null && !(expr is MethodCall
&& ((MethodCall
) expr
).is_assert
)) {
145 expr
= expr
.parent_node
;
147 return (expr
!= null);
150 public override bool check (SemanticAnalyzer analyzer
) {
157 // some expressions are not in a block,
158 // for example, expressions in method contracts
160 // also don't convert expressions in asserts to not execute
161 // assert expressions when disabled on the C level and
162 // avoid unusable assertion messages
163 // reachability analysis and error handling should never be
164 // necessary for assertion expressions, so it is ok to avoid
166 if (analyzer
.current_symbol is Block
&& !in_assert ()
167 && (operator
== BinaryOperator
.AND
|| operator
== BinaryOperator
.OR
)) {
168 // convert conditional expression into if statement
169 // required for flow analysis and exception handling
171 var local
= new
LocalVariable (analyzer
.bool_type
.copy (), get_temp_name (), null, source_reference
);
172 var decl
= new
DeclarationStatement (local
, source_reference
);
173 decl
.check (analyzer
);
175 var right_stmt
= new
ExpressionStatement (new
Assignment (new MemberAccess
.simple (local
.name
, right
.source_reference
), right
, AssignmentOperator
.SIMPLE
, right
.source_reference
), right
.source_reference
);
177 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
);
179 var true_block
= new
Block (source_reference
);
180 var false_block
= new
Block (source_reference
);
182 if (operator
== BinaryOperator
.AND
) {
183 true_block
.add_statement (right_stmt
);
184 false_block
.add_statement (stmt
);
186 true_block
.add_statement (stmt
);
187 false_block
.add_statement (right_stmt
);
190 var if_stmt
= new
IfStatement (left
, true_block
, false_block
, source_reference
);
192 insert_statement (analyzer
.insert_block
, decl
);
193 insert_statement (analyzer
.insert_block
, if_stmt
);
195 if (!if_stmt
.check (analyzer
)) {
200 var ma
= new MemberAccess
.simple (local
.name
, source_reference
);
201 ma
.target_type
= target_type
;
204 parent_node
.replace_expression (this
, ma
);
209 if (!left
.check (analyzer
) || !right
.check (analyzer
)) {
210 /* if there were any errors in inner expressions, skip type check */
215 if (left
.value_type
== null) {
216 Report
.error (left
.source_reference
, "invalid left operand");
221 if (operator
!= BinaryOperator
.IN
&& right
.value_type
== null) {
222 Report
.error (right
.source_reference
, "invalid right operand");
227 if (left
.value_type
.data_type
== analyzer
.string_type
.data_type
228 && operator
== BinaryOperator
.PLUS
) {
229 // string concatenation
231 if (right
.value_type
== null || right
.value_type
.data_type
!= analyzer
.string_type
.data_type
) {
233 Report
.error (source_reference
, "Operands must be strings");
237 value_type
= analyzer
.string_type
.copy ();
238 if (left
.is_constant () && right
.is_constant ()) {
239 value_type
.value_owned
= false;
241 value_type
.value_owned
= true;
243 } else if (left
.value_type is ArrayType
&& operator
== BinaryOperator
.PLUS
) {
244 // array concatenation
246 var array_type
= (ArrayType
) left
.value_type
;
248 if (right
.value_type
== null || !right
.value_type
.compatible (array_type
.element_type
)) {
250 Report
.error (source_reference
, "Incompatible operand");
254 value_type
= array_type
.copy ();
255 value_type
.value_owned
= true;
256 } else if (operator
== BinaryOperator
.PLUS
257 || operator
== BinaryOperator
.MINUS
258 || operator
== BinaryOperator
.MUL
259 || operator
== BinaryOperator
.DIV
) {
260 // check for pointer arithmetic
261 if (left
.value_type is PointerType
) {
262 var offset_type
= right
.value_type
.data_type as Struct
;
263 if (offset_type
!= null && offset_type
.is_integer_type ()) {
264 if (operator
== BinaryOperator
.PLUS
265 || operator
== BinaryOperator
.MINUS
) {
266 // pointer arithmetic: pointer +/- offset
267 value_type
= left
.value_type
.copy ();
269 } else if (right
.value_type is PointerType
) {
270 // pointer arithmetic: pointer - pointer
271 value_type
= analyzer
.size_t_type
;
275 if (value_type
== null) {
276 value_type
= analyzer
.get_arithmetic_result_type (left
.value_type
, right
.value_type
);
279 if (value_type
== null) {
281 Report
.error (source_reference
, "Arithmetic operation not supported for types `%s' and `%s'".printf (left
.value_type
.to_string (), right
.value_type
.to_string ()));
284 } else if (operator
== BinaryOperator
.MOD
285 || operator
== BinaryOperator
.SHIFT_LEFT
286 || operator
== BinaryOperator
.SHIFT_RIGHT
287 || operator
== BinaryOperator
.BITWISE_XOR
) {
288 value_type
= analyzer
.get_arithmetic_result_type (left
.value_type
, right
.value_type
);
290 if (value_type
== null) {
292 Report
.error (source_reference
, "Arithmetic operation not supported for types `%s' and `%s'".printf (left
.value_type
.to_string (), right
.value_type
.to_string ()));
295 } else if (operator
== BinaryOperator
.LESS_THAN
296 || operator
== BinaryOperator
.GREATER_THAN
297 || operator
== BinaryOperator
.LESS_THAN_OR_EQUAL
298 || operator
== BinaryOperator
.GREATER_THAN_OR_EQUAL
) {
299 if (left
.value_type
.compatible (analyzer
.string_type
)
300 && right
.value_type
.compatible (analyzer
.string_type
)) {
302 } else if (left
.value_type is PointerType
&& right
.value_type is PointerType
) {
303 // pointer arithmetic
305 var resulting_type
= analyzer
.get_arithmetic_result_type (left
.value_type
, right
.value_type
);
307 if (resulting_type
== null) {
309 Report
.error (source_reference
, "Relational operation not supported for types `%s' and `%s'".printf (left
.value_type
.to_string (), right
.value_type
.to_string ()));
314 value_type
= analyzer
.bool_type
;
315 } else if (operator
== BinaryOperator
.EQUALITY
316 || operator
== BinaryOperator
.INEQUALITY
) {
317 /* relational operation */
319 if (!right
.value_type
.compatible (left
.value_type
)
320 && !left
.value_type
.compatible (right
.value_type
)) {
321 Report
.error (source_reference
, "Equality operation: `%s' and `%s' are incompatible".printf (right
.value_type
.to_string (), left
.value_type
.to_string ()));
326 if (left
.value_type
.compatible (analyzer
.string_type
)
327 && right
.value_type
.compatible (analyzer
.string_type
)) {
331 value_type
= analyzer
.bool_type
;
332 } else if (operator
== BinaryOperator
.BITWISE_AND
333 || operator
== BinaryOperator
.BITWISE_OR
) {
334 // integer type or flags type
336 value_type
= left
.value_type
;
337 } else if (operator
== BinaryOperator
.AND
338 || operator
== BinaryOperator
.OR
) {
339 if (!left
.value_type
.compatible (analyzer
.bool_type
) || !right
.value_type
.compatible (analyzer
.bool_type
)) {
341 Report
.error (source_reference
, "Operands must be boolean");
344 value_type
= analyzer
.bool_type
;
345 } else if (operator
== BinaryOperator
.IN
) {
346 if (left
.value_type
.compatible (analyzer
.int_type
)
347 && right
.value_type
.compatible (analyzer
.int_type
)) {
350 // otherwise require a bool contains () method
351 var contains_method
= right
.value_type
.get_member ("contains") as Method
;
352 if (contains_method
== null) {
353 Report
.error (source_reference
, "`%s' does not have a `contains' method".printf (right
.value_type
.to_string ()));
357 if (contains_method
.get_parameters ().size
!= 1) {
358 Report
.error (source_reference
, "`%s' must have one parameter".printf (contains_method
.get_full_name ()));
362 if (!contains_method
.return_type
.compatible (analyzer
.bool_type
)) {
363 Report
.error (source_reference
, "`%s' must return a boolean value".printf (contains_method
.get_full_name ()));
368 var contains_call
= new
MethodCall (new
MemberAccess (right
, "contains"));
369 contains_call
.add_argument (left
);
370 parent_node
.replace_expression (this
, contains_call
);
371 return contains_call
.check (analyzer
);
374 value_type
= analyzer
.bool_type
;
377 assert_not_reached ();
383 public override void get_defined_variables (Collection
<LocalVariable
> collection
) {
384 left
.get_defined_variables (collection
);
385 right
.get_defined_variables (collection
);
388 public override void get_used_variables (Collection
<LocalVariable
> collection
) {
389 left
.get_used_variables (collection
);
390 right
.get_used_variables (collection
);
394 public enum Vala
.BinaryOperator
{
406 GREATER_THAN_OR_EQUAL
,