123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104 |
- """Hygienic Quasiquotes, which pull in names from their definition scope rather
- than their expansion scope."""
- from macropy.core.macros import *
- from macropy.core.quotes import macros, q, unquote_search, u, ast, ast_list, name
- macros = Macros()
- @macro_stub
- def unhygienic():
- """Used to delimit a section of a hq[...] that should not be hygienified"""
- from macros import filters, injected_vars, post_processing
- @register(injected_vars)
- def captured_registry(**kw):
- return []
- @register(post_processing)
- def post_proc(tree, captured_registry, gen_sym, **kw):
- if captured_registry == []:
- return tree
- unpickle_name = gen_sym("unpickled")
- with q as pickle_import:
- from pickle import loads as x
- pickle_import[0].names[0].asname = unpickle_name
- import pickle
- syms = [Name(id=sym) for val, sym in captured_registry]
- vals = [val for val, sym in captured_registry]
- with q as stored:
- ast_list[syms] = name[unpickle_name](u[pickle.dumps(vals)])
- from cleanup import ast_ctx_fixer
- stored = ast_ctx_fixer.recurse(stored)
- tree.body = map(fix_missing_locations, pickle_import + stored) + tree.body
- return tree
- @register(filters)
- def hygienate(tree, captured_registry, gen_sym, **kw):
- @Walker
- def hygienator(tree, stop, **kw):
- if type(tree) is Captured:
- new_sym = [sym for val, sym in captured_registry if val is tree.val]
- if not new_sym:
- new_sym = gen_sym(tree.name)
- captured_registry.append((tree.val, new_sym))
- else:
- new_sym = new_sym[0]
- return Name(new_sym, Load())
- return hygienator.recurse(tree)
- @macros.block
- def hq(tree, target, **kw):
- tree = unquote_search.recurse(tree)
- tree = hygienator.recurse(tree)
- tree = ast_repr(tree)
- return [Assign([target], tree)]
- @macros.expr
- def hq(tree, **kw):
- """Hygienic Quasiquote macro, used to quote sections of code while ensuring
- that names within the quoted code will refer to the value bound to that name
- when the code was quoted. Used together with the `u`, `name`, `ast`,
- `ast_list`, `unhygienic` unquotes."""
- tree = unquote_search.recurse(tree)
- tree = hygienator.recurse(tree)
- tree = ast_repr(tree)
- return tree
- @Walker
- def hygienator(tree, stop, **kw):
- if type(tree) is Name and type(tree.ctx) is Load:
- stop()
- return Captured(
- tree,
- tree.id
- )
- if type(tree) is Literal:
- stop()
- return tree
- res = check_annotated(tree)
- if res:
- id, subtree = res
- if 'unhygienic' == id:
- stop()
- tree.slice.value.ctx = None
- return tree.slice.value
|