raw
1use itertools::Itertools as _;
2
3use super::ast::{BinOp, Expr, UnOp};
4
5pub fn ast_to_js(expr: &Expr<'_>) -> String {
6 inner(expr)
7}
8
9fn inner(expr: &Expr<'_>) -> String {
11 match expr {
12 Expr::Ident(ident) => ident.0.to_string(),
13 Expr::Bool(value) => value.to_string(),
14 Expr::Integer(value) => value.to_string(),
15 Expr::Decimal(value) => format!("{value:?}"),
16 Expr::String(value) => format!("{value:?}"),
17 Expr::Regex(value) => format!("/{value:?}/"),
18 Expr::Function(expr, params) => {
19 let mut expr = inner(&expr.inner);
20
21 if expr == "if" {
22 expr = "__if_expr__".to_string();
23 }
24
25 let params = params
26 .inner
27 .iter()
28 .map(|param| inner(¶m.inner))
29 .join(",");
30
31 format!("({expr})({params})")
32 }
33 Expr::Method(expr, name, params) => {
34 let expr = inner(&expr.inner);
35 let params = params
36 .inner
37 .iter()
38 .map(|param| inner(¶m.inner))
39 .join(",");
40
41 format!("({expr}).{}({params})", name.0)
42 }
43 Expr::Member(expr, name) => {
44 let expr = inner(&expr.inner);
45
46 format!("({expr}).{}", name.0)
47 }
48 Expr::Index(expr, index) => {
49 let expr = inner(&expr.inner);
50 let index = inner(&index.inner);
51
52 format!("({expr})[{index}]")
53 }
54 Expr::BinOp(op, lhs, rhs) => {
55 let lhs = inner(&lhs.inner);
56 let rhs = inner(&rhs.inner);
57
58 let sym = match op.inner {
59 BinOp::Add => "+",
60 BinOp::Sub => "-",
61 BinOp::Mul => "*",
62 BinOp::Div => "/",
63 BinOp::Mod => "%",
64 BinOp::Equal => "==",
65 BinOp::NotEqual => "!=",
66 BinOp::GreaterThan => ">",
67 BinOp::LessThan => "<",
68 BinOp::GreaterEqual => ">=",
69 BinOp::LessEqual => "<=",
70 BinOp::And => "&&",
71 BinOp::Or => "||",
72 };
73
74 format!("({lhs} {sym} {rhs})")
75 }
76 Expr::UnOp(op, expr) => {
77 let expr = inner(&expr.inner);
78
79 let sym = match op.inner {
80 UnOp::Not => "!",
81 UnOp::Plus => "+",
82 UnOp::Neg => "-",
83 };
84
85 format!("({sym}{expr})")
86 }
87 Expr::Group(expr) => format!("({})", inner(&expr.inner)),
88 }
89}
90
91#[allow(
92 clippy::too_many_lines,
93 clippy::cognitive_complexity,
94 reason = "theses are tests..."
95)]
96#[cfg(test)]
97mod tests {
98 use chumsky::span::{SimpleSpan, Spanned};
99
100 use super::super::ast::{BinOp, Expr, Ident, UnOp};
101
102 use super::ast_to_js;
103
104 fn s<T>(t: T) -> Spanned<T> {
105 Spanned {
106 span: SimpleSpan::from(0..1),
107 inner: t,
108 }
109 }
110
111 #[test]
112 fn simple() {
113 assert_eq!("test", ast_to_js(&Expr::Ident(Ident("test".into()))));
114
115 assert_eq!("true", ast_to_js(&Expr::Bool(true)));
116 assert_eq!("false", ast_to_js(&Expr::Bool(false)));
117
118 assert_eq!("0", ast_to_js(&Expr::Integer(0)));
119 assert_eq!("1", ast_to_js(&Expr::Integer(1)));
120
121 assert_eq!("0.0", ast_to_js(&Expr::Decimal(0.0)));
122 assert_eq!("1.0", ast_to_js(&Expr::Decimal(1.0)));
123 assert_eq!(
124 "1.000000000000001",
125 ast_to_js(&Expr::Decimal(1.000_000_000_000_001))
126 );
127
128 assert_eq!("\"test\\\"s\"", ast_to_js(&Expr::String("test\"s".into())));
129
130 assert_eq!(
131 "(test)()",
132 ast_to_js(&Expr::Function(
133 Box::new(s(Expr::Ident(Ident("test".into())))),
134 s(vec![]),
135 ))
136 );
137 assert_eq!(
138 "(test)(a)",
139 ast_to_js(&Expr::Function(
140 Box::new(s(Expr::Ident(Ident("test".into())))),
141 s(vec![s(Expr::Ident(Ident("a".into())))]),
142 ))
143 );
144 assert_eq!(
145 "(test)(a,b)",
146 ast_to_js(&Expr::Function(
147 Box::new(s(Expr::Ident(Ident("test".into())))),
148 s(vec![
149 s(Expr::Ident(Ident("a".into()))),
150 s(Expr::Ident(Ident("b".into()))),
151 ]),
152 ))
153 );
154
155 assert_eq!(
156 "(test).test()",
157 ast_to_js(&Expr::Method(
158 Box::new(s(Expr::Ident(Ident("test".into())))),
159 s(Ident("test".into())),
160 s(vec![]),
161 ))
162 );
163 assert_eq!(
164 "(test).test(a)",
165 ast_to_js(&Expr::Method(
166 Box::new(s(Expr::Ident(Ident("test".into())))),
167 s(Ident("test".into())),
168 s(vec![s(Expr::Ident(Ident("a".into())))]),
169 ))
170 );
171 assert_eq!(
172 "(test).test(a,b)",
173 ast_to_js(&Expr::Method(
174 Box::new(s(Expr::Ident(Ident("test".into())))),
175 s(Ident("test".into())),
176 s(vec![
177 s(Expr::Ident(Ident("a".into()))),
178 s(Expr::Ident(Ident("b".into()))),
179 ]),
180 ))
181 );
182
183 assert_eq!(
184 "(test).test",
185 ast_to_js(&Expr::Member(
186 Box::new(s(Expr::Ident(Ident("test".into())))),
187 s(Ident("test".into())),
188 ))
189 );
190
191 assert_eq!(
192 "(test)[1]",
193 ast_to_js(&Expr::Index(
194 Box::new(s(Expr::Ident(Ident("test".into())))),
195 Box::new(s(Expr::Integer(1))),
196 ))
197 );
198
199 assert_eq!(
200 "(a + b)",
201 ast_to_js(&Expr::BinOp(
202 s(BinOp::Add),
203 Box::new(s(Expr::Ident(Ident("a".into())))),
204 Box::new(s(Expr::Ident(Ident("b".into())))),
205 ))
206 );
207 assert_eq!(
208 "(a - b)",
209 ast_to_js(&Expr::BinOp(
210 s(BinOp::Sub),
211 Box::new(s(Expr::Ident(Ident("a".into())))),
212 Box::new(s(Expr::Ident(Ident("b".into())))),
213 ))
214 );
215 assert_eq!(
216 "(a * b)",
217 ast_to_js(&Expr::BinOp(
218 s(BinOp::Mul),
219 Box::new(s(Expr::Ident(Ident("a".into())))),
220 Box::new(s(Expr::Ident(Ident("b".into())))),
221 ))
222 );
223 assert_eq!(
224 "(a / b)",
225 ast_to_js(&Expr::BinOp(
226 s(BinOp::Div),
227 Box::new(s(Expr::Ident(Ident("a".into())))),
228 Box::new(s(Expr::Ident(Ident("b".into())))),
229 ))
230 );
231 assert_eq!(
232 "(a % b)",
233 ast_to_js(&Expr::BinOp(
234 s(BinOp::Mod),
235 Box::new(s(Expr::Ident(Ident("a".into())))),
236 Box::new(s(Expr::Ident(Ident("b".into())))),
237 ))
238 );
239 assert_eq!(
240 "(a == b)",
241 ast_to_js(&Expr::BinOp(
242 s(BinOp::Equal),
243 Box::new(s(Expr::Ident(Ident("a".into())))),
244 Box::new(s(Expr::Ident(Ident("b".into())))),
245 ))
246 );
247 assert_eq!(
248 "(a != b)",
249 ast_to_js(&Expr::BinOp(
250 s(BinOp::NotEqual),
251 Box::new(s(Expr::Ident(Ident("a".into())))),
252 Box::new(s(Expr::Ident(Ident("b".into())))),
253 ))
254 );
255 assert_eq!(
256 "(a > b)",
257 ast_to_js(&Expr::BinOp(
258 s(BinOp::GreaterThan),
259 Box::new(s(Expr::Ident(Ident("a".into())))),
260 Box::new(s(Expr::Ident(Ident("b".into())))),
261 ))
262 );
263 assert_eq!(
264 "(a < b)",
265 ast_to_js(&Expr::BinOp(
266 s(BinOp::LessThan),
267 Box::new(s(Expr::Ident(Ident("a".into())))),
268 Box::new(s(Expr::Ident(Ident("b".into())))),
269 ))
270 );
271 assert_eq!(
272 "(a >= b)",
273 ast_to_js(&Expr::BinOp(
274 s(BinOp::GreaterEqual),
275 Box::new(s(Expr::Ident(Ident("a".into())))),
276 Box::new(s(Expr::Ident(Ident("b".into())))),
277 ))
278 );
279 assert_eq!(
280 "(a <= b)",
281 ast_to_js(&Expr::BinOp(
282 s(BinOp::LessEqual),
283 Box::new(s(Expr::Ident(Ident("a".into())))),
284 Box::new(s(Expr::Ident(Ident("b".into())))),
285 ))
286 );
287 assert_eq!(
288 "(a && b)",
289 ast_to_js(&Expr::BinOp(
290 s(BinOp::And),
291 Box::new(s(Expr::Ident(Ident("a".into())))),
292 Box::new(s(Expr::Ident(Ident("b".into())))),
293 ))
294 );
295 assert_eq!(
296 "(a || b)",
297 ast_to_js(&Expr::BinOp(
298 s(BinOp::Or),
299 Box::new(s(Expr::Ident(Ident("a".into())))),
300 Box::new(s(Expr::Ident(Ident("b".into())))),
301 ))
302 );
303
304 assert_eq!(
305 "(!a)",
306 ast_to_js(&Expr::UnOp(
307 s(UnOp::Not),
308 Box::new(s(Expr::Ident(Ident("a".into())))),
309 ))
310 );
311 assert_eq!(
312 "(+a)",
313 ast_to_js(&Expr::UnOp(
314 s(UnOp::Plus),
315 Box::new(s(Expr::Ident(Ident("a".into())))),
316 ))
317 );
318 assert_eq!(
319 "(-a)",
320 ast_to_js(&Expr::UnOp(
321 s(UnOp::Neg),
322 Box::new(s(Expr::Ident(Ident("a".into())))),
323 ))
324 );
325
326 assert_eq!(
327 "(a)",
328 ast_to_js(&Expr::Group(Box::new(s(Expr::Ident(Ident("a".into())))),))
329 );
330 }
331}
332