while1stmt.py 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
  1. # Copyright (c) 2020, 2022 Rocky Bernstein
  2. def while1stmt(
  3. self, lhs: str, n: int, rule, ast, tokens: list, first: int, last: int
  4. ) -> bool:
  5. # If there is a fall through to the COME_FROM_LOOP, then this is
  6. # not a while 1. So the instruction before should either be a
  7. # JUMP_LOOP or the instruction before should not be the target of a
  8. # jump. (Well that last clause i not quite right; that target could be
  9. # from dead code. Ugh. We need a more uniform control flow analysis.)
  10. if last == n or tokens[last - 1] == "COME_FROM_LOOP":
  11. cfl = last - 1
  12. else:
  13. cfl = last
  14. assert tokens[cfl] == "COME_FROM_LOOP"
  15. for loop_end in range(cfl - 1, first, -1):
  16. if tokens[loop_end] != "POP_BLOCK":
  17. break
  18. if tokens[loop_end].kind not in ("JUMP_LOOP", "RETURN_VALUE", "RAISE_VARARGS_1"):
  19. if not tokens[loop_end].kind.startswith("COME_FROM"):
  20. return True
  21. # Check that the SETUP_LOOP jumps to the offset after the
  22. # COME_FROM_LOOP
  23. if 0 <= last and tokens[last] in ("COME_FROM_LOOP", "JUMP_LOOP"):
  24. # jump_back should be right before COME_FROM_LOOP?
  25. last += 1
  26. if last == n:
  27. last -= 1
  28. offset = tokens[last].off2int()
  29. assert tokens[first] == "SETUP_LOOP"
  30. # Scan for jumps out of the loop. Skip the initial "SETUP_LOOP" instruction.
  31. # If there is a JUMP_LOOP at the end, jumping to that is not breaking out
  32. # of the loop. However after that, any "POP_BLOCK"s or "COME_FROM_LOOP"s
  33. # are considered to break out of the loop.
  34. if tokens[loop_end] == "JUMP_LOOP":
  35. loop_end += 1
  36. loop_end_offset = tokens[loop_end].off2int(prefer_last=False)
  37. for t in range(first + 1, loop_end):
  38. token = tokens[t]
  39. # token could be a pseudo-op like "LOAD_STR", which is not in
  40. # token.opc. We will replace that with LOAD_CONST as an
  41. # example of an instruction that is not in token.opc.JUMP_OPS
  42. if token.opc.opmap.get(token.kind, "LOAD_CONST") in token.opc.JUMP_OPS:
  43. if token.attr >= loop_end_offset:
  44. return True
  45. # SETUP_LOOP location must jump either to the last token or the token after the last one
  46. return tokens[first].attr not in (offset, offset + 2)