1use std::fmt;
2
3use super::ssa::{Var, VarId};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum UnOp {
7 Neg,
8 Not,
9 BitNot,
10 Typeof,
11 Delete,
12}
13
14impl UnOp {
15 pub fn op_str(self) -> &'static str {
16 match self {
17 UnOp::Neg => "-",
18 UnOp::Not => "!",
19 UnOp::BitNot => "~",
20 UnOp::Typeof => "typeof",
21 UnOp::Delete => "delete",
22 }
23 }
24
25 pub fn needs_space(self) -> bool {
26 matches!(self, UnOp::Typeof | UnOp::Delete)
27 }
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub enum BinOp {
32 Add,
33 Sub,
34 Mul,
35 Div,
36 Mod,
37
38 Shl,
39 Shr,
40 UShr,
41
42 Lt,
43 Le,
44 Gt,
45 Ge,
46
47 In,
48
49 Eq,
50 Ne,
51 StrictEq,
52 StrictNe,
53
54 BitAnd,
55 BitOr,
56 BitXor,
57
58 LogAnd,
59 LogOr,
60
61 Assign,
62
63 AddAssign,
64 SubAssign,
65 MulAssign,
66 DivAssign,
67 ModAssign,
68 ShlAssign,
69 ShrAssign,
70 UShrAssign,
71 AndAssign,
72 OrAssign,
73 XorAssign,
74}
75
76impl BinOp {
77 pub fn op_str(self) -> &'static str {
78 match self {
79 BinOp::Add => "+",
80 BinOp::Sub => "-",
81 BinOp::Mul => "*",
82 BinOp::Div => "/",
83 BinOp::Mod => "%",
84
85 BinOp::Shl => "<<",
86 BinOp::Shr => ">>",
87 BinOp::UShr => ">>>",
88
89 BinOp::Lt => "<",
90 BinOp::Le => "<=",
91 BinOp::Gt => ">",
92 BinOp::Ge => ">=",
93
94 BinOp::In => "in",
95
96 BinOp::Eq => "==",
97 BinOp::Ne => "!=",
98 BinOp::StrictEq => "===",
99 BinOp::StrictNe => "!==",
100
101 BinOp::BitAnd => "&",
102 BinOp::BitOr => "|",
103 BinOp::BitXor => "^",
104
105 BinOp::LogAnd => "&&",
106 BinOp::LogOr => "||",
107
108 BinOp::Assign => "=",
109
110 BinOp::AddAssign => "+=",
111 BinOp::SubAssign => "-=",
112 BinOp::MulAssign => "*=",
113 BinOp::DivAssign => "/=",
114 BinOp::ModAssign => "%=",
115 BinOp::ShlAssign => "<<=",
116 BinOp::ShrAssign => ">>=",
117 BinOp::UShrAssign => ">>>=",
118 BinOp::AndAssign => "&=",
119 BinOp::OrAssign => "|=",
120 BinOp::XorAssign => "^=",
121 }
122 }
123
124 pub fn precedence(self) -> u8 {
126 match self {
127 BinOp::Assign
128 | BinOp::AddAssign
129 | BinOp::SubAssign
130 | BinOp::MulAssign
131 | BinOp::DivAssign
132 | BinOp::ModAssign
133 | BinOp::ShlAssign
134 | BinOp::ShrAssign
135 | BinOp::UShrAssign
136 | BinOp::AndAssign
137 | BinOp::OrAssign
138 | BinOp::XorAssign => 0,
139
140 BinOp::LogOr => 1,
141 BinOp::LogAnd => 2,
142
143 BinOp::BitOr => 3,
144 BinOp::BitXor => 4,
145 BinOp::BitAnd => 5,
146
147 BinOp::Eq | BinOp::Ne | BinOp::StrictEq | BinOp::StrictNe => 6,
148
149 BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge | BinOp::In => 7,
150
151 BinOp::Shl | BinOp::Shr | BinOp::UShr => 8,
152
153 BinOp::Add | BinOp::Sub => 9,
154
155 BinOp::Mul | BinOp::Div | BinOp::Mod => 10,
156 }
157 }
158
159 pub fn tightens_right(self) -> bool {
160 matches!(
161 self,
162 BinOp::Assign
163 | BinOp::AddAssign
164 | BinOp::SubAssign
165 | BinOp::MulAssign
166 | BinOp::DivAssign
167 | BinOp::ModAssign
168 | BinOp::ShlAssign
169 | BinOp::ShrAssign
170 | BinOp::UShrAssign
171 | BinOp::AndAssign
172 | BinOp::OrAssign
173 | BinOp::XorAssign
174 )
175 }
176}
177
178#[derive(Debug, Clone)]
179pub enum Expr {
180 Reg(i32),
182 Flag,
183 ConstData(i32),
184
185 SsaVar(VarId),
187
188 Void,
189 Null,
190 Bool(bool),
191 Int(i64),
192 Real(f64),
193 Str(String),
194 Octet(Vec<u8>),
195
196 Unary(UnOp, Box<Expr>),
197 Deref(Box<Expr>),
198
199 Binary(BinOp, Box<Expr>, Box<Expr>),
200
201 Call(Box<Expr>, Vec<Expr>),
202 New(Box<Expr>, Vec<Expr>),
203
204 Index(Box<Expr>, Box<Expr>),
205 Member(Box<Expr>, String),
206
207 MethodCall {
208 base: Box<Expr>,
209 member: String,
210 args: Vec<Expr>,
211 },
212
213 Opaque(String, Vec<Expr>),
214}
215
216impl Expr {
217 pub fn to_tjs(&self) -> String {
218 self.to_tjs_with(&fmt_vid_default)
219 }
220
221 pub fn to_tjs_with(&self, fmt_var: &dyn Fn(VarId) -> String) -> String {
222 let mut out = String::new();
223 self.fmt_with_prec(&mut out, 0, fmt_var)
224 .expect("fmt never fails for String");
225 out
226 }
227
228 fn precedence(&self) -> u8 {
229 match self {
230 Expr::Reg(_)
231 | Expr::Flag
232 | Expr::ConstData(_)
233 | Expr::SsaVar(_)
234 | Expr::Void
235 | Expr::Null
236 | Expr::Bool(_)
237 | Expr::Int(_)
238 | Expr::Real(_)
239 | Expr::Str(_)
240 | Expr::Octet(_) => 20,
241
242 Expr::Member(..) | Expr::Index(..) | Expr::Call(..) | Expr::MethodCall { .. } => 19,
243
244 Expr::New(..) => 18,
245
246 Expr::Unary(..) | Expr::Deref(..) => 17,
247
248 Expr::Binary(op, ..) => op.precedence(),
249 Expr::Opaque(_, _) => 1,
250 }
251 }
252
253 fn fmt_with_prec(
254 &self,
255 f: &mut dyn fmt::Write,
256 outer_prec: u8,
257 fmt_var: &dyn Fn(VarId) -> String,
258 ) -> fmt::Result {
259 let my_prec = self.precedence();
260 let needs_paren = my_prec < outer_prec;
261
262 if needs_paren {
263 write!(f, "(")?;
264 }
265
266 match self {
267 Expr::Reg(r) => {
268 if *r <= -3 {
270 write!(f, "a{}", -3 - r)?
271 } else if *r == -2 {
272 write!(f, "global")?
273 } else if *r == -1 {
274 write!(f, "this")?
275 } else {
276 write!(f, "r{}", r)?
277 }
278 }
279 Expr::Flag => write!(f, "flag")?,
280 Expr::ConstData(i) => write!(f, "__d[{}]", i)?,
281
282 Expr::SsaVar(v) => write!(f, "{}", fmt_var(*v))?,
283
284 Expr::Void => write!(f, "void")?,
285 Expr::Null => write!(f, "null")?,
286 Expr::Bool(b) => write!(f, "{}", if *b { "true" } else { "false" })?,
287 Expr::Int(i) => write!(f, "{}", i)?,
288 Expr::Real(x) => write!(f, "{}", x)?,
289 Expr::Str(s) => write!(f, "\"{}\"", escape_tjs_string(s))?,
290 Expr::Octet(bytes) => {
291 if bytes.is_empty() {
292 write!(f, "octet(0)")?;
293 } else {
294 let mut preview = String::new();
295 let n = bytes.len().min(16);
296 for (i, b) in bytes.iter().take(n).enumerate() {
297 if i != 0 {
298 preview.push(' ');
299 }
300 preview.push_str(&format!("{:02x}", b));
301 }
302 if bytes.len() > n {
303 write!(f, "octet(len={}, head={})", bytes.len(), preview)?;
304 } else {
305 write!(f, "octet(len={}, bytes={})", bytes.len(), preview)?;
306 }
307 }
308 }
309
310 Expr::Unary(op, e) => {
311 if op.needs_space() {
312 write!(f, "{} ", op.op_str())?;
313 } else {
314 write!(f, "{}", op.op_str())?;
315 }
316 e.fmt_with_prec(f, my_prec, fmt_var)?;
317 }
318
319 Expr::Deref(e) => {
320 write!(f, "*")?;
321 e.fmt_with_prec(f, my_prec, fmt_var)?;
322 }
323
324 Expr::Binary(op, l, r) => {
325 let lp = op.precedence();
326 let rp = if op.tightens_right() { lp } else { lp + 1 };
327 l.fmt_with_prec(f, lp, fmt_var)?;
328 write!(f, " {} ", op.op_str())?;
329 r.fmt_with_prec(f, rp, fmt_var)?;
330 }
331
332 Expr::Call(callee, args) => {
333 callee.fmt_with_prec(f, 19, fmt_var)?;
334 write!(f, "(")?;
335 for (i, a) in args.iter().enumerate() {
336 if i != 0 {
337 write!(f, ", ")?;
338 }
339 a.fmt_with_prec(f, 0, fmt_var)?;
340 }
341 write!(f, ")")?;
342 }
343
344 Expr::New(callee, args) => {
345 write!(f, "new ")?;
346 callee.fmt_with_prec(f, 19, fmt_var)?;
347 write!(f, "(")?;
348 for (i, a) in args.iter().enumerate() {
349 if i != 0 {
350 write!(f, ", ")?;
351 }
352 a.fmt_with_prec(f, 0, fmt_var)?;
353 }
354 write!(f, ")")?;
355 }
356
357 Expr::Index(base, key) => {
358 base.fmt_with_prec(f, 19, fmt_var)?;
359 write!(f, "[")?;
360 key.fmt_with_prec(f, 0, fmt_var)?;
361 write!(f, "]")?;
362 }
363
364 Expr::Member(base, member) => {
365 base.fmt_with_prec(f, 19, fmt_var)?;
366 write!(f, ".{}", member)?;
367 }
368
369 Expr::MethodCall { base, member, args } => {
370 base.fmt_with_prec(f, 19, fmt_var)?;
371 write!(f, ".{}(", member)?;
372 for (i, a) in args.iter().enumerate() {
373 if i != 0 {
374 write!(f, ", ")?;
375 }
376 a.fmt_with_prec(f, 0, fmt_var)?;
377 }
378 write!(f, ")")?;
379 }
380
381 Expr::Opaque(name, args) => {
382 if args.is_empty() {
383 write!(f, "{}", name)?;
384 } else {
385 write!(f, "{}(", name)?;
386 for (i, a) in args.iter().enumerate() {
387 if i != 0 {
388 write!(f, ", ")?;
389 }
390 a.fmt_with_prec(f, 0, fmt_var)?;
391 }
392 write!(f, ")")?;
393 }
394 }
395 }
396
397 if needs_paren {
398 write!(f, ")")?;
399 }
400 Ok(())
401 }
402}
403
404impl fmt::Display for Expr {
405 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
406 write!(f, "{}", self.to_tjs())
407 }
408}
409
410fn fmt_vid_default(vid: VarId) -> String {
411 match vid.var {
412 Var::Reg(r) => {
413 if r <= -3 {
414 format!("a{}", -3 - r)
415 } else if r == -2 {
416 "global".to_string()
417 } else if r == -1 {
418 "this".to_string()
419 } else {
420 format!("r{}#{}", r, vid.ver)
421 }
422 }
423 Var::Flag => format!("flag#{}", vid.ver),
424 Var::Exception => format!("exc#{}", vid.ver),
425 }
426}
427
428pub fn escape_tjs_string(s: &str) -> String {
429 let mut out = String::new();
430 for c in s.chars() {
431 match c {
432 '\\' => out.push_str("\\\\"),
433 '"' => out.push_str("\\\""),
434 '\n' => out.push_str("\\n"),
435 '\r' => out.push_str("\\r"),
436 '\t' => out.push_str("\\t"),
437 '\0' => out.push_str("\\0"),
438 _ => out.push(c),
439 }
440 }
441 out
442}