shadowplay/puppet_pp_printer/
expression.rs

1use crate::puppet_pp_printer::Printer;
2use pretty::{Doc, RcDoc};
3
4pub fn infix_to_doc<'a>(
5    left: RcDoc<'a, ()>,
6    right: RcDoc<'a, ()>,
7    op: &'static str,
8) -> RcDoc<'a, ()> {
9    left.append(RcDoc::softline())
10        .append(RcDoc::text(op))
11        .append(RcDoc::space())
12        .append(right)
13        .group()
14}
15
16fn assigment_to_doc<'a>(left: RcDoc<'a, ()>, right: RcDoc<'a, ()>) -> RcDoc<'a, ()> {
17    left.append(RcDoc::line())
18        .append(
19            RcDoc::text("=")
20                .append(RcDoc::space())
21                .append(right)
22                .nest(2),
23        )
24        .group()
25        .nest(2)
26}
27
28impl<EXTRA> Printer for crate::puppet_lang::expression::Lambda<EXTRA> {
29    fn to_doc(&self) -> RcDoc<()> {
30        crate::puppet_pp_printer::argument::list_to_piped_doc(&self.args)
31            .append(RcDoc::softline())
32            .append(crate::puppet_pp_printer::statement::statement_block_to_doc(
33                &self.body, true,
34            ))
35    }
36}
37impl<EXTRA> Printer for crate::puppet_lang::expression::FunctionCall<EXTRA> {
38    fn to_doc(&self) -> RcDoc<()> {
39        let lambda = match &self.lambda {
40            Some(v) => RcDoc::softline().append(v.to_doc()),
41            None => RcDoc::nil(),
42        };
43
44        let parens = if self.args.is_empty() {
45            RcDoc::text("()")
46        } else {
47            RcDoc::text("(")
48                .append(RcDoc::softline_())
49                .append(RcDoc::intersperse(
50                    self.args
51                        .iter()
52                        .map(|x| crate::puppet_pp_printer::expression::to_doc(x, false)),
53                    RcDoc::text(",").append(Doc::line()),
54                ))
55                // .append(self.args.last_comment.to_doc())
56                .nest(2)
57                .append(RcDoc::softline_())
58                .group()
59                .append(RcDoc::text(")"))
60        };
61
62        self.identifier
63            .to_doc()
64            .append(parens)
65            .append(lambda)
66            .group()
67    }
68}
69
70impl<EXTRA> Printer for crate::puppet_lang::expression::ChainCall<EXTRA> {
71    fn to_doc(&self) -> RcDoc<()> {
72        to_doc(&self.left, false)
73            .append(RcDoc::softline_())
74            .append(RcDoc::text(".").append(self.right.to_doc()))
75            .group()
76    }
77}
78
79impl<EXTRA> Printer for crate::puppet_lang::expression::SelectorCase<EXTRA> {
80    fn to_doc(&self) -> RcDoc<()> {
81        let case = match &self.case {
82            crate::puppet_lang::expression::CaseVariant::Term(v) => {
83                crate::puppet_pp_printer::term::to_doc(v, false)
84            }
85            crate::puppet_lang::expression::CaseVariant::Default(_) => RcDoc::text("default"),
86        };
87
88        crate::puppet_pp_printer::comment::comment_or(
89            &self.comment,
90            RcDoc::hardline(),
91            RcDoc::nil(),
92        )
93        .append(case)
94        .append(RcDoc::softline())
95        .append(RcDoc::text("=>"))
96        .append(RcDoc::softline())
97        .append(to_doc(&self.body, false))
98        .group()
99    }
100}
101impl<EXTRA> Printer for crate::puppet_lang::expression::Selector<EXTRA> {
102    fn to_doc(&self) -> RcDoc<()> {
103        to_doc(&self.condition, false)
104            .append(RcDoc::softline())
105            .append(RcDoc::text("?"))
106            .append(RcDoc::space())
107            .append(RcDoc::text("{"))
108            .append(RcDoc::line())
109            .append(
110                RcDoc::intersperse(
111                    self.cases
112                        .value
113                        .iter()
114                        .map(|x| x.to_doc().append(RcDoc::text(","))),
115                    Doc::line(),
116                )
117                .group()
118                .append(crate::puppet_pp_printer::comment::comment_or(
119                    &self.cases.last_comment,
120                    RcDoc::hardline(),
121                    RcDoc::nil(),
122                )),
123            )
124            .nest(2)
125            .append(RcDoc::line())
126            .append(RcDoc::text("}"))
127            .group()
128    }
129}
130
131fn builtin_many1_to_doc<'a, EXTRA>(
132    name: &'a str,
133    elt: &'a crate::puppet_lang::builtin::Many1<EXTRA>,
134    with_parens: bool,
135) -> RcDoc<'a, ()> {
136    let args_list = RcDoc::intersperse(
137        elt.args
138            .iter()
139            .map(|x| crate::puppet_pp_printer::expression::to_doc(x, false)),
140        RcDoc::text(",").append(Doc::line()),
141    )
142    .group()
143    // .append(v.args.last_comment.to_doc())
144    .nest(2);
145
146    let lambda = match &elt.lambda {
147        Some(v) => RcDoc::softline().append(v.to_doc()),
148        None => RcDoc::nil(),
149    };
150
151    let args_list = match ((with_parens || elt.lambda.is_some()), elt.args.is_empty()) {
152        (_, true) => RcDoc::text("()"),
153        (true, false) => RcDoc::text("(")
154            .append(RcDoc::softline_())
155            .append(args_list)
156            .append(RcDoc::softline_())
157            .append(RcDoc::text(")")),
158        (false, false) => RcDoc::softline()
159            .append(args_list)
160            .append(RcDoc::softline_()),
161    };
162
163    RcDoc::text(name)
164        .append(RcDoc::softline_())
165        .append(args_list)
166        .append(lambda)
167}
168
169impl<EXTRA> Printer for crate::puppet_lang::builtin::BuiltinVariant<EXTRA> {
170    fn to_doc(&self) -> RcDoc<()> {
171        match self {
172            crate::puppet_lang::builtin::BuiltinVariant::Undef => RcDoc::text("undef"),
173            crate::puppet_lang::builtin::BuiltinVariant::Tag(v) => {
174                builtin_many1_to_doc("tag", v, false)
175            }
176            crate::puppet_lang::builtin::BuiltinVariant::Require(v) => {
177                builtin_many1_to_doc("require", v, false)
178            }
179            crate::puppet_lang::builtin::BuiltinVariant::Include(v) => {
180                builtin_many1_to_doc("include", v, false)
181            }
182            crate::puppet_lang::builtin::BuiltinVariant::Realize(v) => {
183                builtin_many1_to_doc("realize", v, true)
184            }
185            crate::puppet_lang::builtin::BuiltinVariant::CreateResources(v) => {
186                builtin_many1_to_doc("create_resources", v, true)
187            }
188            crate::puppet_lang::builtin::BuiltinVariant::Return(v) => match v.as_ref() {
189                None => RcDoc::text("return()"),
190                Some(v) => RcDoc::text("return")
191                    .append(RcDoc::text("("))
192                    .append(RcDoc::softline_())
193                    .append(crate::puppet_pp_printer::expression::to_doc(v, false))
194                    .nest(2)
195                    .append(RcDoc::softline_())
196                    .append(RcDoc::text(")")),
197            },
198            crate::puppet_lang::builtin::BuiltinVariant::Template(v) => RcDoc::text("template")
199                .append(RcDoc::text("("))
200                .append(RcDoc::softline_())
201                .append(crate::puppet_pp_printer::expression::to_doc(v, false))
202                .nest(2)
203                .append(RcDoc::softline_())
204                .append(RcDoc::text(")")),
205        }
206    }
207}
208pub fn to_doc<EXTRA>(
209    expr: &crate::puppet_lang::expression::Expression<EXTRA>,
210    hide_toplevel_variable_tag: bool,
211) -> RcDoc<()> {
212    let v = match &expr.value {
213        crate::puppet_lang::expression::ExpressionVariant::Term(v) => {
214            crate::puppet_pp_printer::term::to_doc(v, hide_toplevel_variable_tag)
215        }
216        crate::puppet_lang::expression::ExpressionVariant::Assign((left, right)) => {
217            assigment_to_doc(to_doc(left, false), to_doc(right, false))
218        }
219        crate::puppet_lang::expression::ExpressionVariant::And((left, right)) => {
220            infix_to_doc(to_doc(left, false), to_doc(right, false), "and")
221        }
222        crate::puppet_lang::expression::ExpressionVariant::Or((left, right)) => {
223            infix_to_doc(to_doc(left, false), to_doc(right, false), "or")
224        }
225        crate::puppet_lang::expression::ExpressionVariant::Equal((left, right)) => {
226            infix_to_doc(to_doc(left, false), to_doc(right, false), "==")
227        }
228        crate::puppet_lang::expression::ExpressionVariant::NotEqual((left, right)) => {
229            infix_to_doc(to_doc(left, false), to_doc(right, false), "!=")
230        }
231        crate::puppet_lang::expression::ExpressionVariant::Gt((left, right)) => {
232            infix_to_doc(to_doc(left, false), to_doc(right, false), ">")
233        }
234        crate::puppet_lang::expression::ExpressionVariant::GtEq((left, right)) => {
235            infix_to_doc(to_doc(left, false), to_doc(right, false), ">=")
236        }
237        crate::puppet_lang::expression::ExpressionVariant::Lt((left, right)) => {
238            infix_to_doc(to_doc(left, false), to_doc(right, false), "<")
239        }
240        crate::puppet_lang::expression::ExpressionVariant::LtEq((left, right)) => {
241            infix_to_doc(to_doc(left, false), to_doc(right, false), "<=")
242        }
243        crate::puppet_lang::expression::ExpressionVariant::ShiftLeft((left, right)) => {
244            infix_to_doc(to_doc(left, false), to_doc(right, false), "<<")
245        }
246        crate::puppet_lang::expression::ExpressionVariant::ShiftRight((left, right)) => {
247            infix_to_doc(to_doc(left, false), to_doc(right, false), ">>")
248        }
249        crate::puppet_lang::expression::ExpressionVariant::Plus((left, right)) => {
250            infix_to_doc(to_doc(left, false), to_doc(right, false), "+")
251        }
252        crate::puppet_lang::expression::ExpressionVariant::Minus((left, right)) => {
253            infix_to_doc(to_doc(left, false), to_doc(right, false), "-")
254        }
255        crate::puppet_lang::expression::ExpressionVariant::Multiply((left, right)) => {
256            infix_to_doc(to_doc(left, false), to_doc(right, false), "*")
257        }
258        crate::puppet_lang::expression::ExpressionVariant::Divide((left, right)) => {
259            infix_to_doc(to_doc(left, false), to_doc(right, false), "/")
260        }
261        crate::puppet_lang::expression::ExpressionVariant::Modulo((left, right)) => {
262            infix_to_doc(to_doc(left, false), to_doc(right, false), "%")
263        }
264        crate::puppet_lang::expression::ExpressionVariant::ChainCall(v) => v.to_doc(),
265        crate::puppet_lang::expression::ExpressionVariant::MatchRegex((left, right)) => {
266            infix_to_doc(
267                to_doc(left, false),
268                RcDoc::text("/")
269                    .append(&right.data)
270                    .append(RcDoc::text("/")),
271                "=~",
272            )
273        }
274        crate::puppet_lang::expression::ExpressionVariant::NotMatchRegex((left, right)) => {
275            infix_to_doc(
276                to_doc(left, false),
277                RcDoc::text("/")
278                    .append(&right.data)
279                    .append(RcDoc::text("/")),
280                "!~",
281            )
282        }
283        crate::puppet_lang::expression::ExpressionVariant::MatchType((left, right)) => {
284            infix_to_doc(to_doc(left, false), right.to_doc(), "=~")
285        }
286        crate::puppet_lang::expression::ExpressionVariant::NotMatchType((left, right)) => {
287            infix_to_doc(to_doc(left, false), right.to_doc(), "!~")
288        }
289        crate::puppet_lang::expression::ExpressionVariant::In((left, right)) => {
290            infix_to_doc(to_doc(left, false), to_doc(right, false), "in")
291        }
292        crate::puppet_lang::expression::ExpressionVariant::Not(v) => {
293            RcDoc::text("!").append(to_doc(v, false))
294        }
295        crate::puppet_lang::expression::ExpressionVariant::Selector(v) => v.to_doc(),
296        crate::puppet_lang::expression::ExpressionVariant::FunctionCall(v) => v.to_doc(),
297        crate::puppet_lang::expression::ExpressionVariant::BuiltinFunction(v) => v.to_doc(),
298    };
299
300    crate::puppet_pp_printer::comment::comment_or(&expr.comment, RcDoc::hardline(), RcDoc::nil())
301        .append(v)
302        .append(expr.accessor.to_doc())
303}
304
305#[test]
306fn test_idempotence_short() {
307    let cases = vec![
308        "123",
309        "4.0 + 5.1",
310        "'hello universe'",
311        "\"hello\n universe\"",
312        "\"hello ${universe}\"",
313        "\"hello ${::universe}\"",
314        "\"hello ${universe[\n    0\n  ]}\"",
315        "\"hello ${funcall()} suffix\"",
316        "\"hello ${funcall(\n  1,\n  2\n)} suffix\"",
317        "123.45 * 1\n- 2",
318        "123[1][\n    2, 3\n  ][4][5]",
319        "[\n  (123.45),\n  146,\n]",
320        "[\n  (\n    #comment\n    123.45),\n  146,\n]",
321        "[\n  (\n    #comment\n    123.45),\n  146,\n  #ending_comment\n]",
322        "!$a",
323        "/a/",
324        "/a\\d/",
325        "$z\n  = 11111111\n    and 2222",
326        "$z\n  = 11111111\n    + 2222\n    + 3333",
327        "1 + 1 + 1\n+ 1 + 1 + 1\n+ 1 + 1 + 1\n+ 1 + 1 + 1\n+ 1",
328        "(1 or 2)\nand (3 + 4\n  * 5)\nor (true\n  and (!true\n    and false))",
329        "$v.call1()\n.call2(1,\n  2)\n.call3withlongname()",
330        "fn(1, 2)\n|$a, $b| {\n  1\n}",
331        "$v ? {\n  1 => a,\n  \n  #comment\n  2 => b,\n  default\n  => c,\n}",
332        "undef",
333        "require\na::b, c",
334        "create_resources\n(1, 2)",
335        "realize(1,\n  2) |$a,\n  $b| {\n  1\n}",
336        "return()",
337        "return(\n  aaaaaaaaaaaaaaa\n)",
338    ];
339
340    for case in cases {
341        let (_, v) = crate::puppet_parser::expression::parse_expression(
342            crate::puppet_parser::Span::new(case),
343        )
344        .unwrap();
345
346        let mut w = Vec::new();
347        to_doc(&v, false).render(11, &mut w).unwrap();
348        let generated = String::from_utf8(w).unwrap();
349        println!("{} ==>\n------\n{}\n------", case, generated);
350
351        assert_eq!(&generated, case)
352    }
353}