def require(source_module, target_module, assignments, prefix=""): """Load macros from `source_module` in the namespace of `target_module`. `assignments` maps old names to new names, or should be the string "ALL". If `prefix` is nonempty, it is prepended to the name of each imported macro. (This means you get macros named things like "mymacromodule.mymacro", which looks like an attribute of a module, although it's actually just a symbol with a period in its name.) This function is called from the `require` special form in the compiler. """ seen_names = set() if prefix: prefix += "." if assignments != "ALL": assignments = {mangle(str_type(k)): v for k, v in assignments} for d in _hy_macros, _hy_tag: for name, macro in d[source_module].items(): seen_names.add(name) if assignments == "ALL": d[target_module][mangle(prefix + name)] = macro elif name in assignments: d[target_module][mangle(prefix + assignments[name])] = macro if assignments != "ALL": unseen = frozenset(assignments.keys()).difference(seen_names) if unseen: raise ImportError("cannot require names: " + repr(list(unseen)))
def __init__(self, spy=False, output_fn=None, locals=None, filename="<input>"): self.spy = spy if output_fn is None: self.output_fn = repr elif callable(output_fn): self.output_fn = output_fn else: if "." in output_fn: parts = [mangle(x) for x in output_fn.split(".")] module, f = '.'.join(parts[:-1]), parts[-1] self.output_fn = getattr(importlib.import_module(module), f) else: self.output_fn = __builtins__[mangle(output_fn)] code.InteractiveConsole.__init__(self, locals=locals, filename=filename) # Pre-mangle symbols for repl recent results: *1, *2, *3 self._repl_results_symbols = [ mangle("*{}".format(i + 1)) for i in range(3) ] self.locals.update({sym: None for sym in self._repl_results_symbols})
def compile_symbol(self, symbol): if "." in symbol: glob, local = symbol.rsplit(".", 1) if not glob: raise self._syntax_error( symbol, 'cannot access attribute on anything other than a name (in order to get attributes of expressions, use `(. <expression> {attr})` or `(.{attr} <expression>)`)' .format(attr=local)) if not local: raise self._syntax_error(symbol, 'cannot access empty attribute') glob = Symbol(glob).replace(symbol) ret = self.compile_symbol(glob) return asty.Attribute(symbol, value=ret, attr=mangle(local), ctx=ast.Load()) if mangle(symbol) in ("None", "False", "True"): return asty.Constant(symbol, value=ast.literal_eval(mangle(symbol))) return asty.Name(symbol, id=mangle(symbol), ctx=ast.Load())
def __init__(self, spy=False, output_fn=None, locals=None, filename="<stdin>"): # Create a proper module for this REPL so that we can obtain it easily # (e.g. using `importlib.import_module`). # We let `InteractiveConsole` initialize `self.locals` when it's # `None`. super(HyREPL, self).__init__(locals=locals, filename=filename) module_name = self.locals.get('__name__', '__console__') # Make sure our newly created module is properly introduced to # `sys.modules`, and consistently use its namespace as `self.locals` # from here on. self.module = sys.modules.setdefault(module_name, types.ModuleType(module_name)) self.module.__dict__.update(self.locals) self.locals = self.module.__dict__ # Load cmdline-specific macros. require('hy.cmdline', self.module, assignments='ALL') self.hy_compiler = HyASTCompiler(self.module) self.cmdline_cache = {} self.compile = HyCommandCompiler(self.module, self.locals, ast_callback=self.ast_callback, hy_compiler=self.hy_compiler, cmdline_cache=self.cmdline_cache) self.spy = spy self.last_value = None self.print_last_value = True if output_fn is None: self.output_fn = repr elif callable(output_fn): self.output_fn = output_fn else: if "." in output_fn: parts = [mangle(x) for x in output_fn.split(".")] module, f = '.'.join(parts[:-1]), parts[-1] self.output_fn = getattr(importlib.import_module(module), f) else: self.output_fn = getattr(builtins, mangle(output_fn)) # Pre-mangle symbols for repl recent results: *1, *2, *3 self._repl_results_symbols = [ mangle("*{}".format(i + 1)) for i in range(3) ] self.locals.update({sym: None for sym in self._repl_results_symbols}) # Allow access to the running REPL instance self.locals['_hy_repl'] = self
def _(fn): _name = mangle('#{}'.format(name)) if not PY3: _name = _name.encode('UTF-8') fn.__name__ = _name module_name = fn.__module__ if module_name.startswith("hy.core"): module_name = None _hy_tag[module_name][mangle(name)] = fn return fn
def __init__(self, spy=False, output_fn=None, locals=None, filename="<stdin>"): # Create a proper module for this REPL so that we can obtain it easily # (e.g. using `importlib.import_module`). # We let `InteractiveConsole` initialize `self.locals` when it's # `None`. super(HyREPL, self).__init__(locals=locals, filename=filename) module_name = self.locals.get('__name__', '__console__') # Make sure our newly created module is properly introduced to # `sys.modules`, and consistently use its namespace as `self.locals` # from here on. self.module = sys.modules.setdefault(module_name, types.ModuleType(module_name)) self.module.__dict__.update(self.locals) self.locals = self.module.__dict__ # Load cmdline-specific macros. require('hy.cmdline', self.module, assignments='ALL') self.hy_compiler = HyASTCompiler(self.module) self.cmdline_cache = {} self.compile = HyCommandCompiler(self.module, self.locals, ast_callback=self.ast_callback, hy_compiler=self.hy_compiler, cmdline_cache=self.cmdline_cache) self.spy = spy self.last_value = None self.print_last_value = True if output_fn is None: self.output_fn = repr elif callable(output_fn): self.output_fn = output_fn else: if "." in output_fn: parts = [mangle(x) for x in output_fn.split(".")] module, f = '.'.join(parts[:-1]), parts[-1] self.output_fn = getattr(importlib.import_module(module), f) else: self.output_fn = __builtins__[mangle(output_fn)] # Pre-mangle symbols for repl recent results: *1, *2, *3 self._repl_results_symbols = [mangle("*{}".format(i + 1)) for i in range(3)] self.locals.update({sym: None for sym in self._repl_results_symbols}) # Allow access to the running REPL instance self.locals['_hy_repl'] = self
def _(fn): _name = mangle('#{}'.format(name)) fn = rename_function(fn, _name) module = inspect.getmodule(fn) module_name = module.__name__ if module_name.startswith("hy.core"): module_name = None module_tags = module.__dict__.setdefault('__tags__', {}) module_tags[mangle(name)] = fn return fn
def macro(name): """Decorator to define a macro called `name`. This stores the macro `name` in the namespace for the module where it is defined. If the module where it is defined is in `hy.core`, then the macro is stored in the default `None` namespace. This function is called from the `defmacro` special form in the compiler. """ name = mangle(name) def _(fn): fn.__name__ = '({})'.format(name) try: fn._hy_macro_pass_compiler = hy.inspect.has_kwargs(fn) except Exception: # An exception might be raised if fn has arguments with # names that are invalid in Python. fn._hy_macro_pass_compiler = False module_name = fn.__module__ if module_name.startswith("hy.core"): module_name = None _hy_macros[module_name][name] = fn return fn return _
def __call__(self, data, default=_sentinel): from hy.lex import mangle try: return data[mangle(self.name)] except KeyError: if default is Keyword._sentinel: raise return default
def _(fn): _name = mangle('#{}'.format(name)) if not PY3: _name = _name.encode('UTF-8') fn.__name__ = _name module = inspect.getmodule(fn) module_name = module.__name__ if module_name.startswith("hy.core"): module_name = None module_tags = module.__dict__.setdefault('__tags__', {}) module_tags[mangle(name)] = fn return fn
def _(fn): _name = mangle('#{}'.format(name)) if not PY3: _name = _name.encode('UTF-8') fn = rename_function(fn, _name) module = inspect.getmodule(fn) module_name = module.__name__ if module_name.startswith("hy.core"): module_name = None module_tags = module.__dict__.setdefault('__tags__', {}) module_tags[mangle(name)] = fn return fn
def __init__(self, spy=False, output_fn=None, locals=None, filename="<input>"): super(HyREPL, self).__init__(locals=locals, filename=filename) # Create a proper module for this REPL so that we can obtain it easily # (e.g. using `importlib.import_module`). # Also, make sure it's properly introduced to `sys.modules` and # consistently use its namespace as `locals` from here on. module_name = self.locals.get('__name__', '__console__') self.module = sys.modules.setdefault(module_name, types.ModuleType(module_name)) self.module.__dict__.update(self.locals) self.locals = self.module.__dict__ # 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: self.output_fn = repr elif callable(output_fn): self.output_fn = output_fn else: if "." in output_fn: parts = [mangle(x) for x in output_fn.split(".")] module, f = '.'.join(parts[:-1]), parts[-1] self.output_fn = getattr(importlib.import_module(module), f) else: self.output_fn = __builtins__[mangle(output_fn)] # Pre-mangle symbols for repl recent results: *1, *2, *3 self._repl_results_symbols = [ mangle("*{}".format(i + 1)) for i in range(3) ] self.locals.update({sym: None for sym in self._repl_results_symbols})
def _compile_collect(self, exprs, with_kwargs=False, dict_display=False): """Collect the expression contexts from a list of compiled expression. This returns a list of the expression contexts, and the sum of the Result objects passed as arguments. """ compiled_exprs = [] ret = Result() keywords = [] exprs_iter = iter(exprs) for expr in exprs_iter: if is_unpack("mapping", expr): ret += self.compile(expr[1]) if dict_display: compiled_exprs.append(None) compiled_exprs.append(ret.force_expr) elif with_kwargs: keywords.append( asty.keyword(expr, arg=None, value=ret.force_expr)) elif with_kwargs and isinstance(expr, Keyword): if keyword.iskeyword(expr.name): raise self._syntax_error( expr, "keyword argument cannot be Python reserved word") try: value = next(exprs_iter) except StopIteration: raise self._syntax_error( expr, "Keyword argument {kw} needs a value.".format(kw=expr)) if not expr: raise self._syntax_error( expr, "Can't call a function with the empty keyword") compiled_value = self.compile(value) ret += compiled_value arg = str(expr)[1:] keywords.append( asty.keyword(expr, arg=mangle(arg), value=compiled_value.force_expr)) else: ret += self.compile(expr) compiled_exprs.append(ret.force_expr) return compiled_exprs, ret, keywords
def _error_wrap(self, error_fn, exc_info_override=False, *args, **kwargs): sys.last_type, sys.last_value, sys.last_traceback = sys.exc_info() if exc_info_override: # Use a traceback that doesn't have the REPL frames. sys.last_type = self.locals.get('_hy_last_type', sys.last_type) sys.last_value = self.locals.get('_hy_last_value', sys.last_value) sys.last_traceback = self.locals.get('_hy_last_traceback', sys.last_traceback) sys.excepthook(sys.last_type, sys.last_value, sys.last_traceback) self.locals[mangle("*e")] = sys.last_value
def macroexpand(tree, compiler, once=False): """Expand the toplevel macros for the `tree`. Load the macros from the given `compiler.module_name`, then expand the (top-level) macros in `tree` until we no longer can. """ load_macros(compiler.module_name) while True: if not isinstance(tree, HyExpression) or tree == []: break fn = tree[0] if fn in ("quote", "quasiquote") or not isinstance(fn, HySymbol): break fn = mangle(fn) m = _hy_macros[compiler.module_name].get(fn) or _hy_macros[None].get( fn) if not m: break opts = {} if m._hy_macro_pass_compiler: opts['compiler'] = compiler try: m_copy = make_empty_fn_copy(m) m_copy(compiler.module_name, *tree[1:], **opts) except TypeError as e: msg = "expanding `" + str(tree[0]) + "': " msg += str(e).replace("<lambda>()", "", 1).strip() raise HyMacroExpansionError(tree, msg) try: obj = m(compiler.module_name, *tree[1:], **opts) except HyTypeError as e: if e.expression is None: e.expression = tree raise except Exception as e: msg = "expanding `" + str(tree[0]) + "': " + repr(e) raise HyMacroExpansionError(tree, msg) tree = replace_hy_obj(obj, tree) if once: break tree = wrap_value(tree) return tree
def rename(self, new_name): """Rename the Result's temporary variables to a `new_name`. We know how to handle ast.Names and ast.FunctionDefs. """ new_name = mangle(new_name) for var in self.temp_variables: if isinstance(var, ast.Name): var.id = new_name var.arg = new_name elif isinstance(var, (ast.FunctionDef, ast.AsyncFunctionDef)): var.name = new_name else: raise TypeError("Don't know how to rename a %s!" % (var.__class__.__name__)) self.temp_variables = []
def _error_wrap(self, error_fn, exc_info_override=False, *args, **kwargs): sys.last_type, sys.last_value, sys.last_traceback = sys.exc_info() if exc_info_override: # Use a traceback that doesn't have the REPL frames. sys.last_type = self.locals.get('_hy_last_type', sys.last_type) sys.last_value = self.locals.get('_hy_last_value', sys.last_value) sys.last_traceback = self.locals.get('_hy_last_traceback', sys.last_traceback) # Sadly, this method in Python 2.7 ignores an overridden `sys.excepthook`. if sys.excepthook is sys.__excepthook__: error_fn(*args, **kwargs) else: sys.excepthook(sys.last_type, sys.last_value, sys.last_traceback) self.locals[mangle("*e")] = sys.last_value
def macro(name): """Decorator to define a macro called `name`. """ name = mangle(name) def _(fn): fn = rename_function(fn, name) try: fn._hy_macro_pass_compiler = has_kwargs(fn) except Exception: # An exception might be raised if fn has arguments with # names that are invalid in Python. fn._hy_macro_pass_compiler = False module = inspect.getmodule(fn) module_macros = module.__dict__.setdefault('__macros__', {}) module_macros[name] = fn return fn return _
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, str): if module.startswith("<") and module.endswith(">"): module = types.ModuleType(module) else: module = importlib.import_module(mangle(module)) 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 add(self, target, new_name=None): """Add a new let-binding target, mapped to a new, unique name.""" if isinstance(target, (str, Symbol)): if "." in target: raise ValueError("binding target may not contain a dot") name = mangle(target) if new_name is None: new_name = self.compiler.get_anon_var(f"_hy_let_{name}") self.bindings[name] = new_name if isinstance(target, Symbol): return Symbol(new_name).replace(target) return new_name if new_name is not None: raise ValueError("cannot specify name for compound targets") if isinstance(target, List): return List(map(self.add, target)).replace(target) if ( isinstance(target, Expression) and target and target[0] in (Symbol(","), Symbol("unpack-iterable")) ): return Expression([target[0], *map(self.add, target[1:])]).replace(target) raise ValueError(f"invalid binding target: {type(target)}")
def require(source_module, target_module, assignments, prefix=""): """Load macros from one module into the namespace of another. This function is called from the `require` special form in the compiler. Parameters ---------- source_module: str or types.ModuleType The module from which macros are to be imported. target_module: str, types.ModuleType or None The module into which the macros will be loaded. If `None`, then the caller's namespace. The latter is useful during evaluation of generated AST/bytecode. assignments: str or list of tuples of strs The string "ALL" or a list of macro name and alias pairs. prefix: str, optional ("") If nonempty, its value is prepended to the name of each imported macro. This allows one to emulate namespaced macros, like "mymacromodule.mymacro", which looks like an attribute of a module. Returns ------- out: boolean Whether or not macros and tags were actually transferred. """ if target_module is None: parent_frame = inspect.stack()[1][0] target_namespace = parent_frame.f_globals target_module = target_namespace.get('__name__', None) elif isinstance(target_module, string_types): target_module = importlib.import_module(target_module) target_namespace = target_module.__dict__ elif inspect.ismodule(target_module): target_namespace = target_module.__dict__ else: raise HyTypeError('`target_module` is not a recognized type: {}'.format( type(target_module))) # Let's do a quick check to make sure the source module isn't actually # the module being compiled (e.g. when `runpy` executes a module's code # in `__main__`). # We use the module's underlying filename for this (when they exist), since # it's the most "fixed" attribute. if _same_modules(source_module, target_module): return False if not inspect.ismodule(source_module): try: source_module = importlib.import_module(source_module) except ImportError as e: reraise(HyRequireError, HyRequireError(e.args[0]), None) source_macros = source_module.__dict__.setdefault('__macros__', {}) source_tags = source_module.__dict__.setdefault('__tags__', {}) if len(source_module.__macros__) + len(source_module.__tags__) == 0: if assignments != "ALL": raise HyRequireError('The module {} has no macros or tags'.format( source_module)) else: return False target_macros = target_namespace.setdefault('__macros__', {}) target_tags = target_namespace.setdefault('__tags__', {}) if prefix: prefix += "." if assignments == "ALL": name_assigns = [(k, k) for k in tuple(source_macros.keys()) + tuple(source_tags.keys())] else: name_assigns = assignments for name, alias in name_assigns: _name = mangle(name) alias = mangle(prefix + alias) if _name in source_module.__macros__: target_macros[alias] = source_macros[_name] elif _name in source_module.__tags__: target_tags[alias] = source_tags[_name] else: raise HyRequireError('Could not require name {} from {}'.format( _name, source_module)) return True
def install_macro(name, fn, module_of): name = mangle(name) fn = rename_function(fn, name) (inspect.getmodule(module_of).__dict__.setdefault("__macros__", {})[name]) = fn return fn
def macroexpand(tree, module, compiler=None, once=False): """Expand the toplevel macros for the given Hy AST tree. Load the macros from the given `module`, then expand the (top-level) macros in `tree` until we no longer can. `HyExpression` resulting from macro expansions are assigned the module in which the macro function is defined (determined using `inspect.getmodule`). If the resulting `HyExpression` is itself macro expanded, then the namespace of the assigned module is checked first for a macro corresponding to the expression's head/car symbol. If the head/car symbol of such a `HyExpression` is not found among the macros of its assigned module's namespace, the outer-most namespace--e.g. the one given by the `module` parameter--is used as a fallback. Parameters ---------- tree: HyObject or list Hy AST tree. module: str or types.ModuleType Module used to determine the local namespace for macros. compiler: HyASTCompiler, optional The compiler object passed to expanded macros. once: boolean, optional Only expand the first macro in `tree`. Returns ------ out: HyObject Returns a mutated tree with macros expanded. """ if not inspect.ismodule(module): module = importlib.import_module(module) assert not compiler or compiler.module == module while isinstance(tree, HyExpression) and tree: fn = tree[0] if fn in ("quote", "quasiquote") or not isinstance(fn, HySymbol): break fn = mangle(fn) expr_modules = ( ([] if not hasattr(tree, 'module') else [tree.module]) + [module]) # Choose the first namespace with the macro. m = next((mod.__macros__[fn] for mod in expr_modules if fn in mod.__macros__), None) if not m: break opts = {} if m._hy_macro_pass_compiler: if compiler is None: from hy.compiler import HyASTCompiler compiler = HyASTCompiler(module) opts['compiler'] = compiler with macro_exceptions(module, tree, compiler): obj = m(module.__name__, *tree[1:], **opts) if isinstance(obj, HyExpression): obj.module = inspect.getmodule(m) tree = replace_hy_obj(obj, tree) if once: break tree = wrap_value(tree) return tree
def require(source_module, target_module, assignments, prefix=""): """Load macros from one module into the namespace of another. This function is called from the `require` special form in the compiler. Parameters ---------- source_module: str or types.ModuleType The module from which macros are to be imported. target_module: str, types.ModuleType or None The module into which the macros will be loaded. If `None`, then the caller's namespace. The latter is useful during evaluation of generated AST/bytecode. assignments: str or list of tuples of strs The string "ALL" or a list of macro name and alias pairs. prefix: str, optional ("") If nonempty, its value is prepended to the name of each imported macro. This allows one to emulate namespaced macros, like "mymacromodule.mymacro", which looks like an attribute of a module. Returns ------- out: boolean Whether or not macros and tags were actually transferred. """ if target_module is None: parent_frame = inspect.stack()[1][0] target_namespace = parent_frame.f_globals target_module = target_namespace.get('__name__', None) elif isinstance(target_module, str): target_module = importlib.import_module(target_module) target_namespace = target_module.__dict__ elif inspect.ismodule(target_module): target_namespace = target_module.__dict__ else: raise HyTypeError( '`target_module` is not a recognized type: {}'.format( type(target_module))) # Let's do a quick check to make sure the source module isn't actually # the module being compiled (e.g. when `runpy` executes a module's code # in `__main__`). # We use the module's underlying filename for this (when they exist), since # it's the most "fixed" attribute. if _same_modules(source_module, target_module): return False if not inspect.ismodule(source_module): try: source_module = importlib.import_module(source_module) except ImportError as e: reraise(HyRequireError, HyRequireError(e.args[0]), None) source_macros = source_module.__dict__.setdefault('__macros__', {}) source_tags = source_module.__dict__.setdefault('__tags__', {}) if len(source_module.__macros__) + len(source_module.__tags__) == 0: if assignments != "ALL": raise HyRequireError( 'The module {} has no macros or tags'.format(source_module)) else: return False target_macros = target_namespace.setdefault('__macros__', {}) target_tags = target_namespace.setdefault('__tags__', {}) if prefix: prefix += "." if assignments == "ALL": name_assigns = [ (k, k) for k in tuple(source_macros.keys()) + tuple(source_tags.keys()) ] else: name_assigns = assignments for name, alias in name_assigns: _name = mangle(name) alias = mangle(prefix + alias) if _name in source_module.__macros__: target_macros[alias] = source_macros[_name] elif _name in source_module.__tags__: target_tags[alias] = source_tags[_name] else: raise HyRequireError('Could not require name {} from {}'.format( _name, source_module)) return True
def error_handler(e, use_simple_traceback=False): self.locals[mangle("*e")] = e if use_simple_traceback: print(e, file=sys.stderr) else: self.showtraceback()
def require(source_module, target_module, assignments, prefix=""): """Load macros from one module into the namespace of another. This function is called from the `require` special form in the compiler. Parameters ---------- source_module: str or types.ModuleType The module from which macros are to be imported. target_module: str, types.ModuleType or None The module into which the macros will be loaded. If `None`, then the caller's namespace. The latter is useful during evaluation of generated AST/bytecode. assignments: str or list of tuples of strs The string "ALL" or a list of macro name and alias pairs. prefix: str, optional ("") If nonempty, its value is prepended to the name of each imported macro. This allows one to emulate namespaced macros, like "mymacromodule.mymacro", which looks like an attribute of a module. Returns ------- out: boolean Whether or not macros were actually transferred. """ if target_module is None: parent_frame = inspect.stack()[1][0] target_namespace = parent_frame.f_globals target_module = target_namespace.get('__name__', None) elif isinstance(target_module, str): target_module = importlib.import_module(target_module) target_namespace = target_module.__dict__ elif inspect.ismodule(target_module): target_namespace = target_module.__dict__ else: raise HyTypeError( '`target_module` is not a recognized type: {}'.format( type(target_module))) # Let's do a quick check to make sure the source module isn't actually # the module being compiled (e.g. when `runpy` executes a module's code # in `__main__`). # We use the module's underlying filename for this (when they exist), since # it's the most "fixed" attribute. if _same_modules(source_module, target_module): return False if not inspect.ismodule(source_module): try: if source_module.startswith("."): source_dirs = source_module.split(".") target_dirs = (getattr(target_module, "__name__", target_module).split(".")) while (len(source_dirs) > 1 and source_dirs[0] == "" and target_dirs): source_dirs.pop(0) target_dirs.pop() package = ".".join(target_dirs + source_dirs[:-1]) else: package = None source_module = importlib.import_module(source_module, package) except ImportError as e: raise HyRequireError(e.args[0]).with_traceback(None) source_macros = source_module.__dict__.setdefault('__macros__', {}) if not source_module.__macros__: if assignments != "ALL": for name, alias in assignments: try: require(f"{source_module.__name__}.{mangle(name)}", target_module, "ALL", prefix=alias) except HyRequireError as e: raise HyRequireError(f"Cannot import name '{name}'" f" from '{source_module.__name__}'" f" ({source_module.__file__})") return True else: return False target_macros = target_namespace.setdefault('__macros__', {}) if prefix: prefix += "." if assignments == "ALL": name_assigns = [(k, k) for k in source_macros.keys()] else: name_assigns = assignments for name, alias in name_assigns: _name = mangle(name) alias = mangle('#' + prefix + unmangle(alias)[1:] if unmangle(alias). startswith('#') else prefix + alias) if _name in source_module.__macros__: target_macros[alias] = source_macros[_name] else: raise HyRequireError('Could not require name {} from {}'.format( _name, source_module)) return True
def macroexpand(tree, module, compiler=None, once=False, result_ok=True): """Expand the toplevel macros for the given Hy AST tree. Load the macros from the given `module`, then expand the (top-level) macros in `tree` until we no longer can. `Expression` resulting from macro expansions are assigned the module in which the macro function is defined (determined using `inspect.getmodule`). If the resulting `Expression` is itself macro expanded, then the namespace of the assigned module is checked first for a macro corresponding to the expression's head/car symbol. If the head/car symbol of such a `Expression` is not found among the macros of its assigned module's namespace, the outer-most namespace--e.g. the one given by the `module` parameter--is used as a fallback. Args: tree (Union[Object, list]): Hy AST tree. module (Union[str, ModuleType]): Module used to determine the local namespace for macros. compiler (Optional[HyASTCompiler] ): The compiler object passed to expanded macros. Defaults to None once (bool): Only expand the first macro in `tree`. Defaults to False result_ok (bool): Whether or not it's okay to return a compiler `Result` instance. Defaults to True. Returns: Union[Object, Result]: A mutated tree with macros expanded. """ if not inspect.ismodule(module): module = importlib.import_module(module) assert not compiler or compiler.module == module while isinstance(tree, Expression) and tree: fn = tree[0] if fn in ("quote", "quasiquote") or not isinstance(fn, Symbol): break fn = mangle(fn) expr_modules = ([] if not hasattr(tree, "module") else [tree.module ]) + [module] expr_modules.append(builtins) # Choose the first namespace with the macro. m = next( (mod.__macros__[fn] for mod in expr_modules if fn in getattr(mod, "__macros__", ())), None, ) if not m: break with MacroExceptions(module, tree, compiler): if compiler: compiler.this = tree obj = m(compiler, *tree[1:]) if isinstance(obj, (hy.compiler.Result, AST)): return obj if result_ok else tree if isinstance(obj, Expression): obj.module = inspect.getmodule(m) tree = replace_hy_obj(obj, tree) if once: break tree = as_model(tree) return tree
def __init__(self, spy=False, output_fn=None, locals=None, filename="<stdin>"): # Create a proper module for this REPL so that we can obtain it easily # (e.g. using `importlib.import_module`). # We let `InteractiveConsole` initialize `self.locals` when it's # `None`. super(HyREPL, self).__init__(locals=locals, filename=filename) module_name = self.locals.get('__name__', '__console__') # Make sure our newly created module is properly introduced to # `sys.modules`, and consistently use its namespace as `self.locals` # from here on. self.module = sys.modules.setdefault(module_name, types.ModuleType(module_name)) self.module.__dict__.update(self.locals) self.locals = self.module.__dict__ if os.environ.get("HYSTARTUP"): try: loader = HyLoader("__hystartup__", os.environ.get("HYSTARTUP")) spec = importlib.util.spec_from_loader(loader.name, loader) mod = importlib.util.module_from_spec(spec) sys.modules.setdefault(mod.__name__, mod) loader.exec_module(mod) imports = mod.__dict__.get('__all__', [ name for name in mod.__dict__ if not name.startswith("_") ]) imports = {name: mod.__dict__[name] for name in imports} spy = spy or imports.get("repl_spy") output_fn = output_fn or imports.get("repl_output_fn") # Load imports and defs self.locals.update(imports) # load module macros require(mod, self.module, assignments='ALL') except Exception as e: print(e) # Load cmdline-specific macros. require('hy.cmdline', self.module, assignments='ALL') self.hy_compiler = HyASTCompiler(self.module) self.cmdline_cache = {} self.compile = HyCommandCompiler(self.module, self.locals, ast_callback=self.ast_callback, hy_compiler=self.hy_compiler, cmdline_cache=self.cmdline_cache) self.spy = spy self.last_value = None self.print_last_value = True if output_fn is None: self.output_fn = hy.repr elif callable(output_fn): self.output_fn = output_fn elif "." in output_fn: parts = [mangle(x) for x in output_fn.split(".")] module, f = '.'.join(parts[:-1]), parts[-1] self.output_fn = getattr(importlib.import_module(module), f) else: self.output_fn = getattr(builtins, mangle(output_fn)) # Pre-mangle symbols for repl recent results: *1, *2, *3 self._repl_results_symbols = [ mangle("*{}".format(i + 1)) for i in range(3) ] self.locals.update({sym: None for sym in self._repl_results_symbols}) # Allow access to the running REPL instance self.locals['_hy_repl'] = self # Compile an empty statement to load the standard prelude exec_ast = hy_compile(hy_parse(''), self.module, compiler=self.hy_compiler, import_stdlib=True) if self.ast_callback: self.ast_callback(exec_ast, None)
def hy_compile( tree, module, root=ast.Module, get_expr=False, compiler=None, filename=None, source=None, import_stdlib=True, ): """Compile a hy.models.Object tree into a Python AST Module. Args: tree (Object): The Hy AST object to compile. module (Union[str, types.ModuleType]): Module, or name of the module, in which the Hy tree is evaluated. The module associated with `compiler` takes priority over this value. root (Type[ast.AST]): Root object for the Python AST tree. get_expr (bool): If true, return a tuple with `(root_obj, last_expression)`. compiler (Optional[HyASTCompiler]): An existing Hy compiler to use for compilation. Also serves as the `module` value when given. filename (Optional[str]): The filename corresponding to the source for `tree`. This will be overridden by the `filename` field of `tree`, if any; otherwise, it defaults to "<string>". When `compiler` is given, its `filename` field value is always used. source (Optional[str]): A string containing the source code for `tree`. This will be overridden by the `source` field of `tree`, if any; otherwise, if `None`, an attempt will be made to obtain it from the module given by `module`. When `compiler` is given, its `source` field value is always used. Returns: ast.AST: A Python AST tree """ module = get_compiler_module(module, compiler, False) if isinstance(module, str): if module.startswith("<") and module.endswith(">"): module = types.ModuleType(module) else: module = importlib.import_module(mangle(module)) if not inspect.ismodule(module): raise TypeError("Invalid module type: {}".format(type(module))) filename = getattr(tree, "filename", filename) source = getattr(tree, "source", source) tree = as_model(tree) if not isinstance(tree, Object): raise TypeError( "`tree` must be a hy.models.Object or capable of " "being promoted to one" ) compiler = compiler or HyASTCompiler(module, filename=filename, source=source) if import_stdlib: # Import hy for compile time, but save the compiled AST. stdlib_ast = compiler.compile( mkexpr("eval-and-compile", mkexpr("import", "hy")) ) with compiler.scope: result = compiler.compile(tree) expr = result.force_expr if not get_expr: result += result.expr_as_stmt() body = [] if issubclass(root, ast.Module): # Pull out a single docstring and prepend to the resulting body. if ( result.stmts and isinstance(result.stmts[0], ast.Expr) and isinstance(result.stmts[0].value, ast.Str) ): body += [result.stmts.pop(0)] # Pull out any __future__ imports, since they are required to be at the beginning. while ( result.stmts and isinstance(result.stmts[0], ast.ImportFrom) and result.stmts[0].module == "__future__" ): body += [result.stmts.pop(0)] # Import hy for runtime. if import_stdlib: body += stdlib_ast.stmts body += result.stmts ret = root(body=body, type_ignores=[]) if get_expr: expr = ast.Expression(body=expr) ret = (ret, expr) return ret
def compile_expression(self, expr, *, allow_annotation_expression=False): # Perform macro expansions expr = macroexpand(expr, self.module, self) if isinstance(expr, (Result, ast.AST)): # Use this as-is. return expr elif not isinstance(expr, Expression): # Go through compile again if we have a different type of model. return self.compile(expr) if not expr: raise self._syntax_error( expr, "empty expressions are not allowed at top level" ) args = list(expr) root = args.pop(0) func = None if isinstance(root, Symbol) and root.startswith("."): # (.split "test test") -> "test test".split() # (.a.b.c x v1 v2) -> (.c (. x a b) v1 v2) -> x.a.b.c(v1, v2) # Get the method name (the last named attribute # in the chain of attributes) attrs = [ Symbol(a).replace(root) if a else None for a in root.split(".")[1:] ] if not all(attrs): raise self._syntax_error(expr, "cannot access empty attribute") root = attrs.pop() # Get the object we're calling the method on # (extracted with the attribute access DSL) # Skip past keywords and their arguments. try: kws, obj, rest = ( many(KEYWORD + FORM | unpack("mapping")) + FORM + many(FORM) ).parse(args) except NoParseError: raise self._syntax_error(expr, "attribute access requires object") # Reconstruct `args` to exclude `obj`. args = [x for p in kws for x in p] + list(rest) if is_unpack("iterable", obj): raise self._syntax_error( obj, "can't call a method on an unpacking form" ) func = self.compile(Expression([Symbol(".").replace(root), obj] + attrs)) # And get the method func += asty.Attribute( root, value=func.force_expr, attr=mangle(root), ctx=ast.Load() ) if is_annotate_expression(root): # Flatten and compile the annotation expression. ann_expr = Expression(root + args).replace(root) return self.compile_expression(ann_expr, allow_annotation_expression=True) if not func: func = self.compile(root) args, ret, keywords = self._compile_collect(args, with_kwargs=True) return ( func + ret + asty.Call(expr, func=func.expr, args=args, keywords=keywords) )
def macroexpand(tree, module, compiler=None, once=False): """Expand the toplevel macros for the given Hy AST tree. Load the macros from the given `module`, then expand the (top-level) macros in `tree` until we no longer can. `HyExpression` resulting from macro expansions are assigned the module in which the macro function is defined (determined using `inspect.getmodule`). If the resulting `HyExpression` is itself macro expanded, then the namespace of the assigned module is checked first for a macro corresponding to the expression's head/car symbol. If the head/car symbol of such a `HyExpression` is not found among the macros of its assigned module's namespace, the outer-most namespace--e.g. the one given by the `module` parameter--is used as a fallback. Parameters ---------- tree: HyObject or list Hy AST tree. module: str or types.ModuleType Module used to determine the local namespace for macros. compiler: HyASTCompiler, optional The compiler object passed to expanded macros. once: boolean, optional Only expand the first macro in `tree`. Returns ------ out: HyObject Returns a mutated tree with macros expanded. """ if not inspect.ismodule(module): module = importlib.import_module(module) assert not compiler or compiler.module == module while True: if not isinstance(tree, HyExpression) or tree == []: break fn = tree[0] if fn in ("quote", "quasiquote") or not isinstance(fn, HySymbol): break fn = mangle(fn) expr_modules = (([] if not hasattr(tree, 'module') else [tree.module]) + [module]) # Choose the first namespace with the macro. m = next((mod.__macros__[fn] for mod in expr_modules if fn in mod.__macros__), None) if not m: break opts = {} if m._hy_macro_pass_compiler: if compiler is None: from hy.compiler import HyASTCompiler compiler = HyASTCompiler(module) opts['compiler'] = compiler with macro_exceptions(module, tree, compiler): obj = m(module.__name__, *tree[1:], **opts) if isinstance(obj, HyExpression): obj.module = inspect.getmodule(m) tree = replace_hy_obj(obj, tree) if once: break tree = wrap_value(tree) return tree