Skip to content

Commit 657e1f6

Browse files
brandonwillardKodiologist
authored andcommitted
Produce Python AST for require statements and skip self requires
Closes hylang#1211.
1 parent 6194226 commit 657e1f6

15 files changed

+493
-210
lines changed

hy/cmdline.py

+23-10
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

@@ -72,6 +73,20 @@ def __init__(self, spy=False, output_fn=None, locals=None,
7273
self._repl_results_symbols = [mangle("*{}".format(i + 1)) for i in range(3)]
7374
self.locals.update({sym: None for sym in self._repl_results_symbols})
7475

76+
# Create a proper module for this REPL so that we can obtain it easily
77+
# (e.g. using `importlib.import_module`), have it be distinct from
78+
# `__main__`, and use it with `hy_compile`.
79+
module_name = self.locals['__name__']
80+
self.module = types.ModuleType(module_name)
81+
self.module.__dict__.update(self.locals)
82+
83+
self.locals = self.module.__dict__
84+
85+
sys.modules[module_name] = self.module
86+
87+
# Load cmdline-specific macros.
88+
requiref('hy.cmdline', '__console__', assignments='ALL')
89+
7590
def runsource(self, source, filename='<input>', symbol='single'):
7691
global SIMPLE_TRACEBACKS
7792

@@ -102,8 +117,7 @@ def ast_callback(main_ast, expr_ast):
102117
new_ast = ast.Module(main_ast.body +
103118
[ast.Expr(expr_ast.body)])
104119
print(astor.to_source(new_ast))
105-
value = hy_eval(do, self.locals, "__console__",
106-
ast_callback)
120+
value = hy_eval(do, self.locals, self.module, ast_callback)
107121
except HyTypeError as e:
108122
if e.source is None:
109123
e.source = source
@@ -181,7 +195,7 @@ def ideas_macro(ETname):
181195
182196
""")])
183197

184-
requiref("hy.cmdline", "__console__", assignments="ALL")
198+
185199
requiref("hy.cmdline", "__main__", assignments="ALL")
186200

187201
SIMPLE_TRACEBACKS = True
@@ -199,7 +213,7 @@ def pretty_error(func, *args, **kw):
199213

200214
def run_command(source):
201215
tree = hy_parse(source)
202-
pretty_error(hy_eval, tree, module_name="__main__")
216+
pretty_error(hy_eval, tree, None, importlib.import_module('__main__'))
203217
return 0
204218

205219

@@ -208,12 +222,12 @@ def run_repl(hr=None, **kwargs):
208222
sys.ps1 = "=> "
209223
sys.ps2 = "... "
210224

211-
namespace = {'__name__': '__console__', '__doc__': ''}
225+
if not hr:
226+
hr = HyREPL(**kwargs)
212227

213-
with completion(Completer(namespace)):
228+
namespace = hr.locals
214229

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

218232
hr.interact("{appname} {version} using "
219233
"{py}({build}) {pyversion} on {os}".format(
@@ -409,7 +423,6 @@ def hyc_main():
409423
# entry point for cmd line script "hy2py"
410424
def hy2py_main():
411425
import platform
412-
module_name = "<STDIN>"
413426

414427
options = dict(prog="hy2py", usage="%(prog)s [options] [FILE]",
415428
formatter_class=argparse.RawDescriptionHelpFormatter)
@@ -448,7 +461,7 @@ def hy2py_main():
448461
print()
449462
print()
450463

451-
_ast = pretty_error(hy_compile, hst, module_name)
464+
_ast = pretty_error(hy_compile, hst, '__main__')
452465
if options.with_ast:
453466
if PY3 and platform.system() == "Windows":
454467
_print_for_windows(astor.dump_tree(_ast))

hy/compiler.py

+84-38
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 requiref, 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 requiref, 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
@@ -283,28 +285,45 @@ def is_unpack(kind, x):
283285

284286

285287
class HyASTCompiler(object):
288+
"""A Hy-to-Python AST compiler"""
286289

287-
def __init__(self, module_name):
290+
def __init__(self, module):
291+
"""
292+
Parameters
293+
----------
294+
module: str or types.ModuleType
295+
Module in which the Hy tree is evaluated.
296+
"""
288297
self.anon_var_count = 0
289298
self.imports = defaultdict(set)
290-
self.module_name = module_name
291299
self.temp_if = None
300+
301+
if not inspect.ismodule(module):
302+
module = importlib.import_module(module)
303+
304+
self.module = module
305+
self.module_name = module.__name__
306+
292307
self.can_use_stdlib = (
293-
not module_name.startswith("hy.core")
294-
or module_name == "hy.core.macros")
308+
not self.module_name.startswith("hy.core")
309+
or self.module_name == "hy.core.macros")
310+
311+
# Load stdlib macros into the module namespace.
312+
load_macros(self.module)
313+
295314
# Everything in core needs to be explicit (except for
296315
# the core macros, which are built with the core functions).
297316
if self.can_use_stdlib and not _stdlib:
298317
# Populate _stdlib.
299318
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):
319+
for stdlib_module in hy.core.STDLIB:
320+
mod = importlib.import_module(stdlib_module)
321+
for e in map(ast_str, getattr(mod, 'EXPORTS', [])):
303322
if getattr(mod, e) is not getattr(builtins, e, ''):
304323
# Don't bother putting a name in _stdlib if it
305324
# points to a builtin with the same name. This
306325
# prevents pointless imports.
307-
_stdlib[e] = module
326+
_stdlib[e] = stdlib_module
308327

309328
def get_anon_var(self):
310329
self.anon_var_count += 1
@@ -1098,11 +1117,6 @@ def compile_unary_operator(self, expr, root, arg):
10981117
brackets(SYM, sym(":as"), _symn) |
10991118
brackets(SYM, brackets(many(_symn + maybe(sym(":as") + _symn)))))])
11001119
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-
"""
11061120
ret = Result()
11071121

11081122
for entry in entries:
@@ -1128,8 +1142,9 @@ def compile_import_or_require(self, expr, root, entries):
11281142
else:
11291143
assignments = [(k, v or k) for k, v in kids]
11301144

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

11581186
return ret
11591187

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

14861514
for e in body:
1487-
e = self.compile(self._rewire_init(macroexpand(e, self)))
1515+
e = self.compile(self._rewire_init(
1516+
macroexpand(e, self.module, self)))
14881517
bodyr += e + e.expr_as_stmt()
14891518

14901519
return bases + asty.ClassDef(
@@ -1520,28 +1549,24 @@ def compile_dispatch_tag_macro(self, expr, root, tag, arg):
15201549
return self.compile(tag_macroexpand(
15211550
HyString(mangle(tag)).replace(tag),
15221551
arg,
1523-
self))
1524-
1525-
_namespaces = {}
1552+
self.module))
15261553

