1use crate::ast::expressions::{
2 BinaryOperation, ComparisonOperation, DatexExpression, DatexExpressionData,
3 UnaryOperation,
4};
5use crate::{
6 fmt::{
7 Assoc, Format, Formatter, Operation, ParentContext,
8 options::BracketStyle,
9 },
10 global::operators::{
11 BinaryOperator, ComparisonOperator, LogicalUnaryOperator,
12 UnaryOperator,
13 binary::{ArithmeticOperator, LogicalOperator},
14 },
15};
16
17impl<'a> Formatter<'a> {
18 pub fn handle_bracketing(
20 &'a self,
21 expression: &'a DatexExpression,
22 doc: Format<'a>,
23 parent_ctx: Option<ParentContext<'a>>,
24 is_left_child_of_parent: bool,
25 ) -> Format<'a> {
26 self.maybe_wrap_by_parent(
27 expression,
28 doc,
29 parent_ctx,
30 is_left_child_of_parent,
31 )
32 }
33
34 pub fn maybe_wrap_by_parent(
36 &'a self,
37 expression: &'a DatexExpression,
38 inner: Format<'a>,
39 parent_ctx: Option<ParentContext<'a>>,
40 is_left_child_of_parent: bool,
41 ) -> Format<'a> {
42 if parent_ctx.is_none() {
44 return inner;
45 }
46
47 if let DatexExpressionData::Statements(statements) = &expression.data {
49 println!(
50 "DEBUG: Handling Statements bracketing: {:#?}",
51 statements
52 );
53 return
54 if statements.statements.len() > 1 || statements.is_terminated {
56 self.wrap_in_parens(inner)
57 }
58 else {
60 match self.options.bracket_style {
61 BracketStyle::KeepAll => self.wrap_in_parens(inner),
62 BracketStyle::RemoveDuplicate | BracketStyle::Minimal => inner,
63 }
64 };
65 }
66
67 let need = self.needs_parens_for_child_expr(
68 expression,
69 &parent_ctx.unwrap(),
70 is_left_child_of_parent,
71 );
72
73 if need {
74 self.wrap_in_parens(inner)
75 } else {
76 inner
77 }
78 }
79
80 pub fn binary_operator_info(
82 &self,
83 op: &BinaryOperator,
84 ) -> (u8, Assoc, bool) {
85 match op {
86 BinaryOperator::Arithmetic(aop) => match aop {
87 ArithmeticOperator::Multiply
88 | ArithmeticOperator::Divide
89 | ArithmeticOperator::Modulo => (20, Assoc::Left, false),
90 ArithmeticOperator::Add => (10, Assoc::Left, true), ArithmeticOperator::Subtract => (10, Assoc::Left, false), ArithmeticOperator::Power => (30, Assoc::Right, false),
93 _ => (10, Assoc::Left, false),
94 },
95 BinaryOperator::Logical(lop) => match lop {
96 LogicalOperator::And => (5, Assoc::Left, false),
97 LogicalOperator::Or => (4, Assoc::Left, false),
98 },
99 _ => (1, Assoc::None, false),
101 }
102 }
103
104 fn comparison_operator_info(
106 &self,
107 op: &ComparisonOperator,
108 ) -> (u8, Assoc, bool) {
109 match op {
110 ComparisonOperator::Equal
111 | ComparisonOperator::NotEqual
112 | ComparisonOperator::LessThan
113 | ComparisonOperator::LessThanOrEqual
114 | ComparisonOperator::GreaterThan
115 | ComparisonOperator::GreaterThanOrEqual => (7, Assoc::None, false),
116 _ => (7, Assoc::None, false),
117 }
118 }
119
120 fn unary_operator_info(&self, op: &UnaryOperator) -> (u8, Assoc, bool) {
122 match op {
123 UnaryOperator::Arithmetic(_) => (35, Assoc::Right, false),
124 UnaryOperator::Logical(LogicalUnaryOperator::Not) => {
125 (35, Assoc::Right, false)
126 }
127 UnaryOperator::Reference(_) => (40, Assoc::Right, false),
128 UnaryOperator::Bitwise(_) => (35, Assoc::Right, false),
129 }
130 }
131
132 fn expression_precedence(&self, expression: &DatexExpression) -> u8 {
134 match &expression.data {
135 DatexExpressionData::BinaryOperation(BinaryOperation {
136 operator,
137 ..
138 }) => {
139 let (prec, _, _) = self.binary_operator_info(operator);
140 prec
141 }
142 DatexExpressionData::ComparisonOperation(ComparisonOperation {
143 operator,
144 ..
145 }) => {
146 let (prec, _, _) = self.comparison_operator_info(operator);
147 prec
148 }
149 DatexExpressionData::UnaryOperation(UnaryOperation {
150 operator: op,
151 ..
152 }) => {
153 let (prec, _, _) = self.unary_operator_info(op);
154 prec
155 }
156 DatexExpressionData::CreateRef(_) => 40,
157 _ => 255, }
159 }
160
161 fn needs_parens_for_child_expr(
165 &self,
166 child: &DatexExpression,
167 parent_context: &ParentContext<'a>,
168 is_left_child: bool,
169 ) -> bool {
170 let child_prec = self.expression_precedence(child);
172
173 if child_prec < parent_context.precedence {
174 return true; }
176 if child_prec > parent_context.precedence {
177 return false; }
179
180 let same_op_and_assoc = match (&child.data, &parent_context.operation) {
186 (
187 DatexExpressionData::BinaryOperation(BinaryOperation {
188 operator,
189 ..
190 }),
191 Operation::Binary(parent_op),
192 ) => {
193 let (_, _, c_is_assoc) = self.binary_operator_info(operator);
194 operator == *parent_op && c_is_assoc
195 }
196 (
197 DatexExpressionData::ComparisonOperation(ComparisonOperation {
198 operator,
199 ..
200 }),
201 Operation::Comparison(parent_op),
202 ) => {
203 let (_, _, c_is_assoc) =
204 self.comparison_operator_info(operator);
205 operator == *parent_op && c_is_assoc
206 }
207 (
208 DatexExpressionData::UnaryOperation(UnaryOperation {
209 operator,
210 ..
211 }),
212 Operation::Unary(parent_op),
213 ) => {
214 let (_, _, c_is_assoc) = self.unary_operator_info(operator);
215 operator == *parent_op && c_is_assoc
216 }
217 _ => false,
218 };
219
220 if same_op_and_assoc {
221 return false;
223 }
224
225 match parent_context.associativity {
227 Assoc::Left => {
228 !is_left_child
230 }
231 Assoc::Right => {
232 is_left_child
234 }
235 Assoc::None => {
236 true
238 }
239 }
240 }
241}
242
243#[cfg(test)]
244mod tests {
245 use crate::fmt::options::{BracketStyle, FormattingOptions};
246
247 use super::*;
248
249 fn to_string(script: &str, options: FormattingOptions) -> String {
250 let formatter = Formatter::new(script, options);
251 formatter.render()
252 }
253
254 #[test]
255 #[ignore = "bracketing must be fixed"]
256 fn bracketing() {
257 let expr = "((42))";
258 assert_eq!(
259 to_string(
260 expr,
261 FormattingOptions {
262 bracket_style: BracketStyle::KeepAll,
263 ..Default::default()
264 }
265 ),
266 "((42))"
267 );
268 assert_eq!(
269 to_string(
270 expr,
271 FormattingOptions {
272 bracket_style: BracketStyle::RemoveDuplicate,
273 ..Default::default()
274 }
275 ),
276 "(42)"
277 );
278 assert_eq!(
279 to_string(
280 expr,
281 FormattingOptions {
282 bracket_style: BracketStyle::Minimal,
283 ..Default::default()
284 }
285 ),
286 "42"
287 );
288 }
289
290 #[test]
291 #[ignore = "bracketing must be fixed"]
292 fn binary_operations_wrapped() {
293 let expr = "(1 + 2) * 3 - 4 / 5";
295 assert_eq!(
296 to_string(
297 expr,
298 FormattingOptions {
299 bracket_style: BracketStyle::Minimal,
300 ..Default::default()
301 }
302 ),
303 "(1 + 2) * 3 - 4 / 5"
304 );
305
306 let expr = "1 + (2 * 3) - 4 / 5";
308 assert_eq!(
309 to_string(
310 expr,
311 FormattingOptions {
312 bracket_style: BracketStyle::Minimal,
313 ..Default::default()
314 }
315 ),
316 "1 + 2 * 3 - 4 / 5"
317 );
318 }
319
320 #[test]
321 fn associative_operations_no_parens_needed() {
322 let expr = "(1 + 2) + 3";
324 assert_eq!(
325 to_string(
326 expr,
327 FormattingOptions {
328 bracket_style: BracketStyle::Minimal,
329 ..Default::default()
330 }
331 ),
332 "1 + 2 + 3"
333 );
334
335 let expr = "1 + (2 + 3)";
337 assert_eq!(
338 to_string(
339 expr,
340 FormattingOptions {
341 bracket_style: BracketStyle::Minimal,
342 ..Default::default()
343 }
344 ),
345 "1 + 2 + 3"
346 );
347 }
348
349 #[test]
350 #[ignore = "bracketing must be fixed"]
351 fn non_associative_operations_keep_parens() {
352 let expr = "1 - (2 - 3)";
354 assert_eq!(
355 to_string(
356 expr,
357 FormattingOptions {
358 bracket_style: BracketStyle::Minimal,
359 ..Default::default()
360 }
361 ),
362 "1 - (2 - 3)"
363 );
364
365 let expr = "(1 - 2) - 3";
367 assert_eq!(
368 to_string(
369 expr,
370 FormattingOptions {
371 bracket_style: BracketStyle::Minimal,
372 ..Default::default()
373 }
374 ),
375 "1 - 2 - 3"
376 );
377 }
378
379 #[test]
380 #[ignore = "bracketing must be fixed"]
381 fn power_operator_right_associative() {
382 let expr = "2 ^ (3 ^ 4)";
384 assert_eq!(
385 to_string(
386 expr,
387 FormattingOptions {
388 bracket_style: BracketStyle::Minimal,
389 ..Default::default()
390 }
391 ),
392 "2 ^ 3 ^ 4"
393 );
394
395 let expr = "(2 ^ 3) ^ 4";
397 assert_eq!(
398 to_string(
399 expr,
400 FormattingOptions {
401 bracket_style: BracketStyle::Minimal,
402 ..Default::default()
403 }
404 ),
405 "(2 ^ 3) ^ 4"
406 );
407 }
408
409 #[test]
410 #[ignore = "bracketing must be fixed"]
411 fn logical_and_or_precedence() {
412 let expr = "(true and false) or true";
414 assert_eq!(
415 to_string(
416 expr,
417 FormattingOptions {
418 bracket_style: BracketStyle::Minimal,
419 ..Default::default()
420 }
421 ),
422 "true and false or true"
423 );
424
425 let expr = "true and (false or true)";
427 assert_eq!(
428 to_string(
429 expr,
430 FormattingOptions {
431 bracket_style: BracketStyle::Minimal,
432 ..Default::default()
433 }
434 ),
435 "true and (false or true)"
436 );
437 }
438
439 #[test]
440 #[ignore = "bracketing must be fixed"]
441 fn remove_duplicate_brackets() {
442 let expr = "(((1 + 2)))";
444 assert_eq!(
445 to_string(
446 expr,
447 FormattingOptions {
448 bracket_style: BracketStyle::RemoveDuplicate,
449 ..Default::default()
450 }
451 ),
452 "(1 + 2)"
453 );
454 }
455
456 #[test]
457 fn keep_all_brackets_exactly() {
458 let expr = "(((1 + 2)))";
460 assert_eq!(
461 to_string(
462 expr,
463 FormattingOptions {
464 bracket_style: BracketStyle::KeepAll,
465 ..Default::default()
466 }
467 ),
468 "(((1 + 2)))"
469 );
470 }
471
472 #[test]
473 fn minimal_vs_keepall_equivalence_for_simple() {
474 let expr = "1 + 2 * 3";
475 let minimal = to_string(
476 expr,
477 FormattingOptions {
478 bracket_style: BracketStyle::Minimal,
479 ..Default::default()
480 },
481 );
482 let keep_all = to_string(
483 expr,
484 FormattingOptions {
485 bracket_style: BracketStyle::KeepAll,
486 ..Default::default()
487 },
488 );
489 assert_eq!(minimal, keep_all);
490 assert_eq!(minimal, "1 + 2 * 3");
491 }
492}