Merge pull request #1700 from brandonwillard/reuse-compiler-instances

Reuse Hy compiler instances
This commit is contained in:
Kodi Arfer 2018-11-28 19:04:41 -05:00 committed by GitHub
commit 9efd2a9d61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 58 additions and 31 deletions

View File

@ -9,6 +9,8 @@ Removals
New Features
------------------------------
* `eval` / `hy_eval` and `hy_compile` now accept an optional `compiler` argument
that enables the use of an existing `HyASTCompiler` instance.
* Keyword objects (not just literal keywords) can be called, as
shorthand for `(get obj :key)`, and they accept a default value
as a second argument.

View File

@ -195,7 +195,9 @@ eval
``eval`` evaluates a quoted expression and returns the value. The optional
second and third arguments specify the dictionary of globals to use and the
module name. The globals dictionary defaults to ``(local)`` and the module name
defaults to the name of the current module.
defaults to the name of the current module. An optional fourth keyword parameter,
``compiler``, allows one to re-use an existing ``HyASTCompiler`` object for the
compilation step.
.. code-block:: clj
@ -1403,4 +1405,3 @@ are available. Some of their names have been changed:
- ``dropwhile`` has been changed to ``drop-while``
- ``filterfalse`` has been changed to ``remove``

View File

@ -20,7 +20,8 @@ import astor.code_gen
import hy
from hy.lex import hy_parse, mangle
from hy.lex.exceptions import LexException, PrematureEndOfInput
from hy.compiler import HyTypeError, hy_compile, hy_eval
from hy.compiler import HyASTCompiler, hy_compile, hy_eval
from hy.errors import HyTypeError
from hy.importer import runhy
from hy.completer import completion, Completer
from hy.macros import macro, require
@ -69,6 +70,8 @@ class HyREPL(code.InteractiveConsole, object):
# Load cmdline-specific macros.
require('hy.cmdline', module_name, assignments='ALL')
self.hy_compiler = HyASTCompiler(self.module)
self.spy = spy
if output_fn is None:
@ -117,7 +120,10 @@ class HyREPL(code.InteractiveConsole, object):
new_ast = ast.Module(main_ast.body +
[ast.Expr(expr_ast.body)])
print(astor.to_source(new_ast))
value = hy_eval(do, self.locals, self.module, ast_callback)
value = hy_eval(do, self.locals,
ast_callback=ast_callback,
compiler=self.hy_compiler)
except HyTypeError as e:
if e.source is None:
e.source = source

View File

@ -1779,18 +1779,38 @@ class HyASTCompiler(object):
return ret + asty.Dict(m, keys=keyvalues[::2], values=keyvalues[1::2])
def hy_eval(hytree, locals=None, module=None, ast_callback=None):
"""Evaluates a quoted expression and returns the value.
def get_compiler_module(module=None, compiler=None, calling_frame=False):
"""Get a module object from a compiler, given module object,
string name of a module, and (optionally) the calling frame; otherwise,
raise an error."""
module = getattr(compiler, 'module', None) or module
if isinstance(module, string_types):
if module.startswith('<') and module.endswith('>'):
module = types.ModuleType(module)
else:
module = importlib.import_module(ast_str(module, piecewise=True))
if calling_frame and not module:
module = calling_module(n=2)
if not inspect.ismodule(module):
raise TypeError('Invalid module type: {}'.format(type(module)))
return module
def hy_eval(hytree, locals=None, module=None, ast_callback=None,
compiler=None):
"""Evaluates a quoted expression and returns the value.
Examples
--------
=> (eval '(print "Hello World"))
"Hello World"
If you want to evaluate a string, use ``read-str`` to convert it to a
form first:
=> (eval (read-str "(+ 1 1)"))
2
@ -1806,25 +1826,25 @@ def hy_eval(hytree, locals=None, module=None, ast_callback=None):
module: str or types.ModuleType, optional
Module, or name of the module, to which the Hy tree is assigned and
the global values are taken.
Defaults to the calling frame's module, if any, and '__eval__'
otherwise.
The module associated with `compiler` takes priority over this value.
When neither `module` nor `compiler` is specified, the calling frame's
module is used.
ast_callback: callable, optional
A callback that is passed the Hy compiled tree and resulting
expression object, in that order, after compilation but before
evaluation.
compiler: HyASTCompiler, optional
An existing Hy compiler to use for compilation. Also serves as
the `module` value when given.
Returns
-------
out : Result of evaluating the Hy compiled tree.
"""
if module is None:
module = calling_module()
if isinstance(module, string_types):
module = importlib.import_module(ast_str(module, piecewise=True))
elif not inspect.ismodule(module):
raise TypeError('Invalid module type: {}'.format(type(module)))
module = get_compiler_module(module, compiler, True)
if locals is None:
frame = inspect.stack()[1][0]
@ -1833,7 +1853,8 @@ def hy_eval(hytree, locals=None, module=None, ast_callback=None):
if not isinstance(locals, dict):
raise TypeError("Locals must be a dictionary")
_ast, expr = hy_compile(hytree, module, get_expr=True)
_ast, expr = hy_compile(hytree, module=module, get_expr=True,
compiler=compiler)
# Spoof the positions in the generated ast...
for node in ast.walk(_ast):
@ -1856,14 +1877,15 @@ def hy_eval(hytree, locals=None, module=None, ast_callback=None):
return eval(ast_compile(expr, "<eval>", "eval"), globals, locals)
def hy_compile(tree, module, root=ast.Module, get_expr=False):
"""
Compile a Hy tree into a Python AST tree.
def hy_compile(tree, module=None, root=ast.Module, get_expr=False,
compiler=None):
"""Compile a Hy tree into a Python AST tree.
Parameters
----------
module: str or types.ModuleType
module: str or types.ModuleType, optional
Module, or name of the module, in which the Hy tree is evaluated.
The module associated with `compiler` takes priority over this value.
root: ast object, optional (ast.Module)
Root object for the Python AST tree.
@ -1871,26 +1893,22 @@ def hy_compile(tree, module, root=ast.Module, get_expr=False):
get_expr: bool, optional (False)
If true, return a tuple with `(root_obj, last_expression)`.
compiler: HyASTCompiler, optional
An existing Hy compiler to use for compilation. Also serves as
the `module` value when given.
Returns
-------
out : A Python AST tree
"""
if isinstance(module, string_types):
if module.startswith('<') and module.endswith('>'):
module = types.ModuleType(module)
else:
module = importlib.import_module(ast_str(module, piecewise=True))
if not inspect.ismodule(module):
raise TypeError('Invalid module type: {}'.format(type(module)))
module = get_compiler_module(module, compiler, False)
tree = wrap_value(tree)
if not isinstance(tree, HyObject):
raise HyCompileError("`tree` must be a HyObject or capable of "
"being promoted to one")
compiler = HyASTCompiler(module)
compiler = compiler or HyASTCompiler(module)
result = compiler.compile(tree)
expr = result.force_expr