datex_core/fmt/
formatting.rs

1use core::ops::Range;
2
3use pretty::DocAllocator;
4
5use crate::ast::expressions::{
6    BinaryOperation, DatexExpression, DatexExpressionData, List, Map,
7    VariableAccess, VariableDeclaration,
8};
9use crate::fmt::Assoc;
10use crate::references::reference::ReferenceMutability;
11use crate::{
12    fmt::{
13        Format, Formatter, Operation, ParentContext,
14        options::{StatementFormatting, VariantFormatting},
15    },
16    values::core_values::{
17        decimal::typed_decimal::TypedDecimal,
18        integer::typed_integer::TypedInteger,
19    },
20};
21
22impl<'a> Formatter<'a> {
23    pub fn datex_expression_to_source_code(
24        &'a self,
25        expr: &'a DatexExpression,
26    ) -> Format<'a> {
27        let a = &self.alloc;
28        match &expr.data {
29            DatexExpressionData::Integer(i) => a.as_string(i),
30            DatexExpressionData::TypedInteger(ti) => {
31                self.typed_integer_to_source_code(ti, &expr.span)
32            }
33            DatexExpressionData::Decimal(d) => a.as_string(d),
34            DatexExpressionData::TypedDecimal(td) => {
35                self.typed_decimal_to_source_code(td, &expr.span)
36            }
37            DatexExpressionData::Boolean(b) => a.as_string(b),
38            DatexExpressionData::Text(t) => self.text_to_source_code(t),
39            DatexExpressionData::Endpoint(e) => a.text(e.to_string()),
40            DatexExpressionData::Null => a.text("null"),
41            DatexExpressionData::Identifier(l) => unreachable!(
42                "Identifiers should have been resolved before formatting"
43            ),
44            DatexExpressionData::Map(map) => self.map_to_source_code(map),
45            DatexExpressionData::List(list) => self.list_to_source_code(list),
46            DatexExpressionData::CreateRef(create_ref) => {
47                (match create_ref.mutability {
48                    ReferenceMutability::Immutable => a.text("&"),
49                    ReferenceMutability::Mutable => a.text("&mut "),
50                }) + self.format_datex_expression(&create_ref.expression)
51            }
52            DatexExpressionData::BinaryOperation(BinaryOperation {
53                operator,
54                left,
55                right,
56                ..
57            }) => {
58                let (precedence, associativity, _is_assoc) =
59                    self.binary_operator_info(operator);
60
61                // format children with parent context so they can decide about parens themselves
62                let left_doc = self.format_datex_expression_with_parent(
63                    left,
64                    Some(ParentContext {
65                        precedence,
66                        associativity,
67                        operation: Operation::Binary(operator),
68                    }),
69                    true,
70                );
71                let right_doc = self.format_datex_expression_with_parent(
72                    right,
73                    Some(ParentContext {
74                        precedence,
75                        associativity,
76                        operation: Operation::Binary(operator),
77                    }),
78                    false,
79                );
80
81                let a = &self.alloc;
82                (left_doc
83                    + self.operator_with_spaces(a.text(operator.to_string()))
84                    + right_doc)
85                    .group()
86            }
87            DatexExpressionData::Statements(statements) => {
88                let is_terminated = statements.is_terminated;
89                let docs: Vec<_> = statements
90                    .statements
91                    .iter()
92                    .enumerate()
93                    .map(|(i, stmt)| {
94                        self.format_datex_expression_with_parent(
95                            stmt,
96                            Some(ParentContext {
97                                precedence: 0,
98                                associativity: Assoc::None,
99                                operation: Operation::Statements,
100                            }),
101                            false,
102                        ) + (if is_terminated
103                            || i < statements.statements.len() - 1
104                        {
105                            a.text(";")
106                        } else {
107                            self.alloc.nil()
108                        })
109                    })
110                    .collect();
111
112                let joined = a.intersperse(
113                    docs,
114                    match self.options.statement_formatting {
115                        StatementFormatting::NewlineBetween => a.hardline(),
116                        StatementFormatting::SpaceBetween => a.space(),
117                        StatementFormatting::Compact => a.nil(),
118                    },
119                );
120                joined.group()
121            }
122            DatexExpressionData::VariableDeclaration(VariableDeclaration {
123                id: _,
124                init_expression,
125                kind,
126                name,
127                type_annotation,
128            }) => {
129                let type_annotation_doc =
130                    if let Some(type_annotation) = type_annotation {
131                        self.type_declaration_colon()
132                            + self.format_type_expression(type_annotation)
133                    } else {
134                        a.nil()
135                    };
136                a.text(kind.to_string())
137                    + a.space()
138                    + a.text(name)
139                    + type_annotation_doc
140                    + self.operator_with_spaces(a.text("="))
141                    + self.format_datex_expression(init_expression)
142            }
143            DatexExpressionData::TypeExpression(type_expr) => {
144                let a = &self.alloc;
145                let inner = self.format_type_expression(type_expr);
146                (a.text("type<") + a.line_() + inner + a.line_() + a.text(">"))
147                    .group()
148            }
149            DatexExpressionData::VariableAccess(VariableAccess {
150                name,
151                ..
152            }) => a.text(name),
153            e => core::panic!("Formatter not implemented for {:?}", e),
154        }
155    }
156
157    /// Formats a typed integer into source code representation based on variant formatting options.
158    fn typed_integer_to_source_code(
159        &'a self,
160        ti: &'a TypedInteger,
161        span: &'a Range<usize>,
162    ) -> Format<'a> {
163        let a = &self.alloc;
164        match self.options.variant_formatting {
165            VariantFormatting::KeepAll => a.text(self.tokens_at(span)),
166            VariantFormatting::WithSuffix => a.text(ti.to_string_with_suffix()),
167            VariantFormatting::WithoutSuffix => a.text(ti.to_string()),
168        }
169    }
170
171    /// Formats a typed decimal into source code representation based on variant formatting options.
172    fn typed_decimal_to_source_code(
173        &'a self,
174        td: &'a TypedDecimal,
175        span: &'a Range<usize>,
176    ) -> Format<'a> {
177        let a = &self.alloc;
178        match self.options.variant_formatting {
179            VariantFormatting::KeepAll => a.text(self.tokens_at(span)),
180            VariantFormatting::WithSuffix => a.text(td.to_string_with_suffix()),
181            VariantFormatting::WithoutSuffix => a.text(td.to_string()),
182        }
183    }
184
185    /// Formats a list into source code representation.
186    fn list_to_source_code(&'a self, elements: &'a List) -> Format<'a> {
187        self.wrap_collection(
188            elements
189                .items
190                .iter()
191                .map(|e| self.format_datex_expression(e)),
192            ("[", "]"),
193            ",",
194        )
195    }
196
197    /// Formats a string into source code representation.
198    fn text_to_source_code(&'a self, s: &'a str) -> Format<'a> {
199        self.alloc.text(format!("{:?}", s)) // quoted string
200    }
201
202    /// Formats a map into source code representation.
203    fn map_to_source_code(&'a self, map: &'a Map) -> Format<'a> {
204        let a = &self.alloc;
205        let entries = map.entries.iter().map(|(key, value)| {
206            self.format_datex_expression(key)
207                + a.text(":")
208                + (self.options.space_in_collection.then(|| a.space()))
209                + self.format_datex_expression(value)
210        });
211        self.wrap_collection(entries, ("{", "}"), ",")
212    }
213}