1mod ast_decompiler;
2mod ast_from_value_container;
3mod ast_to_source_code;
4
5use std::collections::HashMap; use std::collections::HashSet;
7use std::fmt::Write;
8use std::io::Cursor;
9use crate::ast::DatexExpression;
12use crate::global::protocol_structures::instructions::Int128Data;
13use crate::global::protocol_structures::instructions::IntegerData;
14use crate::global::protocol_structures::instructions::UInt8Data;
15use crate::global::protocol_structures::instructions::UInt16Data;
16use crate::global::protocol_structures::instructions::UInt32Data;
17use crate::global::protocol_structures::instructions::UInt64Data;
18use crate::global::protocol_structures::instructions::UInt128Data;
19use crate::global::protocol_structures::instructions::{
20 DecimalData, Float32Data, Float64Data, FloatAsInt16Data, FloatAsInt32Data,
21 Instruction, Int8Data, Int16Data, Int32Data, Int64Data, ShortTextData,
22 TextData,
23};
24use crate::parser::body;
25use crate::parser::body::DXBParserError;
26use crate::values::core_values::decimal::utils::decimal_to_string;
27use crate::values::value_container::ValueContainer;
28use syntect::easy::HighlightLines;
29use syntect::highlighting::{Style, Theme, ThemeSet};
30use syntect::parsing::{SyntaxDefinition, SyntaxSetBuilder};
31use syntect::util::{LinesWithEndings, as_24_bit_terminal_escaped};
32
33pub fn decompile_body(
35 dxb_body: &[u8],
36 options: DecompileOptions,
37) -> Result<String, DXBParserError> {
38 let mut initial_state = DecompilerState {
39 dxb_body,
40 options,
41
42 scopes: vec![ScopeState {
43 scope_type: (ScopeType::default(), true),
44 ..ScopeState::default()
45 }],
46
47 current_label: 0,
48 labels: HashMap::new(),
49 inserted_labels: HashSet::new(),
50 variables: HashMap::new(),
51 };
52
53 decompile_loop(&mut initial_state)
54}
55
56pub fn decompile_value(
58 value: &ValueContainer,
59 options: DecompileOptions,
60) -> String {
61 let ast = DatexExpression::from(value);
62 let source_code = ast_to_source_code::ast_to_source_code(&ast, &options);
63 if options.colorized {
65 apply_syntax_highlighting(source_code).unwrap()
66 } else {
67 source_code
68 }
69}
70
71fn int_to_label(n: i32) -> String {
72 let mut label = String::new();
74 let mut n = n;
75
76 while n > 0 {
77 let r = n % 26;
79
80 label.insert(0, (r as u8 + b'a') as char);
82
83 n /= 26;
85 }
86
87 if label.is_empty() {
89 label = "a".to_string();
90 }
91
92 label
93}
94
95#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
96pub enum Formatting {
97 #[default]
98 Compact,
99 Multiline {
100 indent: usize,
101 },
102}
103
104impl Formatting {
105 pub fn multiline() -> Self {
107 Formatting::Multiline { indent: 4 }
108 }
109}
110
111#[derive(Debug, Clone, Default)]
112pub struct DecompileOptions {
113 pub formatting: Formatting,
114 pub colorized: bool,
115 pub resolve_slots: bool,
117 pub json_compat: bool,
121}
122
123impl DecompileOptions {
124 pub fn json() -> Self {
125 DecompileOptions {
126 json_compat: true,
127 ..DecompileOptions::default()
128 }
129 }
130
131 pub fn colorized() -> Self {
133 DecompileOptions {
134 colorized: true,
135 formatting: Formatting::Multiline { indent: 4 },
136 resolve_slots: true,
137 ..DecompileOptions::default()
138 }
139 }
140}
141
142#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
143pub enum ScopeType {
144 #[default]
145 Default,
146 List,
147 Map,
148 SlotAssignment,
149 Transparent,
150}
151
152impl ScopeType {
153 pub fn write_start(
154 &self,
155 output: &mut String,
156 formatting: &Formatting,
157 indentation_levels: usize,
158 ) -> Result<(), DXBParserError> {
159 match self {
160 ScopeType::Default => write!(output, "(")?,
161 ScopeType::List => write!(output, "[")?,
162 ScopeType::Map => write!(output, "{{")?,
163 ScopeType::SlotAssignment => {
164 }
166 ScopeType::Transparent => {}
167 }
168 match self {
169 ScopeType::Default | ScopeType::List | ScopeType::Map => {
170 match formatting {
171 Formatting::Multiline { indent } => {
172 write!(output, "\r\n")?;
173 for _ in 0..(indentation_levels * indent) {
174 write!(output, " ")?;
175 }
176 }
177 Formatting::Compact => {}
178 }
179 }
180 _ => {}
181 }
182 Ok(())
183 }
184 pub fn write_end(
185 &self,
186 output: &mut String,
187 formatting: &Formatting,
188 indentation_levels: usize,
189 ) -> Result<(), DXBParserError> {
190 match self {
191 ScopeType::Default | ScopeType::List | ScopeType::Map => {
192 match formatting {
193 Formatting::Multiline { indent } => {
194 write!(output, "\r\n")?;
195 for _ in
196 0..(indentation_levels.saturating_sub(1) * indent)
197 {
198 write!(output, " ")?;
199 }
200 }
201 Formatting::Compact => {}
202 }
203 }
204 _ => {}
205 }
206 match self {
207 ScopeType::Default => write!(output, ")")?,
208 ScopeType::List => write!(output, "]")?,
209 ScopeType::Map => write!(output, "}}")?,
210 ScopeType::SlotAssignment => {
211 }
213 ScopeType::Transparent => {}
214 }
215 Ok(())
216 }
217}
218
219#[derive(Debug, Clone, Default)]
220struct ScopeState {
221 is_outer_scope: bool,
223 active_operator: Option<(Instruction, bool)>,
225 scope_type: (ScopeType, bool),
226 skip_comma_for_next_item: bool,
228 next_item_is_key: bool,
230 close_scope_after_term: bool,
232}
233
234impl ScopeState {
235 fn write_start(
236 &self,
237 output: &mut String,
238 formatting: &Formatting,
239 indentation_levels: usize,
240 ) -> Result<(), DXBParserError> {
241 self.scope_type
242 .0
243 .write_start(output, formatting, indentation_levels)
244 }
245 fn write_end(
246 &self,
247 output: &mut String,
248 formatting: &Formatting,
249 indentation_levels: usize,
250 ) -> Result<(), DXBParserError> {
251 self.scope_type
252 .0
253 .write_end(output, formatting, indentation_levels)
254 }
255}
256
257#[derive(Debug, Clone)]
258struct DecompilerState<'a> {
259 scopes: Vec<ScopeState>,
261
262 dxb_body: &'a [u8],
264
265 options: DecompileOptions,
267
268 current_label: i32,
270 labels: HashMap<usize, String>,
271 inserted_labels: HashSet<usize>,
272 variables: HashMap<u16, String>,
273}
274
275impl DecompilerState<'_> {
276 fn get_current_scope(&mut self) -> &mut ScopeState {
277 self.scopes.last_mut().unwrap()
278 }
279 fn new_scope(&mut self, scope_type: ScopeType) {
280 self.scopes.push(ScopeState {
281 scope_type: (scope_type, true),
282 ..ScopeState::default()
283 });
284 }
285 fn close_scope(&mut self) {
286 if !self.scopes.is_empty() {
287 self.scopes.pop();
288 }
289 }
290}
291
292impl DecompilerState<'_> {
293 fn get_insert_label(&mut self, index: usize) -> String {
294 if self.labels.contains_key(&index) {
296 self.labels
297 .get(&index)
298 .unwrap_or(&"?invalid?".to_string())
299 .to_string()
300 }
301 else {
303 let name = self.current_label.to_string();
304 self.current_label += 1;
305 self.labels.insert(index, name.clone());
306 name
307 }
308 }
309}
310
311#[deprecated]
312fn decompile_loop(
313 state: &mut DecompilerState,
314) -> Result<String, DXBParserError> {
315 let mut output = String::new();
316 let mut indentation_levels = 0;
317 let formatting = state.options.formatting;
318
319 let instruction_iterator = body::iterate_instructions(state.dxb_body);
320
321 for instruction in instruction_iterator {
322 let instruction = instruction?;
323
324 match instruction {
325 Instruction::Int8(Int8Data(i8)) => {
326 handle_before_term(
327 state,
328 &mut output,
329 true,
330 indentation_levels,
331 )?;
332 write!(output, "{i8}")?;
333 handle_after_term(state, &mut output, false)?;
334 }
335 Instruction::Int16(Int16Data(i16)) => {
336 handle_before_term(
337 state,
338 &mut output,
339 true,
340 indentation_levels,
341 )?;
342 write!(output, "{i16}")?;
343 handle_after_term(state, &mut output, false)?;
344 }
345 Instruction::Int32(Int32Data(i32)) => {
346 handle_before_term(
347 state,
348 &mut output,
349 true,
350 indentation_levels,
351 )?;
352 write!(output, "{i32}")?;
353 handle_after_term(state, &mut output, false)?;
354 }
355 Instruction::Int64(Int64Data(i64)) => {
356 handle_before_term(
357 state,
358 &mut output,
359 true,
360 indentation_levels,
361 )?;
362 write!(output, "{i64}")?;
363 handle_after_term(state, &mut output, false)?;
364 }
365 Instruction::Int128(Int128Data(i128)) => {
366 handle_before_term(
367 state,
368 &mut output,
369 true,
370 indentation_levels,
371 )?;
372 write!(output, "{i128}")?;
373 handle_after_term(state, &mut output, false)?;
374 }
375 Instruction::UInt8(UInt8Data(u8)) => {
376 handle_before_term(
377 state,
378 &mut output,
379 true,
380 indentation_levels,
381 )?;
382 write!(output, "{u8}")?;
383 handle_after_term(state, &mut output, false)?;
384 }
385 Instruction::UInt16(UInt16Data(u16)) => {
386 handle_before_term(
387 state,
388 &mut output,
389 true,
390 indentation_levels,
391 )?;
392 write!(output, "{u16}")?;
393 handle_after_term(state, &mut output, false)?;
394 }
395 Instruction::UInt32(UInt32Data(u32)) => {
396 handle_before_term(
397 state,
398 &mut output,
399 true,
400 indentation_levels,
401 )?;
402 write!(output, "{u32}")?;
403 handle_after_term(state, &mut output, false)?;
404 }
405 Instruction::UInt64(UInt64Data(u64)) => {
406 handle_before_term(
407 state,
408 &mut output,
409 true,
410 indentation_levels,
411 )?;
412 write!(output, "{u64}")?;
413 handle_after_term(state, &mut output, false)?;
414 }
415 Instruction::UInt128(UInt128Data(u128)) => {
416 handle_before_term(
417 state,
418 &mut output,
419 true,
420 indentation_levels,
421 )?;
422 write!(output, "{u128}")?;
423 handle_after_term(state, &mut output, false)?;
424 }
425 Instruction::BigInteger(IntegerData(big_int)) => {
426 handle_before_term(
427 state,
428 &mut output,
429 true,
430 indentation_levels,
431 )?;
432 write!(output, "{big_int}n")?;
433 handle_after_term(state, &mut output, false)?;
434 }
435 Instruction::DecimalF32(Float32Data(f32)) => {
436 handle_before_term(
437 state,
438 &mut output,
439 true,
440 indentation_levels,
441 )?;
442 write!(
443 output,
444 "{}",
445 decimal_to_string(f32, state.options.json_compat)
446 )?;
447 handle_after_term(state, &mut output, false)?;
448 }
449 Instruction::DecimalF64(Float64Data(f64)) => {
450 handle_before_term(
451 state,
452 &mut output,
453 true,
454 indentation_levels,
455 )?;
456 write!(
457 output,
458 "{}",
459 decimal_to_string(f64, state.options.json_compat)
460 )?;
461 handle_after_term(state, &mut output, false)?;
462 }
463 Instruction::DecimalAsInt16(FloatAsInt16Data(i16)) => {
464 handle_before_term(
465 state,
466 &mut output,
467 true,
468 indentation_levels,
469 )?;
470 write!(
471 output,
472 "{}",
473 decimal_to_string(i16 as f32, state.options.json_compat)
474 )?;
475 handle_after_term(state, &mut output, false)?;
476 }
477 Instruction::DecimalAsInt32(FloatAsInt32Data(i32)) => {
478 handle_before_term(
479 state,
480 &mut output,
481 true,
482 indentation_levels,
483 )?;
484 write!(
485 output,
486 "{}",
487 decimal_to_string(i32 as f32, state.options.json_compat)
488 )?;
489 handle_after_term(state, &mut output, false)?;
490 }
491 Instruction::Decimal(DecimalData(big_decimal)) => {
492 handle_before_term(
493 state,
494 &mut output,
495 true,
496 indentation_levels,
497 )?;
498 write!(output, "{big_decimal}")?;
499 handle_after_term(state, &mut output, false)?;
500 }
501 Instruction::ShortText(ShortTextData(text)) => {
502 handle_before_term(
503 state,
504 &mut output,
505 true,
506 indentation_levels,
507 )?;
508 let text = escape_text(&text);
509 write!(output, "\"{text}\"")?;
510 handle_after_term(state, &mut output, true)?;
511 }
512 Instruction::Text(TextData(text)) => {
513 handle_before_term(
514 state,
515 &mut output,
516 true,
517 indentation_levels,
518 )?;
519 let text = escape_text(&text);
520 write!(output, "\"{text}\"")?;
521 handle_after_term(state, &mut output, true)?;
522 }
523 Instruction::True => {
524 handle_before_term(
525 state,
526 &mut output,
527 false,
528 indentation_levels,
529 )?;
530 write!(output, "true")?;
531 handle_after_term(state, &mut output, false)?;
532 }
533 Instruction::False => {
534 handle_before_term(
535 state,
536 &mut output,
537 false,
538 indentation_levels,
539 )?;
540 write!(output, "false")?;
541 handle_after_term(state, &mut output, false)?;
542 }
543 Instruction::Null => {
544 handle_before_term(
545 state,
546 &mut output,
547 false,
548 indentation_levels,
549 )?;
550 write!(output, "null")?;
551 handle_after_term(state, &mut output, false)?;
552 }
553 Instruction::Endpoint(endpoint) => {
554 handle_before_term(
555 state,
556 &mut output,
557 false,
558 indentation_levels,
559 )?;
560 write!(output, "{endpoint}")?;
561 handle_after_term(state, &mut output, false)?;
562 }
563 Instruction::ListStart => {
564 indentation_levels += 1;
565 handle_before_term(
566 state,
567 &mut output,
568 false,
569 indentation_levels,
570 )?;
571 state.new_scope(ScopeType::List);
572 state.get_current_scope().write_start(
573 &mut output,
574 &formatting,
575 indentation_levels,
576 )?;
577 }
578 Instruction::MapStart => {
579 indentation_levels += 1;
580 handle_before_term(
581 state,
582 &mut output,
583 false,
584 indentation_levels,
585 )?;
586 state.new_scope(ScopeType::Map);
587 state.get_current_scope().write_start(
588 &mut output,
589 &formatting,
590 indentation_levels,
591 )?;
592 }
593 Instruction::ScopeStart => {
594 indentation_levels += 1;
595 handle_before_term(
596 state,
597 &mut output,
598 true,
599 indentation_levels,
600 )?;
601 state.new_scope(ScopeType::Default);
602 state.get_current_scope().write_start(
603 &mut output,
604 &formatting,
605 indentation_levels,
606 )?;
607 }
608 Instruction::ScopeEnd => {
609 let current_scope_is_collection = matches!(
610 state.get_current_scope().scope_type.0,
611 ScopeType::List | ScopeType::Map
612 );
613 handle_scope_close(state, &mut output, indentation_levels)?;
614 handle_after_term(state, &mut output, true)?;
615 if current_scope_is_collection {
616 indentation_levels = indentation_levels.saturating_sub(1);
617 }
618 }
619 Instruction::KeyValueShortText(text_data) => {
620 handle_before_term(
621 state,
622 &mut output,
623 false,
624 indentation_levels,
625 )?;
626 state.get_current_scope().skip_comma_for_next_item = true;
628 write_text_key(
629 state,
630 &text_data.0,
631 &mut output,
632 state.options.formatting,
633 )?;
634 }
635 Instruction::KeyValueDynamic => {
636 handle_before_term(
637 state,
638 &mut output,
639 false,
640 indentation_levels,
641 )?;
642 state.get_current_scope().skip_comma_for_next_item = true;
643 state.get_current_scope().next_item_is_key = true;
644 }
645 Instruction::CloseAndStore => match state.options.formatting {
646 Formatting::Multiline { .. } => {
647 write!(output, ";\r\n")?;
648 }
649 Formatting::Compact => {
650 write!(output, ";")?;
651 }
652 },
653
654 Instruction::Add
656 | Instruction::Subtract
657 | Instruction::Multiply
658 | Instruction::Divide => {
659 handle_before_term(
660 state,
661 &mut output,
662 false,
663 indentation_levels,
664 )?;
665 state.new_scope(ScopeType::Transparent);
666 state.get_current_scope().active_operator =
667 Some((instruction, true));
668 }
669
670 Instruction::UnaryMinus
671 | Instruction::UnaryPlus
672 | Instruction::BitwiseNot => {
673 handle_before_term(
674 state,
675 &mut output,
676 false,
677 indentation_levels,
678 )?;
679 state.new_scope(ScopeType::Transparent);
680 state.get_current_scope().active_operator =
681 Some((instruction, false));
682 }
683
684 Instruction::AllocateSlot(address) => {
686 handle_before_term(
687 state,
688 &mut output,
689 false,
690 indentation_levels,
691 )?;
692 state.new_scope(ScopeType::SlotAssignment);
693 if state.options.resolve_slots {
695 write!(output, "#{} := ", address.0)?;
697 } else {
698 write!(output, "#{} := ", address.0)?;
700 }
701 handle_after_term(state, &mut output, false)?;
702 }
703 Instruction::GetSlot(address) => {
704 handle_before_term(
705 state,
706 &mut output,
707 false,
708 indentation_levels,
709 )?;
710 if state.options.resolve_slots {
712 write!(output, "#{}", address.0)?;
714 } else {
715 write!(output, "#{}", address.0)?;
717 }
718 handle_after_term(state, &mut output, false)?;
719 }
720 Instruction::DropSlot(address) => {
721 if state.options.resolve_slots {
723 write!(output, "#drop {}", address.0)?;
725 } else {
726 write!(output, "#drop {}", address.0)?;
728 }
729 }
730 Instruction::SetSlot(address) => {
731 handle_before_term(
732 state,
733 &mut output,
734 false,
735 indentation_levels,
736 )?;
737 state.new_scope(ScopeType::SlotAssignment);
738 if state.options.resolve_slots {
740 write!(output, "#{} = ", address.0)?;
742 } else {
743 write!(output, "#{} = ", address.0)?;
745 }
746 }
747
748 Instruction::GetRef(address) => {
749 handle_before_term(
750 state,
751 &mut output,
752 false,
753 indentation_levels,
754 )?;
755 let endpoint_hex = address
756 .endpoint
757 .to_binary()
758 .iter()
759 .map(|b| format!("{:02x}", b))
760 .collect::<String>();
761 let address_hex = address
762 .id
763 .iter()
764 .map(|b| format!("{:02x}", b))
765 .collect::<String>();
766 write!(output, "$<{}:{}>", endpoint_hex, address_hex)?;
767 handle_after_term(state, &mut output, false)?;
768 }
769
770 Instruction::GetInternalRef(address) => {
771 handle_before_term(
772 state,
773 &mut output,
774 false,
775 indentation_levels,
776 )?;
777 let address_hex = address
778 .id
779 .iter()
780 .map(|b| format!("{:02x}", b))
781 .collect::<String>();
782 write!(output, "$<internal:{}>", address_hex)?;
783 handle_after_term(state, &mut output, false)?;
784 }
785
786 Instruction::GetLocalRef(address) => {
787 handle_before_term(
788 state,
789 &mut output,
790 false,
791 indentation_levels,
792 )?;
793 let address_hex = address
794 .id
795 .iter()
796 .map(|b| format!("{:02x}", b))
797 .collect::<String>();
798 write!(output, "$<origin:{}>", address_hex)?;
799 handle_after_term(state, &mut output, false)?;
800 }
801
802 Instruction::AddAssign(address) => {
803 handle_before_term(
804 state,
805 &mut output,
806 false,
807 indentation_levels,
808 )?;
809 state.new_scope(ScopeType::SlotAssignment);
810 if state.options.resolve_slots {
812 write!(output, "#{} += ", address.0)?;
813 } else {
814 write!(output, "#{} += ", address.0)?;
816 }
817 }
818
819 Instruction::SubtractAssign(address) => {
820 handle_before_term(
821 state,
822 &mut output,
823 false,
824 indentation_levels,
825 )?;
826 state.new_scope(ScopeType::SlotAssignment);
827 if state.options.resolve_slots {
829 write!(output, "#{} -= ", address.0)?;
830 } else {
831 write!(output, "#{} -= ", address.0)?;
833 }
834 }
835
836 Instruction::CreateRef => {
837 handle_before_term(
838 state,
839 &mut output,
840 false,
841 indentation_levels,
842 )?;
843 state.get_current_scope().skip_comma_for_next_item = true;
844 write!(output, "&")?;
845 }
846
847 Instruction::CreateRefMut => {
848 handle_before_term(
849 state,
850 &mut output,
851 false,
852 indentation_levels,
853 )?;
854 state.get_current_scope().skip_comma_for_next_item = true;
855 write!(output, "&mut ")?;
856 }
857
858 Instruction::CreateRefFinal => {
859 handle_before_term(
860 state,
861 &mut output,
862 false,
863 indentation_levels,
864 )?;
865 state.get_current_scope().skip_comma_for_next_item = true;
866 write!(output, "&final ")?;
867 }
868
869 Instruction::RemoteExecution => {
870 handle_before_term(
871 state,
872 &mut output,
873 false,
874 indentation_levels,
875 )?;
876 state.get_current_scope().active_operator =
877 Some((instruction, true));
878 }
879
880 Instruction::ExecutionBlock(data) => {
881 handle_before_term(
882 state,
883 &mut output,
884 true,
885 indentation_levels,
886 )?;
887 let decompiled_body =
889 decompile_body(&data.body, state.options.clone())?;
890 let slot_mapping = data
891 .injected_slots
892 .iter()
893 .enumerate()
894 .map(|(k, v)| format!("#{v} => #{k}"))
895 .collect::<Vec<_>>()
896 .join(", ");
897 write!(output, "[{slot_mapping}]({decompiled_body})")?;
899 }
900
901 _ => {
902 write!(output, "[[{instruction}]]")?;
903 }
904 }
905 }
906
907 if state.options.colorized {
909 output = apply_syntax_highlighting(output)?;
910 }
911
912 Ok(output)
913}
914
915pub fn apply_syntax_highlighting(
916 datex_script: String,
917) -> Result<String, DXBParserError> {
918 let mut output = String::new();
919
920 static DATEX_SCRIPT_DEF: &str = include_str!(
922 "../../datex-language/datex.tmbundle/Syntaxes/datex.sublime-text"
923 );
924 static DATEX_THEME_DEF: &str =
925 include_str!("../../datex-language/themes/datex-dark.tmTheme");
926 let mut builder = SyntaxSetBuilder::new();
927 let syntax = SyntaxDefinition::load_from_str(DATEX_SCRIPT_DEF, true, None)
928 .expect("Failed to load syntax definition");
929 builder.add(syntax);
930 let theme: Theme =
931 ThemeSet::load_from_reader(&mut Cursor::new(DATEX_THEME_DEF))
932 .expect("Failed to load theme");
933
934 let ps = builder.build();
935 let syntax = ps.find_syntax_by_extension("dx").unwrap();
936 let mut h = HighlightLines::new(syntax, &theme);
937
938 for line in LinesWithEndings::from(&datex_script) {
939 let ranges: Vec<(Style, &str)> = h.highlight_line(line, &ps).unwrap();
940 let escaped = as_24_bit_terminal_escaped(&ranges[..], false);
941 write!(output, "{escaped}")?;
942 }
943 write!(output, "\x1b[0m")?;
945 Ok(output)
946}
947
948fn escape_text(text: &str) -> String {
949 text.replace('\\', r#"\\"#)
951 .replace('"', r#"\""#)
952 .replace('\u{0008}', r#"\b"#)
953 .replace('\u{000c}', r#"\f"#)
954 .replace('\r', r#"\r"#)
955 .replace('\t', r#"\t"#)
956 .replace('\u{000b}', r#"\v"#)
957 .replace('\n', r#"\n"#)
958}
959
960fn write_text_key(
961 state: &mut DecompilerState,
962 text: &str,
963 output: &mut String,
964 formatting: Formatting,
965) -> Result<(), DXBParserError> {
966 let text = if !state.options.json_compat && is_alphanumeric_identifier(text)
968 {
969 text.to_string()
970 } else {
971 format!("\"{}\"", escape_text(text))
972 };
973 match formatting {
974 Formatting::Multiline { .. } => {
975 write!(output, "{text}: ")?;
976 }
977 Formatting::Compact => {
978 write!(output, "{text}:")?;
979 }
980 }
981 Ok(())
982}
983
984fn is_alphanumeric_identifier(s: &str) -> bool {
985 let mut chars = s.chars();
986
987 match chars.next() {
989 Some(c) if c.is_ascii_alphabetic() || c == '_' => {}
990 _ => return false,
991 }
992
993 chars.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
995}
996
997fn handle_before_term(
1001 state: &mut DecompilerState,
1002 output: &mut String,
1003 is_standalone_key: bool,
1004 indentation_levels: usize,
1005) -> Result<(), DXBParserError> {
1006 handle_before_operand(state, output)?;
1007 handle_before_item(state, output, is_standalone_key, indentation_levels)?;
1008 Ok(())
1009}
1010
1011fn handle_after_term(
1014 state: &mut DecompilerState,
1015 output: &mut String,
1016 is_standalone_key: bool,
1017) -> Result<(), DXBParserError> {
1018 let close_scope = state.get_current_scope().close_scope_after_term;
1019 if close_scope {
1020 state.close_scope();
1022 }
1023
1024 if state.get_current_scope().next_item_is_key {
1026 if !is_standalone_key || close_scope {
1027 write!(output, ")")?;
1028 }
1029 state.get_current_scope().next_item_is_key = false;
1031 match state.options.formatting {
1032 Formatting::Multiline { .. } => {
1033 write!(output, ": ")?;
1034 }
1035 Formatting::Compact => {
1036 write!(output, ":")?;
1037 }
1038 }
1039 state.get_current_scope().skip_comma_for_next_item = true;
1041 }
1042
1043 Ok(())
1044}
1045
1046fn handle_scope_close(
1048 state: &mut DecompilerState,
1049 output: &mut String,
1050 indentation_levels: usize,
1051) -> Result<(), DXBParserError> {
1052 let formatting = state.options.formatting;
1053 let scope = state.get_current_scope();
1054 if !scope.is_outer_scope {
1056 state.get_current_scope().write_end(
1057 output,
1058 &formatting,
1059 indentation_levels,
1060 )?;
1061 }
1062 state.close_scope();
1064 Ok(())
1065}
1066
1067fn handle_before_item(
1071 state: &mut DecompilerState,
1072 output: &mut String,
1073 is_standalone_key: bool,
1074 indentation_levels: usize,
1075) -> Result<(), DXBParserError> {
1076 let formatted = state.options.formatting;
1077 let scope = state.get_current_scope();
1078
1079 if !is_standalone_key && scope.next_item_is_key {
1081 write!(output, "(")?;
1082 }
1083
1084 match scope.scope_type {
1085 (_, true) => {
1086 scope.scope_type.1 = false;
1088 }
1089 (ScopeType::List | ScopeType::Map, false)
1090 if !scope.skip_comma_for_next_item =>
1091 {
1092 match formatted {
1093 Formatting::Multiline { indent } => {
1094 write!(output, ",\r\n")?;
1095 let current_indent = indentation_levels * indent;
1096 for _ in 0..current_indent {
1097 write!(output, " ")?;
1098 }
1099 }
1100 Formatting::Compact => {
1101 write!(output, ",")?;
1102 }
1103 }
1104 }
1105 _ => {
1106 }
1108 }
1109
1110 scope.skip_comma_for_next_item = false;
1112
1113 Ok(())
1114}
1115
1116fn handle_before_operand(
1118 state: &mut DecompilerState,
1119 output: &mut String,
1120) -> Result<(), DXBParserError> {
1121 if let Some(operator) = state.get_current_scope().active_operator.take() {
1122 match operator {
1124 (_, true) => {
1125 state.get_current_scope().active_operator =
1127 Some((operator.0.clone(), false));
1128 }
1129 (Instruction::Add, false) => {
1130 write_operator(state, output, "+")?;
1131 state.get_current_scope().close_scope_after_term = true;
1132 }
1133 (Instruction::Subtract, false) => {
1134 write_operator(state, output, "-")?;
1135 state.get_current_scope().close_scope_after_term = true;
1136 }
1137 (Instruction::Multiply, false) => {
1138 write_operator(state, output, "*")?;
1139 state.get_current_scope().close_scope_after_term = true;
1140 }
1141 (Instruction::Divide, false) => {
1142 write_operator(state, output, "/")?;
1143 state.get_current_scope().close_scope_after_term = true;
1144 }
1145 (Instruction::RemoteExecution, false) => {
1146 write_operator(state, output, "::")?;
1147 state.get_current_scope().close_scope_after_term = false;
1148 }
1149 (Instruction::UnaryMinus, false) => {
1150 write!(output, "-")?;
1151 state.get_current_scope().close_scope_after_term = true;
1152 }
1153 (Instruction::UnaryPlus, false) => {
1154 write!(output, "+")?;
1155 state.get_current_scope().close_scope_after_term = true;
1156 }
1157 (Instruction::BitwiseNot, false) => {
1158 write!(output, "~")?;
1159 state.get_current_scope().close_scope_after_term = true;
1160 }
1161 _ => {
1162 todo!("#423 Invalid operator: {operator:?}");
1163 }
1164 }
1165 }
1166 Ok(())
1167}
1168
1169fn write_operator(
1170 state: &mut DecompilerState,
1171 output: &mut String,
1172 operator: &str,
1173) -> Result<(), DXBParserError> {
1174 write!(output, " {operator} ")?;
1175 Ok(())
1176}