exact_src.py 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. """Logic related to lazily performing the computation necessary to finding
  2. the source extent of an AST.
  3. Exposed to each macro as an `exact_src` funciton."""
  4. from macropy.core import unparse
  5. from macropy.core.macros import injected_vars
  6. from ast import *
  7. from macropy.core.util import Lazy, distinct, register
  8. from walkers import Walker
  9. def linear_index(line_lengths, lineno, col_offset):
  10. prev_length = sum(line_lengths[:lineno-1]) + lineno-2
  11. out = prev_length + col_offset + 1
  12. return out
  13. @Walker
  14. def indexer(tree, collect, **kw):
  15. try:
  16. unparse(tree)
  17. collect((tree.lineno, tree.col_offset))
  18. except Exception, e:
  19. pass
  20. _transforms = {
  21. GeneratorExp: "(%s)",
  22. ListComp: "[%s]",
  23. SetComp: "{%s}",
  24. DictComp: "{%s}"
  25. }
  26. @register(injected_vars)
  27. def exact_src(tree, src, **kw):
  28. def exact_src_imp(tree, src, indexes, line_lengths):
  29. all_child_pos = sorted(indexer.collect(tree))
  30. start_index = linear_index(line_lengths(), *all_child_pos[0])
  31. last_child_index = linear_index(line_lengths(), *all_child_pos[-1])
  32. first_successor_index = indexes()[min(indexes().index(last_child_index)+1, len(indexes())-1)]
  33. for end_index in range(last_child_index, first_successor_index+1):
  34. prelim = src[start_index:end_index]
  35. prelim = _transforms.get(type(tree), "%s") % prelim
  36. if isinstance(tree, stmt):
  37. prelim = prelim.replace("\n" + " " * tree.col_offset, "\n")
  38. if isinstance(tree, list):
  39. prelim = prelim.replace("\n" + " " * tree[0].col_offset, "\n")
  40. try:
  41. if isinstance(tree, expr):
  42. x = "(" + prelim + ")"
  43. else:
  44. x = prelim
  45. import ast
  46. parsed = ast.parse(x)
  47. if unparse(parsed).strip() == unparse(tree).strip():
  48. return prelim
  49. except SyntaxError as e:
  50. pass
  51. raise ExactSrcException()
  52. positions = Lazy(lambda: indexer.collect(tree))
  53. line_lengths = Lazy(lambda: map(len, src.split("\n")))
  54. indexes = Lazy(lambda: distinct([linear_index(line_lengths(), l, c) for (l, c) in positions()] + [len(src)]))
  55. return lambda t: exact_src_imp(t, src, indexes, line_lengths)
  56. class ExactSrcException(Exception):
  57. pass