From 15c68455ec7b939da435bb90e655af6167d07177 Mon Sep 17 00:00:00 2001 From: "Brandon T. Willard" Date: Sat, 10 Nov 2018 22:43:39 -0600 Subject: [PATCH 1/3] Use a fixed compiler in `HyREPL` These changes make `HyREPL` use a single `HyASTCompiler` instance, instead of creating one every time a valid source string is processed. This change avoids the unnecessary re-initiation of the standard library `import` and `require` steps that currently occur within the module tracked by a `HyREPL` instance. Also, one can now pass an existing compiler instance to `hy_repl` and `hy_compiler`. Closes hylang/hy#1698. --- hy/cmdline.py | 10 +++++-- hy/compiler.py | 72 +++++++++++++++++++++++++++++++------------------- 2 files changed, 53 insertions(+), 29 deletions(-) diff --git a/hy/cmdline.py b/hy/cmdline.py index 7fdfb49..4c98ef1 100644 --- a/hy/cmdline.py +++ b/hy/cmdline.py @@ -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 diff --git a/hy/compiler.py b/hy/compiler.py index 11473d7..1b5e983 100755 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -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"), 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 From 3d0659b7231dcdeef86fbecd1584ec3aa0e1c432 Mon Sep 17 00:00:00 2001 From: "Brandon T. Willard" Date: Sun, 11 Nov 2018 15:27:43 -0600 Subject: [PATCH 2/3] Update NEWS --- NEWS.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS.rst b/NEWS.rst index 44ab3cb..3355da7 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -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. From 690416b3d6ab7c7194050f772239b53c4b2d6f86 Mon Sep 17 00:00:00 2001 From: "Brandon T. Willard" Date: Wed, 28 Nov 2018 16:56:00 -0600 Subject: [PATCH 3/3] Update description of `eval` in core.rst --- docs/language/core.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/language/core.rst b/docs/language/core.rst index d292d75..4237418 100644 --- a/docs/language/core.rst +++ b/docs/language/core.rst @@ -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`` -