Jsm.Control.Builder.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. using OpenVIII.Fields.Scripts.Instructions;
  2. using System;
  3. using System.Collections.Generic;
  4. namespace OpenVIII.Fields.Scripts
  5. {
  6. public static partial class Jsm
  7. {
  8. public static partial class Control
  9. {
  10. public sealed class Builder
  11. {
  12. public static IReadOnlyList<IJsmControl> Build(List<JsmInstruction> instructions)
  13. {
  14. return new Builder(instructions).Make();
  15. }
  16. private readonly List<JsmInstruction> _instructions;
  17. private readonly ProcessedJumps _processed = new ProcessedJumps();
  18. private readonly List<IJsmControl> _result = new List<IJsmControl>();
  19. private Builder(List<JsmInstruction> instructions)
  20. {
  21. _instructions = instructions;
  22. }
  23. private Int32 _index;
  24. private JPF _begin;
  25. public IReadOnlyList<IJsmControl> Make()
  26. {
  27. for (_index = 0; _index < _instructions.Count; _index++)
  28. {
  29. if (TryMakeGotoOrSkip())
  30. continue;
  31. if (TryMakeWhile())
  32. continue;
  33. if (TryMakeIf())
  34. continue;
  35. throw new InvalidProgramException($"Cannot recognize the logical block: {_begin}");
  36. }
  37. return _result;
  38. }
  39. private Boolean TryMakeGotoOrSkip()
  40. {
  41. var instruction = _instructions[_index];
  42. if (instruction is JMP @goto && _processed.TryProcess(@goto))
  43. {
  44. var control = new Goto(_instructions, _index, @goto.Index);
  45. _result.Add(control);
  46. return true;
  47. }
  48. if (!(instruction is JPF jpf))
  49. return true;
  50. if (!_processed.TryProcess(jpf))
  51. return true;
  52. _begin = jpf;
  53. return false;
  54. }
  55. private Boolean TryMakeWhile()
  56. {
  57. if (_instructions[_begin.Index - 1] is JMP jmp && jmp.Index == _index)
  58. {
  59. _processed.TryProcess(jmp);
  60. _result.Add(new While(_instructions, _index, _begin.Index));
  61. return true;
  62. }
  63. return false;
  64. }
  65. private Boolean TryMakeIf()
  66. {
  67. JPF jpf = _begin;
  68. If control = new If(_instructions, _index, _begin.Index);
  69. _result.Add(control);
  70. if (!(_instructions[_begin.Index - 1] is JMP jmp))
  71. {
  72. // There is no JMP instruction. Simple if {}
  73. return true;
  74. }
  75. if (jmp.Index == jpf.Index)
  76. {
  77. // It isn't our jump, but an nested if. If { nested if{}<-}
  78. return true;
  79. }
  80. if (jmp.Index < jpf.Index)
  81. {
  82. // It isn't our jump, but an nested loop. If { nested while{}<-}
  83. return true;
  84. }
  85. if (jmp.Index < _index)
  86. {
  87. // It isn't our jump, but an nested goto. If { nested goto l;<-}
  88. return true;
  89. }
  90. _processed.Process(jmp);
  91. AddIfElseBranches(control, jpf, jmp);
  92. return true;
  93. }
  94. private void AddIfElseBranches(If control, JPF jpf, JMP jmp)
  95. {
  96. Int32 endOfBlock = jmp.Index;
  97. while (true)
  98. {
  99. Int32 newJpfIndex = jpf.Index;
  100. if (!(_instructions[newJpfIndex] is JPF newJpf) || newJpf.Index > endOfBlock)
  101. {
  102. control.AddElse(jpf.Index, endOfBlock);
  103. return;
  104. }
  105. if (!(_instructions[newJpf.Index - 1] is JMP newJmp))
  106. {
  107. if (newJpf.Index == endOfBlock)
  108. {
  109. // if-elseif without jmp
  110. _processed.Process(newJpf);
  111. control.AddIf(newJpfIndex, newJpf.Index);
  112. }
  113. else
  114. {
  115. // if-else without jmp
  116. control.AddElse(jpf.Index, endOfBlock);
  117. }
  118. return;
  119. }
  120. // Isn't our jump
  121. if (newJmp.Index != endOfBlock)
  122. {
  123. control.AddElse(jpf.Index, endOfBlock);
  124. return;
  125. }
  126. jpf = newJpf;
  127. jmp = newJmp;
  128. _processed.Process(jpf);
  129. _processed.TryProcess(jmp);
  130. control.AddIf(newJpfIndex, jpf.Index);
  131. if (jpf.Index == endOfBlock)
  132. return;
  133. }
  134. }
  135. }
  136. }
  137. }
  138. }