Skip to content

Commit c5abc85

Browse files
authored
Merge pull request hylang#1682 from brandonwillard/macro-changes
Macro processing updates and fixes
2 parents 4132adb + aa9182d commit c5abc85

21 files changed

+742
-265
lines changed

NEWS.rst

+6
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,15 @@ New Features
1212
* Keyword objects (not just literal keywords) can be called, as
1313
shorthand for `(get obj :key)`, and they accept a default value
1414
as a second argument.
15+
* Minimal macro expansion namespacing has been implemented. As a result,
16+
external macros no longer have to `require` their own macro dependencies.
17+
* Macros and tags now reside in module-level `__macros__` and `__tags__`
18+
attributes.
1519

1620
Bug Fixes
1721
------------------------------
22+
* `require` now compiles to Python AST.
23+
* Fixed circular `require`s.
1824
* Fixed module reloading.
1925
* Fixed circular imports.
2026
* Fixed `__main__` file execution.

hy/cmdline.py

+26-15
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import importlib
1414
import py_compile
1515
import runpy
16+
import types
1617

1718
import astor.code_gen
1819

@@ -47,10 +48,26 @@ def __call__(self, code=None):
4748
builtins.exit = HyQuitter('exit')
4849

4950

50-
class HyREPL(code.InteractiveConsole):
51+
class HyREPL(code.InteractiveConsole, object):
5152
def __init__(self, spy=False, output_fn=None, locals=None,
5253
filename="<input>"):
5354

55+
super(HyREPL, self).__init__(locals=locals,
56+
filename=filename)
57+
58+
# Create a proper module for this REPL so that we can obtain it easily
59+
# (e.g. using `importlib.import_module`).
60+
# Also, make sure it's properly introduced to `sys.modules` and
61+
# consistently use its namespace as `locals` from here on.
62+
module_name = self.locals.get('__name__', '__console__')
63+
self.module = sys.modules.setdefault(module_name,
64+
types.ModuleType(module_name))
65+
self.module.__dict__.update(self.locals)
66+
self.locals = self.module.__dict__
67+
68+
# Load cmdline-specific macros.
69+
require('hy.cmdline', module_name, assignments='ALL')
70+
5471
self.spy = spy
5572