15271554
@special(["eval-and-compile", "eval-when-compile"], [many(FORM)])
15281555
def compile_eval_and_compile(self, expr, root, body):
15291556
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}
1557+
15341558
hy.importer.hy_eval(new_expr + body,
1535-
self._namespaces[self.module_name],
1536-
self.module_name)
1559+
self.module.__dict__,
1560+
self.module)
1561+
15371562
return (self._compile_branch(body)
15381563
if ast_str(root) == "eval_and_compile"
15391564
else Result())
15401565

15411566
@builds_model(HyExpression)
15421567
def compile_expression(self, expr):
15431568
# Perform macro expansions
1544-
expr = macroexpand(expr, self)
1569+
expr = macroexpand(expr, self.module, self)
15451570
if not isinstance(expr, HyExpression):
15461571
# Go through compile again if the type changed.
15471572
return self.compile(expr)
@@ -1699,20 +1724,41 @@ def compile_dict(self, m):
16991724
return ret + asty.Dict(m, keys=keyvalues[::2], values=keyvalues[1::2])
17001725

17011726

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

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

1715-
compiler = HyASTCompiler(module_name)
1761+
compiler = HyASTCompiler(module)
17161762
result = compiler.compile(tree)
17171763
expr = result.force_expr
17181764

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

hy/contrib/walk.hy

+6-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
(import [hy [HyExpression HyDict]]
77
[functools [partial]]
8+
[importlib [import-module]]
89
[collections [OrderedDict]]
910
[hy.macros [macroexpand :as mexpand]]
1011
[hy.compiler [HyASTCompiler]])
@@ -42,9 +43,11 @@
4243

4344
(defn macroexpand-all [form &optional module-name]
4445
"Recursively performs all possible macroexpansions in form."
45-
(setv module-name (or module-name (calling-module-name))
46+
(setv module (or (and module-name
47+
(import-module module-name))
48+
(calling-module))
4649
quote-level [0]
47-
ast-compiler (HyASTCompiler module-name)) ; TODO: make nonlocal after dropping Python2
50+
ast-compiler (HyASTCompiler module)) ; TODO: make nonlocal after dropping Python2
4851
(defn traverse [form]
4952
(walk expand identity form))
5053
(defn expand [form]
@@ -68,7 +71,7 @@
6871
[(= (first form) (HySymbol "require"))
6972
(ast-compiler.compile form)
7073
(return)]
71-
[True (traverse (mexpand form ast-compiler))])
74+
[True (traverse (mexpand form module ast-compiler))])
7275
(if (coll? form)
7376
(traverse form)
7477
form)))

hy/core/language.hy

+7-5
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
(import [hy.models [HySymbol HyKeyword]])
2222
(import [hy.lex [LexException PrematureEndOfInput tokenize mangle unmangle]])
2323
(import [hy.compiler [HyASTCompiler]])
24-
(import [hy.importer [hy-eval :as eval]])
24+
(import [hy.importer [calling-module hy-eval :as eval]])
2525

2626
(defn butlast [coll]
2727
"Return an iterator of all but the last item in `coll`."
@@ -295,12 +295,14 @@ Return series of accumulated sums (or other binary function results)."
295295
(defn macroexpand [form]
296296
"Return the full macro expansion of `form`."
297297
(import hy.macros)
298-
(hy.macros.macroexpand form (HyASTCompiler (calling-module-name))))
298+
(setv module (calling-module))
299+
(hy.macros.macroexpand form module (HyASTCompiler module)))
299300

300301
(defn macroexpand-1 [form]
301302
"Return the single step macro expansion of `form`."
302303
(import hy.macros)
303-
(hy.macros.macroexpand-1 form (HyASTCompiler (calling-module-name))))
304+
(setv module (calling-module))
305+
(hy.macros.macroexpand-1 form module (HyASTCompiler module)))
304306

305307
(defn merge-with [f &rest maps]
306308
"Return the map of `maps` joined onto the first via the function `f`.
@@ -467,8 +469,8 @@ Even objects with the __name__ magic will work."
467469
(or a b)))
468470

469471
(setv EXPORTS
470-
'[*map accumulate butlast calling-module-name chain coll? combinations
471-
comp complement compress constantly count cycle dec distinct
472+
'[*map accumulate butlast calling-module calling-module-name chain coll?
473+
combinations comp complement compress constantly count cycle dec distinct
472474
disassemble drop drop-last drop-while empty? eval even? every? exec first
473475
filter flatten float? fraction gensym group-by identity inc input instance?
474476
integer integer? integer-char? interleave interpose islice iterable?

0 commit comments

Comments
 (0)