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 .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 .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}