5673
if output_fn is None:
@@ -65,9 +82,6 @@ def __init__(self, spy=False, output_fn=None, locals=None,
6582
else:
6683
self.output_fn = __builtins__[mangle(output_fn)]
6784

68-
code.InteractiveConsole.__init__(self, locals=locals,
69-
filename=filename)
70-
7185
# Pre-mangle symbols for repl recent results: *1, *2, *3
7286
self._repl_results_symbols = [mangle("*{}".format(i + 1)) for i in range(3)]
7387
self.locals.update({sym: None for sym in self._repl_results_symbols})
@@ -102,8 +116,7 @@ def ast_callback(main_ast, expr_ast):
102116
new_ast = ast.Module(main_ast.body +
103117
[ast.Expr(expr_ast.body)])
104118
print(astor.to_source(new_ast))
105-
value = hy_eval(do, self.locals, "__console__",
106-
ast_callback)
119+
value = hy_eval(do, self.locals, self.module, ast_callback)
107120
except HyTypeError as e:
108121
if e.source is None:
109122
e.source = source
@@ -181,8 +194,6 @@ def ideas_macro(ETname):
181194
182195
""")])
183196

184-
require("hy.cmdline", "__console__", assignments="ALL")
185-
require("hy.cmdline", "__main__", assignments="ALL")
186197

187198
SIMPLE_TRACEBACKS = True
188199

@@ -199,7 +210,8 @@ def pretty_error(func, *args, **kw):
199210

200211
def run_command(source):
201212
tree = hy_parse(source)
202-
pretty_error(hy_eval, tree, module_name="__main__")
213+
require("hy.cmdline", "__main__", assignments="ALL")
214+
pretty_error(hy_eval, tree, None, importlib.import_module('__main__'))
203215
return 0
204216

205217

@@ -208,12 +220,12 @@ def run_repl(hr=None, **kwargs):
208220
sys.ps1 = "=> "
209221
sys.ps2 = "... "
210222

211-
namespace = {'__name__': '__console__', '__doc__': ''}
223+
if not hr:
224+
hr = HyREPL(**kwargs)
212225

213-
with completion(Completer(namespace)):
226+
namespace = hr.locals
214227

215-
if not hr:
216-
hr = HyREPL(locals=namespace, **kwargs)
228+
with completion(Completer(namespace)):
217229

218230
hr.interact("{appname} {version} using "
219231
"{py}({build}) {pyversion} on {os}".format(
@@ -409,7 +421,6 @@ def hyc_main():
409421
# entry point for cmd line script "hy2py"
410422
def hy2py_main():
411423
import platform
412-
module_name = "<STDIN>"
413424

414425
options = dict(prog="hy2py", usage="%(prog)s [options] [FILE]",
415426
formatter_class=argparse.RawDescriptionHelpFormatter)
@@ -448,7 +459,7 @@ def hy2py_main():
448459
print()
449460
print()
450461

451-
_ast = pretty_error(hy_compile, hst, module_name)
462+
_ast = pretty_error(hy_compile, hst, '__main__')
452463
if options.with_ast:
453464
if PY3 and platform.system() == "Windows":
454465
_print_for_windows(astor.dump_tree(_ast))

hy/compiler.py

+89-44
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,16 @@
1313

1414
from hy.lex import mangle, unmangle
1515

16-
import hy.macros
17-
from hy._compat import (
18-
str_type, bytes_type, long_type, PY3, PY35, raise_empty)
19-
from hy.macros import require, macroexpand, tag_macroexpand
16+
from hy._compat import (str_type, string_types, bytes_type, long_type, PY3,
17+
PY35, raise_empty)
18+
from hy.macros import require, load_macros, macroexpand, tag_macroexpand
2019
import hy.importer
2120

2221
import traceback
2322
import importlib
23+
import inspect
24+
import pkgutil
25+
import types
2426
import ast
2527
import sys
2628
import copy
@@ -279,32 +281,48 @@ def is_unpack(kind, x):
279281
and x[0] == "unpack-" + kind)
280282

281283

282-
_stdlib = {}
283-
284-
285284
class HyASTCompiler(object):
285+
"""A Hy-to-Python AST compiler"""
286286

287-
def __init__(self, module_name):
287+
def __init__(self, module):
288+
"""
289+
Parameters
290+
----------
291+
module: str or types.ModuleType
292+
Module in which the Hy tree is evaluated.
293+
"""
288294
self.anon_var_count = 0
289295
self.imports = defaultdict(set)
290-
self.module_name = module_name
291296
self.temp_if = None
297+
298+
if not inspect.ismodule(module):
299+
module = importlib.import_module(module)
300+
301+
self.module = module
302+
self.module_name = module.__name__
303+
292304
self.can_use_stdlib = (
293-
not module_name.startswith("hy.core")
294-
or module_name == "hy.core.macros")
305+
not self.module_name.startswith("hy.core")
306+
or self.module_name == "hy.core.macros")
307+
308+
# Load stdlib macros into the module namespace.
309+
load_macros(self.module)
310+
311+
self._stdlib = {}
312+
295313
# Everything in core needs to be explicit (except for
296314
# the core macros, which are built with the core functions).
297-
if self.can_use_stdlib and not _stdlib:
315+
if self.can_use_stdlib:
298316
# Populate _stdlib.
299317
import hy.core
300-
for module in hy.core.STDLIB:
301-
mod = importlib.import_module(module)
302-
for e in map(ast_str, mod.EXPORTS):
318+
for stdlib_module in hy.core.STDLIB:
319+
mod = importlib.import_module(stdlib_module)
320+
for e in map(ast_str, getattr(mod, 'EXPORTS', [])):
303321
if getattr(mod, e) is not getattr(builtins, e, ''):
304322
# Don't bother putting a name in _stdlib if it
305323
# points to a builtin with the same name. This
306324
# prevents pointless imports.
307-
_stdlib[e] = module
325+
self._stdlib[e] = stdlib_module
308326

309327
def get_anon_var(self):
310328
self.anon_var_count += 1
@@ -1098,11 +1116,6 @@ def compile_unary_operator(self, expr, root, arg):
10981116
brackets(SYM, sym(":as"), _symn) |
10991117
brackets(SYM, brackets(many(_symn + maybe(sym(":as") + _symn)))))])
11001118
def compile_import_or_require(self, expr, root, entries):
1101-
"""
1102-
TODO for `require`: keep track of what we've imported in this run and
1103-
then "unimport" it after we've completed `thing' so that we don't
1104-
pollute other envs.
1105-
"""
11061119
ret = Result()
11071120

11081121
for entry in entries:
@@ -1128,8 +1141,9 @@ def compile_import_or_require(self, expr, root, entries):
11281141
else:
11291142
assignments = [(k, v or k) for k, v in kids]
11301143

1144+
ast_module = ast_str(module, piecewise=True)
1145+
11311146
if root == "import":
1132-
ast_module = ast_str(module, piecewise=True)
11331147
module = ast_module.lstrip(".")
11341148
level = len(ast_module) - len(module)
11351149
if assignments == "ALL" and prefix == "":
@@ -1150,10 +1164,23 @@ def compile_import_or_require(self, expr, root, entries):
11501164
for k, v in assignments]
11511165
ret += node(
11521166
expr, module=module or None, names=names, level=level)
1153-
else: # root == "require"
1154-
importlib.import_module(module)
1155-
require(module, self.module_name,
1156-
assignments=assignments, prefix=prefix)
1167+
1168+
elif require(ast_module, self.module, assignments=assignments,
1169+
prefix=prefix):
1170+
# Actually calling `require` is necessary for macro expansions
1171+
# occurring during compilation.
1172+
self.imports['hy.macros'].update([None])
1173+
# The `require` we're creating in AST is the same as above, but used at
1174+
# run-time (e.g. when modules are loaded via bytecode).
1175+
ret += self.compile(HyExpression([
1176+
HySymbol('hy.macros.require'),
1177+
HyString(ast_module),
1178+
HySymbol('None'),
1179+
HyKeyword('assignments'),
1180+
(HyString("ALL") if assignments == "ALL" else
1181+
[[HyString(k), HyString(v)] for k, v in assignments]),
1182+
HyKeyword('prefix'),
1183+
HyString(prefix)]).replace(expr))
11571184

11581185
return ret
11591186

@@ -1484,7 +1511,8 @@ def compile_class_expression(self, expr, root, name, rest):
14841511
[x for pair in attrs[0] for x in pair]).replace(attrs)))
14851512

14861513
for e in body:
1487-
e = self.compile(self._rewire_init(macroexpand(e, self)))
1514+
e = self.compile(self._rewire_init(
1515+
macroexpand(e, self.module, self)))
14881516
bodyr += e + e.expr_as_stmt()
14891517

14901518
return bases + asty.ClassDef(
@@ -1520,28 +1548,24 @@ def compile_dispatch_tag_macro(self, expr, root, tag, arg):
15201548
return self.compile(tag_macroexpand(
15211549
HyString(mangle(tag)).replace(tag),
15221550
arg,
1523-
self))
1524-
1525-
_namespaces = {}
1551+
self.module))
15261552

15271553
@special(["eval-and-compile", "eval-when-compile"], [many(FORM)])
15281554
def compile_eval_and_compile(self, expr, root, body):
15291555
new_expr = HyExpression([HySymbol("do").replace(expr[0])]).replace(expr)
1530-
if self.module_name not in self._namespaces:
1531-
# Initialize a compile-time namespace for this module.
1532-
self._namespaces[self.module_name] = {
1533-
'hy': hy, '__name__': self.module_name}
1556+
15341557
hy.importer.hy_eval(new_expr + body,
1535-
self._namespaces[self.module_name],
1536-
self.module_name)
1558+
self.module.__dict__,
1559+
self.module)
1560+
15371561
return (self._compile_branch(body)
15381562
if ast_str(root) == "eval_and_compile"
15391563
else Result())
15401564

15411565
@builds_model(HyExpression)
15421566
def compile_expression(self, expr):
15431567
# Perform macro expansions
1544-
expr = macroexpand(expr, self)
1568+
expr = macroexpand(expr, self.module, self)
15451569
if not isinstance(expr, HyExpression):
15461570
# Go through compile again if the type changed.
15471571
return self.compile(expr)
@@ -1665,8 +1689,8 @@ def compile_symbol(self, symbol):
16651689
attr=ast_str(local),
16661690
ctx=ast.Load())
16671691

1668-
if self.can_use_stdlib and ast_str(symbol) in _stdlib:
1669-
self.imports[_stdlib[ast_str(symbol)]].add(ast_str(symbol))
1692+
if self.can_use_stdlib and ast_str(symbol) in self._stdlib:
1693+
self.imports[self._stdlib[ast_str(symbol)]].add(ast_str(symbol))
16701694

16711695
return asty.Name(symbol, id=ast_str(symbol), ctx=ast.Load())
16721696

@@ -1699,20 +1723,41 @@ def compile_dict(self, m):
16991723
return ret + asty.Dict(m, keys=keyvalues[::2], values=keyvalues[1::2])
17001724

17011725

1702-
def hy_compile(tree, module_name, root=ast.Module, get_expr=False):
1726+
def hy_compile(tree, module, root=ast.Module, get_expr=False):
17031727
"""
1704-
Compile a HyObject tree into a Python AST Module.
1728+
Compile a Hy tree into a Python AST tree.
1729+
1730+
Parameters
1731+
----------
1732+
module: str or types.ModuleType
1733+
Module, or name of the module, in which the Hy tree is evaluated.
17051734
1706-
If `get_expr` is True, return a tuple (module, last_expression), where
1707-
`last_expression` is the.
1735+
root: ast object, optional (ast.Module)
1736+
Root object for the Python AST tree.
1737+
1738+
get_expr: bool, optional (False)
1739+
If true, return a tuple with `(root_obj, last_expression)`.
1740+
1741+
Returns
1742+
-------
1743+
out : A Python AST tree
17081744
"""
17091745

1746+
if isinstance(module, string_types):
1747+
if module.startswith('<') and module.endswith('>'):
1748+
module = types.ModuleType(module)
1749+
else:
1750+
module = importlib.import_module(ast_str(module, piecewise=True))
1751+
if not inspect.ismodule(module):
1752+
raise TypeError('Invalid module type: {}'.format(type(module)))
1753+
1754+
17101755
tree = wrap_value(tree)
17111756
if not isinstance(tree, HyObject):
17121757
raise HyCompileError("`tree` must be a HyObject or capable of "
17131758
"being promoted to one")
17141759

1715-
compiler = HyASTCompiler(module_name)
1760+
compiler = HyASTCompiler(module)
17161761
result = compiler.compile(tree)
17171762
expr = result.force_expr
17181763

hy/completer.py

+8-6
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,15 @@ def __init__(self, namespace={}):
3939
self.namespace = namespace
4040
self.path = [hy.compiler._special_form_compilers,
4141
builtins.__dict__,
42-
hy.macros._hy_macros[None],
4342
namespace]
44-
self.tag_path = [hy.macros._hy_tag[None]]
45-
if '__name__' in namespace:
46-
module_name = namespace['__name__']
47-
self.path.append(hy.macros._hy_macros[module_name])
48-
self.tag_path.append(hy.macros._hy_tag[module_name])
43+
44+
self.tag_path = []
45+
46+
namespace.setdefault('__macros__', {})
47+
namespace.setdefault('__tags__', {})
48+
49+
self.path.append(namespace['__macros__'])
50+
self.tag_path.append(namespace['__tags__'])
4951

5052
def attr_matches(self, text):
5153
# Borrowed from IPython's completer

0 commit comments

Comments
 (0)