示例#1
0
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)))
示例#2
0
    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})
示例#3
0
文件: compiler.py 项目: stjordanis/hy
    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())
示例#4
0
    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
示例#5
0
    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
示例#6
0
文件: cmdline.py 项目: hylang/hy
    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
示例#7
0
文件: macros.py 项目: redraiment/hy
    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
示例#8
0
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 _
示例#9
0
文件: models.py 项目: etanol/hy
 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
示例#10
0
文件: macros.py 项目: xmonader/hy
    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
示例#11
0
文件: macros.py 项目: hylang/hy
    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
示例#12
0
文件: cmdline.py 项目: xmonader/hy
    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})
示例#13
0
文件: compiler.py 项目: stjordanis/hy
    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
示例#14
0
    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
示例#15
0
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
示例#16
0
文件: compiler.py 项目: stjordanis/hy
    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 = []
示例#17
0
文件: cmdline.py 项目: hylang/hy
    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
示例#18
0
文件: cmdline.py 项目: jams2/hy
    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
示例#19
0
文件: macros.py 项目: hylang/hy
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 _
示例#20
0
文件: macros.py 项目: zhaqenl/hy
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 _
示例#21
0
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
示例#22
0
 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)}")
示例#23
0
文件: macros.py 项目: hylang/hy
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
示例#24
0
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
示例#25
0
文件: macros.py 项目: redraiment/hy
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
示例#26
0
文件: macros.py 项目: redraiment/hy
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
示例#27
0
 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()
示例#28
0
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
示例#29
0
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
示例#30
0
文件: cmdline.py 项目: rajp152k/hy
    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)
示例#31
0
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
示例#32
0
    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)
        )
示例#33
0
文件: macros.py 项目: hylang/hy
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