1use std::fmt;
2use std::fmt::{Display, Formatter};
3
4use hlbc::fmt::{BytecodeFmt, EnhancedFmt};
5use hlbc::types::{Function, RefField, Type};
6use hlbc::Str;
7use hlbc::{Bytecode, Resolve};
8
9use crate::ast::{Class, Constant, ConstructorCall, Expr, Method, Operation, Statement};
10
11const INDENT: &'static str = " ";
12
13#[derive(Clone)]
14pub struct FormatOptions {
15 indent: &'static str,
16 inc_indent: usize,
17}
18
19impl FormatOptions {
20 pub fn new(inc_indent: usize) -> Self {
21 Self {
22 indent: "",
23 inc_indent,
24 }
25 }
26
27 pub fn inc_nesting(&self) -> Self {
28 FormatOptions {
29 indent: &INDENT[..self.indent.len() + self.inc_indent],
30 ..*self
31 }
32 }
33}
34
35impl Display for FormatOptions {
36 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
37 write!(f, "{}", self.indent)
38 }
39}
40
41fn to_haxe_type<'a>(ty: &Type, ctx: &'a Bytecode) -> impl Display + 'a {
42 use crate::Type::*;
43 match ty {
44 Void => Str::from_static("Void"),
45 I32 => Str::from_static("Int"),
46 F64 => Str::from_static("Float"),
47 Bool => Str::from_static("Bool"),
48 Bytes => Str::from_static("hl.Bytes"),
49 Dyn => Str::from_static("Dynamic"),
50 Fun(_) => Str::from_static("Function"),
51 Obj(obj) => ctx.get(obj.name),
52 _ => Str::from_static("other"),
53 }
54}
55
56impl Class {
57 pub fn display<'a>(&'a self, ctx: &'a Bytecode, opts: &'a FormatOptions) -> impl Display + 'a {
58 let new_opts = opts.inc_nesting();
59 fmtools::fmt! { move
60 {opts}"class "{self.name} if let Some(parent) = self.parent.as_ref() { " extends "{parent} } " {\n"
61 for f in &self.fields {
62 {new_opts} if f.static_ { "static " } "var "{f.name}": "{to_haxe_type(&ctx[f.ty], ctx)}";\n"
63 }
64 for m in &self.methods {
65 "\n"
66 {m.display(ctx, &new_opts)}
67 }
68 {opts}"}"
69 }
70 }
71}
72
73impl Method {
74 pub fn display<'a>(&'a self, ctx: &'a Bytecode, opts: &'a FormatOptions) -> impl Display + 'a {
75 let new_opts = opts.inc_nesting();
76 let fun = self.fun.as_fn(ctx).unwrap();
77 fmtools::fmt! { move
78 {opts} if self.static_ { "static " } if self.dynamic { "dynamic " }
79 "function "{fun.name(ctx)}"("
80 {fmtools::join(", ", fun.args(ctx).iter().enumerate().skip(if self.static_ { 0 } else { 1 })
81 .map(move |(i, arg)| fmtools::fmt! {move
82 {fun.arg_name(ctx, i).unwrap_or(Str::from("_"))}": "{to_haxe_type(&ctx[*arg], ctx)}
83 }))}
84 ")" if !fun.ty(ctx).ret.is_void() { ": "{to_haxe_type(fun.ret(ctx), ctx)} } " {"
85
86 if self.statements.is_empty() {
87 "}"
88 } else {
89 "\n"
90 for stmt in &self.statements {
91 {new_opts}{stmt.display(&new_opts, ctx, fun)}"\n"
92 }
93 {opts}"}"
94 }
95 "\n"
96 }
97 }
98}
99
100impl Constant {
101 fn fmt(&self, f: &mut Formatter, code: &Bytecode) -> fmt::Result {
102 use Constant::*;
103 match *self {
104 InlineInt(c) => Display::fmt(&c, f),
105 Int(c) => EnhancedFmt.fmt_refint(f, code, c),
106 Float(c) => EnhancedFmt.fmt_reffloat(f, code, c),
107 String(c) => {
108 write!(f, "\"{}\"", code[c])
109 }
110 Bool(c) => Display::fmt(&c, f),
111 Null => f.write_str("null"),
112 This => f.write_str("this"),
113 }
114 }
115}
116
117impl Operation {
118 pub fn display<'a>(
119 &'a self,
120 indent: &'a FormatOptions,
121 code: &'a Bytecode,
122 f: &'a Function,
123 ) -> impl Display + 'a {
124 use Operation::*;
125 macro_rules! disp {
126 ($e:ident) => {
127 $e.display(indent, code, f)
128 };
129 }
130 fmtools::fmt! { move
131 match self {
132 Add(e1, e2) => {{disp!(e1)}" + "{disp!(e2)}}
133 Sub(e1, e2) => {{disp!(e1)}" - "{disp!(e2)}}
134 Mul(e1, e2) => {{disp!(e1)}" * "{disp!(e2)}}
135 Div(e1, e2) => {{disp!(e1)}" / "{disp!(e2)}}
136 Mod(e1, e2) => {{disp!(e1)}" % "{disp!(e2)}}
137 Shl(e1, e2) => {{disp!(e1)}" << "{disp!(e2)}}
138 Shr(e1, e2) => {{disp!(e1)}" >> "{disp!(e2)}}
139 And(e1, e2) => {{disp!(e1)}" && "{disp!(e2)}}
140 Or(e1, e2) => {{disp!(e1)}" || "{disp!(e2)}}
141 Xor(e1, e2) => {{disp!(e1)}" ^ "{disp!(e2)}}
142 Neg(expr) => {"-"{disp!(expr)}}
143 Not(expr) => {"!"{disp!(expr)}}
144 Incr(expr) => {{disp!(expr)}"++"}
145 Decr(expr) => {{disp!(expr)}"--"}
146 Eq(e1, e2) => {{disp!(e1)}" == "{disp!(e2)}}
147 NotEq(e1, e2) => {{disp!(e1)}" != "{disp!(e2)}}
148 Gt(e1, e2) => {{disp!(e1)}" > "{disp!(e2)}}
149 Gte(e1, e2) => {{disp!(e1)}" >= "{disp!(e2)}}
150 Lt(e1, e2) => {{disp!(e1)}" < "{disp!(e2)}}
151 Lte(e1, e2) => {{disp!(e1)}" <= "{disp!(e2)}}
152 }
153 }
154 }
155}
156
157impl Expr {
158 pub fn display<'a>(
159 &'a self,
160 indent: &'a FormatOptions,
161 code: &'a Bytecode,
162 f: &'a Function,
163 ) -> impl Display + 'a {
164 macro_rules! disp {
165 ($e:expr) => {
166 $e.display(indent, code, f)
167 };
168 }
169 fmtools::fmt! { move
170 match self {
171 Expr::Anonymous(ty, values) => match &code[*ty] {
172 Type::Virtual { fields } => {
173 "{"{ fmtools::join(", ", fields
174 .iter()
175 .enumerate()
176 .map(|(i, f)| {
177 fmtools::fmt! { move
178 {f.name(code)}": "{disp!(values.get(&RefField(i)).unwrap())}
179 }
180 })) }"}"
181 }
182 _ => "[invalid anonymous type]",
183 },
184 Expr::Array(array, index) => {
185 {disp!(array)}"["{disp!(index)}"]"
186 }
187 Expr::Call(call) => {
188 {disp!(call.fun)}"("{fmtools::join(", ", call.args.iter().map(|e| disp!(e)))}")"
189 }
190 Expr::Constant(c) => {|f| c.fmt(f, code)?;},
191 Expr::Constructor(ConstructorCall { ty, args }) => {
192 "new "{ty.display::<EnhancedFmt>(code)}"("{fmtools::join(", ", args.iter().map(|e| disp!(e)))}")"
193 }
194 Expr::Closure(f, stmts) => {
195 let fun = f.as_fn(code).unwrap();
196 "("{fmtools::join(", ", fun.ty(code).args.iter().enumerate().map(move |(i, arg)|
197 fmtools::fmt! { move
198 {fun.arg_name(code, i).unwrap_or(Str::from("_"))}": "{to_haxe_type(&code[*arg], code)}
199 }
200 ))}") -> {\n"
201 let indent2 = indent.inc_nesting();
202 for stmt in stmts {
203 {indent2}{stmt.display(&indent2, code, fun)}"\n"
204 }
205 {indent}"}"
206 }
207 Expr::EnumConstr(ty, constr, args) => {
208 {constr.display::<EnhancedFmt>(code, &code[*ty])}"("{fmtools::join(", ", args.iter().map(|e| disp!(e)))}")"
209 }
210 Expr::Field(receiver, name) => {
211 {disp!(receiver)}"."{name}
212 }
213 Expr::FunRef(fun) => {{fun.name(code)}},
214 Expr::IfElse { cond, if_, else_ } => {
215 "if ("{disp!(cond)}") {\n"
216 let indent2 = indent.inc_nesting();
217 for stmt in if_ {
218 {indent2}{stmt.display(&indent2, code, f)}"\n"
219 }
220 {indent}"} else {\n"
221 for stmt in else_ {
222 {indent2}{stmt.display(&indent2, code, f)}"\n"
223 }
224 {indent}"}"
225 }
226 Expr::Op(op) => {{disp!(op)}},
227 Expr::Unknown(msg) => {
228 "["{msg}"]"
229 }
230 Expr::Variable(x, name) => {{
231 if let Some(name) = name {
232 name.clone()
233 } else {
234 Str::from(x.to_string())
235 }
236 }}
237 }
238 }
239 }
240}
241
242impl Statement {
243 pub fn display<'a>(
244 &'a self,
245 indent: &'a FormatOptions,
246 code: &'a Bytecode,
247 f: &'a Function,
248 ) -> impl Display + 'a {
249 macro_rules! disp {
250 ($e:expr) => {
251 $e.display(indent, code, f)
252 };
253 }
254 fmtools::fmt! { move
255 match self {
256 Statement::Assign {
257 declaration,
258 variable,
259 assign,
260 } => {
261 if *declaration { "var " } else { "" }{disp!(variable)}" = "{disp!(assign)}";"
262 }
263 Statement::ExprStatement(expr) => {
264 {disp!(expr)}";"
265 }
266 Statement::Return(expr) => {
267 "return" if let Some(e) = expr { " "{disp!(e)} } ";"
268 }
269 Statement::IfElse { cond, if_, else_ } => {
270 "if ("{disp!(cond)}") {\n"
271 let indent2 = indent.inc_nesting();
272 for stmt in if_ {
273 {indent2}{stmt.display(&indent2, code, f)}"\n"
274 }
275 {indent}"}"
276 if !else_.is_empty() {
277 " else {\n"
278 for stmt in else_ {
279 {indent2}{stmt.display(&indent2, code, f)}"\n"
280 }
281 {indent}"}"
282 }
283 }
284 Statement::Switch {arg, default, cases} => {
285 "switch ("{disp!(arg)}") {\n"
286 let indent2 = indent.inc_nesting();
287 let indent3 = indent2.inc_nesting();
288 if !default.is_empty() {
289 {indent2}"default:\n"
290 for stmt in default {
291 {indent3}{stmt.display(&indent3, code, f)}"\n"
292 }
293 }
294 for (pattern, stmts) in cases {
295 {indent2}"case "{disp!(pattern)}":\n"
296 for stmt in stmts {
297 {indent3}{stmt.display(&indent3, code, f)}"\n"
298 }
299 }
300 {indent}"}"
301 }
302 Statement::While { cond, stmts } => {
303 "while ("{disp!(cond)}") {\n"
304 let indent2 = indent.inc_nesting();
305 for stmt in stmts {
306 {indent2}{stmt.display(&indent2, code, f)}"\n"
307 }
308 {indent}"}"
309 }
310 Statement::Break => {
311 "break;"
312 }
313 Statement::Continue => {
314 "continue;"
315 }
316 Statement::Throw(exc) => {
317 "throw "{disp!(exc)}
318 }
319 Statement::Try { stmts } => {
320 "try {\n"
321 let indent2 = indent.inc_nesting();
322 for stmt in stmts {
323 {indent2}{stmt.display(&indent2, code, f)}"\n"
324 }
325 {indent}"}"
326 }
327 Statement::Catch { stmts } => {
328 "catch () {\n"
329 let indent2 = indent.inc_nesting();
330 for stmt in stmts {
331 {indent2}{stmt.display(&indent2, code, f)}"\n"
332 }
333 {indent}"}"
334 }
335 Statement::Comment(comment) => {
336 "// "{comment}
337 }
338 }
339 }
340 }
341}