1use std::fmt::Write;
2
3use crate::lua51::ast::*;
4
5pub fn emit_function(func: &Function) -> String {
7 let mut out = String::new();
8 let body = fold_elseif_chain(&func.body);
9 emit_block(&body, &mut out, 0);
10 out
11}
12
13pub fn emit_chunk(func: &Function) -> String {
15 let mut out = String::new();
16 let body = fold_elseif_chain(&func.body);
17 emit_block(&body, &mut out, 0);
18 while out.ends_with("\n\n") {
20 out.pop();
21 }
22 if !out.ends_with('\n') {
23 out.push('\n');
24 }
25 out
26}
27
28fn fold_elseif_chain(block: &Block) -> Block {
32 block.iter().map(|stat| fold_stat(stat)).collect()
33}
34
35fn fold_stat(stat: &Stat) -> Stat {
36 match stat {
37 Stat::If {
38 cond,
39 then_block,
40 elseif_clauses,
41 else_block,
42 } => {
43 let then_block = fold_elseif_chain(then_block);
44 let mut new_elseifs: Vec<(Expr, Block)> = elseif_clauses
45 .iter()
46 .map(|(c, b)| (c.clone(), fold_elseif_chain(b)))
47 .collect();
48
49 let new_else = if let Some(eb) = else_block {
51 let folded = fold_elseif_chain(eb);
52 if folded.len() == 1 {
53 if let Stat::If {
54 cond: inner_cond,
55 then_block: inner_then,
56 elseif_clauses: inner_elseifs,
57 else_block: inner_else,
58 } = &folded[0]
59 {
60 new_elseifs.push((inner_cond.clone(), inner_then.clone()));
62 new_elseifs.extend(inner_elseifs.iter().cloned());
63 inner_else.clone()
64 } else {
65 Some(folded)
66 }
67 } else {
68 Some(folded)
69 }
70 } else {
71 None
72 };
73
74 Stat::If {
75 cond: cond.clone(),
76 then_block,
77 elseif_clauses: new_elseifs,
78 else_block: new_else,
79 }
80 }
81 Stat::While { cond, body } => Stat::While {
82 cond: cond.clone(),
83 body: fold_elseif_chain(body),
84 },
85 Stat::Repeat { body, cond } => Stat::Repeat {
86 body: fold_elseif_chain(body),
87 cond: cond.clone(),
88 },
89 Stat::NumericFor { name, start, limit, step, body } => Stat::NumericFor {
90 name: name.clone(),
91 start: start.clone(),
92 limit: limit.clone(),
93 step: step.clone(),
94 body: fold_elseif_chain(body),
95 },
96 Stat::GenericFor { names, iterators, body } => Stat::GenericFor {
97 names: names.clone(),
98 iterators: iterators.clone(),
99 body: fold_elseif_chain(body),
100 },
101 Stat::DoBlock(body) => Stat::DoBlock(fold_elseif_chain(body)),
102 other => other.clone(),
103 }
104}
105
106fn emit_block(block: &Block, out: &mut String, indent: usize) {
107 for (i, stat) in block.iter().enumerate() {
108 if indent == 0 && i > 0 {
110 let prev = &block[i - 1];
111 if should_separate(prev, stat) {
112 out.push('\n');
113 }
114 }
115 emit_stat(stat, out, indent);
116 }
117}
118
119fn should_separate(prev: &Stat, curr: &Stat) -> bool {
121 if is_func_def(prev) {
123 return true;
124 }
125 if is_func_def(curr) {
127 return true;
128 }
129 false
132}
133
134fn is_func_def(stat: &Stat) -> bool {
135 match stat {
136 Stat::Assign { values, .. } => {
137 values.len() == 1 && matches!(&values[0], Expr::FunctionDef(_))
138 }
139 Stat::LocalAssign { exprs, .. } => {
140 exprs.len() == 1 && matches!(&exprs[0], Expr::FunctionDef(_))
141 }
142 _ => false,
143 }
144}
145
146fn emit_stat(stat: &Stat, out: &mut String, indent: usize) {
147 let pad = " ".repeat(indent);
148 match stat {
149 Stat::LocalAssign { names, exprs } => {
150 write!(out, "{}local {}", pad, names.join(", ")).unwrap();
151 if !exprs.is_empty() {
152 out.push_str(" = ");
153 emit_expr_list(exprs, out);
154 }
155 out.push('\n');
156 }
157 Stat::Assign { targets, values } => {
158 if targets.len() == 1 && values.len() == 1 {
160 if let Expr::FunctionDef(func) = &values[0] {
161 let name = match &targets[0] {
162 Expr::Global(n) => Some(n.clone()),
163 Expr::Name(n) => Some(n.clone()),
164 Expr::Field(table, field) => {
165 let mut s = String::new();
167 emit_expr(table, &mut s, 10);
168 s.push('.');
169 s.push_str(field);
170 Some(s)
171 }
172 _ => None,
173 };
174 if let Some(fname) = name {
175 write!(out, "{}function {}(", pad, fname).unwrap();
176 let mut params = func.params.join(", ");
177 if func.is_vararg {
178 if !params.is_empty() {
179 params.push_str(", ");
180 }
181 params.push_str("...");
182 }
183 out.push_str(¶ms);
184 out.push_str(")\n");
185 emit_block(&func.body, out, indent + 1);
186 writeln!(out, "{}end", pad).unwrap();
187 return;
188 }
189 }
190 }
191 write!(out, "{}", pad).unwrap();
192 emit_expr_list(targets, out);
193 out.push_str(" = ");
194 emit_expr_list(values, out);
195 out.push('\n');
196 }
197 Stat::Call(call) => {
198 write!(out, "{}", pad).unwrap();
199 emit_call(call, out);
200 out.push('\n');
201 }
202 Stat::DoBlock(body) => {
203 writeln!(out, "{}do", pad).unwrap();
204 emit_block(body, out, indent + 1);
205 writeln!(out, "{}end", pad).unwrap();
206 }
207 Stat::While { cond, body } => {
208 write!(out, "{}while ", pad).unwrap();
209 emit_expr(cond, out, 0);
210 out.push_str(" do\n");
211 emit_block(body, out, indent + 1);
212 writeln!(out, "{}end", pad).unwrap();
213 }
214 Stat::Repeat { body, cond } => {
215 writeln!(out, "{}repeat", pad).unwrap();
216 emit_block(body, out, indent + 1);
217 write!(out, "{}until ", pad).unwrap();
218 emit_expr(cond, out, 0);
219 out.push('\n');
220 }
221 Stat::If {
222 cond,
223 then_block,
224 elseif_clauses,
225 else_block,
226 } => {
227 write!(out, "{}if ", pad).unwrap();
228 emit_expr(cond, out, 0);
229 out.push_str(" then\n");
230 emit_block(then_block, out, indent + 1);
231 for (ec, eb) in elseif_clauses {
232 write!(out, "{}elseif ", pad).unwrap();
233 emit_expr(ec, out, 0);
234 out.push_str(" then\n");
235 emit_block(eb, out, indent + 1);
236 }
237 if let Some(eb) = else_block {
238 writeln!(out, "{}else", pad).unwrap();
239 emit_block(eb, out, indent + 1);
240 }
241 writeln!(out, "{}end", pad).unwrap();
242 }
243 Stat::NumericFor {
244 name,
245 start,
246 limit,
247 step,
248 body,
249 } => {
250 write!(out, "{}for {} = ", pad, name).unwrap();
251 emit_expr(start, out, 0);
252 out.push_str(", ");
253 emit_expr(limit, out, 0);
254 if let Some(s) = step {
255 out.push_str(", ");
256 emit_expr(s, out, 0);
257 }
258 out.push_str(" do\n");
259 emit_block(body, out, indent + 1);
260 writeln!(out, "{}end", pad).unwrap();
261 }
262 Stat::GenericFor {
263 names,
264 iterators,
265 body,
266 } => {
267 write!(out, "{}for {} in ", pad, names.join(", ")).unwrap();
268 emit_expr_list(iterators, out);
269 out.push_str(" do\n");
270 emit_block(body, out, indent + 1);
271 writeln!(out, "{}end", pad).unwrap();
272 }
273 Stat::Return(exprs) => {
274 write!(out, "{}return", pad).unwrap();
275 if !exprs.is_empty() {
276 out.push(' ');
277 emit_expr_list(exprs, out);
278 }
279 out.push('\n');
280 }
281 Stat::Break => {
282 writeln!(out, "{}break", pad).unwrap();
283 }
284 Stat::Comment(text) => {
285 writeln!(out, "{}-- {}", pad, text).unwrap();
286 }
287 }
288}
289
290fn emit_expr_list(exprs: &[Expr], out: &mut String) {
291 for (i, e) in exprs.iter().enumerate() {
292 if i > 0 {
293 out.push_str(", ");
294 }
295 emit_expr(e, out, 0);
296 }
297}
298
299fn emit_expr(expr: &Expr, out: &mut String, parent_prec: u8) {
302 match expr {
303 Expr::Nil => out.push_str("nil"),
304 Expr::Bool(true) => out.push_str("true"),
305 Expr::Bool(false) => out.push_str("false"),
306 Expr::Number(n) => emit_number(n, out),
307 Expr::StringLit(s) => emit_string(s, out),
308 Expr::VarArg => out.push_str("..."),
309 Expr::Name(n) => out.push_str(n),
310 Expr::Global(n) => out.push_str(n),
311 Expr::Register(r) => write!(out, "r{}", r).unwrap(),
312 Expr::Upvalue(u) => write!(out, "upval{}", u).unwrap(),
313 Expr::Index(table, key) => {
314 emit_expr(table, out, 10);
315 out.push('[');
316 emit_expr(key, out, 0);
317 out.push(']');
318 }
319 Expr::Field(table, field) => {
320 emit_expr(table, out, 10);
321 out.push('.');
322 out.push_str(field);
323 }
324 Expr::BinOp(op, lhs, rhs) => {
325 let prec = op.precedence();
326 let needs_parens = prec < parent_prec;
327 if needs_parens {
328 out.push('(');
329 }
330 emit_expr(lhs, out, prec);
331 write!(out, " {} ", op.symbol()).unwrap();
332 let rhs_prec = if op.is_right_assoc() { prec } else { prec + 1 };
334 emit_expr(rhs, out, rhs_prec);
335 if needs_parens {
336 out.push(')');
337 }
338 }
339 Expr::UnOp(op, operand) => {
340 let prec = op.precedence();
341 let needs_parens = prec < parent_prec;
342 if needs_parens {
343 out.push('(');
344 }
345 out.push_str(op.symbol());
346 emit_expr(operand, out, prec);
347 if needs_parens {
348 out.push(')');
349 }
350 }
351 Expr::FuncCall(call) => {
352 emit_call(call, out);
353 }
354 Expr::MethodCall(call) => {
355 emit_call(call, out);
356 }
357 Expr::FunctionDef(func) => {
358 emit_function_def(func, out, false);
359 }
360 Expr::Table(fields) => {
361 emit_table(fields, out);
362 }
363 }
364}
365
366fn emit_call(call: &CallExpr, out: &mut String) {
367 emit_expr(&call.func, out, 10);
368 out.push('(');
369 emit_expr_list(&call.args, out);
370 out.push(')');
371}
372
373fn emit_function_def(func: &Function, out: &mut String, _as_stat: bool) {
374 out.push_str("function(");
375 let mut params = func.params.join(", ");
376 if func.is_vararg {
377 if !params.is_empty() {
378 params.push_str(", ");
379 }
380 params.push_str("...");
381 }
382 out.push_str(¶ms);
383 out.push_str(")\n");
384
385 let current_indent = count_trailing_indent(out);
387 emit_block(&func.body, out, current_indent + 1);
388
389 let pad = " ".repeat(current_indent);
390 write!(out, "{}end", pad).unwrap();
391}
392
393fn emit_table(fields: &[TableField], out: &mut String) {
394 if fields.is_empty() {
395 out.push_str("{}");
396 return;
397 }
398
399 let current_indent = count_trailing_indent(out);
401 let inline = emit_table_inline(fields);
402 let use_multiline = inline.len() > 80 || fields.len() > 4 && inline.len() > 60;
404
405 if !use_multiline {
406 out.push_str(&inline);
407 return;
408 }
409
410 let inner_pad = " ".repeat(current_indent + 1);
412 let outer_pad = " ".repeat(current_indent);
413 out.push_str("{\n");
414 for (i, field) in fields.iter().enumerate() {
415 out.push_str(&inner_pad);
416 match field {
417 TableField::IndexField(key, val) => {
418 out.push('[');
419 emit_expr(key, out, 0);
420 out.push_str("] = ");
421 emit_expr(val, out, 0);
422 }
423 TableField::NameField(name, val) => {
424 out.push_str(name);
425 out.push_str(" = ");
426 emit_expr(val, out, 0);
427 }
428 TableField::Value(val) => {
429 emit_expr(val, out, 0);
430 }
431 }
432 if i + 1 < fields.len() {
433 out.push(',');
434 }
435 out.push('\n');
436 }
437 write!(out, "{}}}", outer_pad).unwrap();
438}
439
440fn emit_table_inline(fields: &[TableField]) -> String {
442 let mut s = String::new();
443 s.push('{');
444 for (i, field) in fields.iter().enumerate() {
445 if i > 0 {
446 s.push_str(", ");
447 }
448 match field {
449 TableField::IndexField(key, val) => {
450 s.push('[');
451 emit_expr(key, &mut s, 0);
452 s.push_str("] = ");
453 emit_expr(val, &mut s, 0);
454 }
455 TableField::NameField(name, val) => {
456 s.push_str(name);
457 s.push_str(" = ");
458 emit_expr(val, &mut s, 0);
459 }
460 TableField::Value(val) => {
461 emit_expr(val, &mut s, 0);
462 }
463 }
464 }
465 s.push('}');
466 s
467}
468
469fn emit_number(n: &NumLit, out: &mut String) {
470 match n {
471 NumLit::Int(v) => write!(out, "{}", v).unwrap(),
472 NumLit::Float(v) => {
473 if v.fract() == 0.0 && v.abs() < 1e15 {
474 write!(out, "{}", *v as i64).unwrap();
476 } else {
477 write!(out, "{}", v).unwrap();
478 }
479 }
480 }
481}
482
483fn emit_string(bytes: &[u8], out: &mut String) {
486 if let Ok(s) = std::str::from_utf8(bytes) {
488 emit_string_content(s, bytes, out);
489 return;
490 }
491
492 let (decoded, _, had_errors) = encoding_rs::GBK.decode(bytes);
494 if !had_errors {
495 emit_string_content(&decoded, bytes, out);
496 return;
497 }
498
499 out.push('"');
501 for &b in bytes {
502 emit_byte_escaped(b, out);
503 }
504 out.push('"');
505}
506
507fn emit_string_content(text: &str, raw: &[u8], out: &mut String) {
509 let has_newlines = text.contains('\n');
510 let has_long_bracket_close = text.contains("]]");
511 let is_printable = text.chars().all(|c| !c.is_control() || c == '\n' || c == '\r' || c == '\t');
512
513 if has_newlines && !has_long_bracket_close && is_printable {
514 out.push_str("[[");
516 if text.starts_with('\n') {
517 out.push('\n');
518 }
519 out.push_str(text);
520 out.push_str("]]");
521 return;
522 }
523
524 out.push('"');
526 for ch in text.chars() {
527 match ch {
528 '\\' => out.push_str("\\\\"),
529 '"' => out.push_str("\\\""),
530 '\n' => out.push_str("\\n"),
531 '\r' => out.push_str("\\r"),
532 '\t' => out.push_str("\\t"),
533 '\0' => out.push_str("\\0"),
534 '\x07' => out.push_str("\\a"),
535 '\x08' => out.push_str("\\b"),
536 '\x0C' => out.push_str("\\f"),
537 '\x0B' => out.push_str("\\v"),
538 c if c >= ' ' && c <= '~' => out.push(c),
539 c if !c.is_control() => out.push(c), c => {
541 let mut buf = [0u8; 4];
543 let s = c.encode_utf8(&mut buf);
544 for &b in s.as_bytes() {
545 write!(out, "\\{}", b).unwrap();
546 }
547 }
548 }
549 }
550 out.push('"');
551}
552
553fn emit_byte_escaped(b: u8, out: &mut String) {
554 match b {
555 b'\\' => out.push_str("\\\\"),
556 b'"' => out.push_str("\\\""),
557 b'\n' => out.push_str("\\n"),
558 b'\r' => out.push_str("\\r"),
559 b'\t' => out.push_str("\\t"),
560 b'\0' => out.push_str("\\0"),
561 0x07 => out.push_str("\\a"),
562 0x08 => out.push_str("\\b"),
563 0x0C => out.push_str("\\f"),
564 0x0B => out.push_str("\\v"),
565 0x20..=0x7E => out.push(b as char),
566 _ => {
567 write!(out, "\\{}", b).unwrap();
568 }
569 }
570}
571
572fn count_trailing_indent(s: &str) -> usize {
573 if let Some(last_nl) = s.rfind('\n') {
575 let after = &s[last_nl + 1..];
576 let spaces = after.len() - after.trim_start().len();
577 spaces / 2
578 } else {
579 0
580 }
581}