hquotes.py 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. """Hygienic Quasiquotes, which pull in names from their definition scope rather
  2. than their expansion scope."""
  3. from macropy.core.macros import *
  4. from macropy.core.quotes import macros, q, unquote_search, u, ast, ast_list, name
  5. macros = Macros()
  6. @macro_stub
  7. def unhygienic():
  8. """Used to delimit a section of a hq[...] that should not be hygienified"""
  9. from macros import filters, injected_vars, post_processing
  10. @register(injected_vars)
  11. def captured_registry(**kw):
  12. return []
  13. @register(post_processing)
  14. def post_proc(tree, captured_registry, gen_sym, **kw):
  15. if captured_registry == []:
  16. return tree
  17. unpickle_name = gen_sym("unpickled")
  18. with q as pickle_import:
  19. from pickle import loads as x
  20. pickle_import[0].names[0].asname = unpickle_name
  21. import pickle
  22. syms = [Name(id=sym) for val, sym in captured_registry]
  23. vals = [val for val, sym in captured_registry]
  24. with q as stored:
  25. ast_list[syms] = name[unpickle_name](u[pickle.dumps(vals)])
  26. from cleanup import ast_ctx_fixer
  27. stored = ast_ctx_fixer.recurse(stored)
  28. tree.body = map(fix_missing_locations, pickle_import + stored) + tree.body
  29. return tree
  30. @register(filters)
  31. def hygienate(tree, captured_registry, gen_sym, **kw):
  32. @Walker
  33. def hygienator(tree, stop, **kw):
  34. if type(tree) is Captured:
  35. new_sym = [sym for val, sym in captured_registry if val is tree.val]
  36. if not new_sym:
  37. new_sym = gen_sym(tree.name)
  38. captured_registry.append((tree.val, new_sym))
  39. else:
  40. new_sym = new_sym[0]
  41. return Name(new_sym, Load())
  42. return hygienator.recurse(tree)
  43. @macros.block
  44. def hq(tree, target, **kw):
  45. tree = unquote_search.recurse(tree)
  46. tree = hygienator.recurse(tree)
  47. tree = ast_repr(tree)
  48. return [Assign([target], tree)]
  49. @macros.expr
  50. def hq(tree, **kw):
  51. """Hygienic Quasiquote macro, used to quote sections of code while ensuring
  52. that names within the quoted code will refer to the value bound to that name
  53. when the code was quoted. Used together with the `u`, `name`, `ast`,
  54. `ast_list`, `unhygienic` unquotes."""
  55. tree = unquote_search.recurse(tree)
  56. tree = hygienator.recurse(tree)
  57. tree = ast_repr(tree)
  58. return tree
  59. @Walker
  60. def hygienator(tree, stop, **kw):
  61. if type(tree) is Name and type(tree.ctx) is Load:
  62. stop()
  63. return Captured(
  64. tree,
  65. tree.id
  66. )
  67. if type(tree) is Literal:
  68. stop()
  69. return tree
  70. res = check_annotated(tree)
  71. if res:
  72. id, subtree = res
  73. if 'unhygienic' == id:
  74. stop()
  75. tree.slice.value.ctx = None
  76. return tree.slice.value