Esempio n. 1
0
class HyASTCompilerTest(unittest.TestCase):

    @staticmethod
    def _make_expression(*args):
        h = HyExpression(args)
        h.start_line = 1
        h.end_line = 1
        h.start_column = 1
        h.end_column = 1
        return h

    def setUp(self):
        self.c = HyASTCompiler()

    def test_fn_compiler_empty_function(self):
        ret = self.c.compile_function_def(
            self._make_expression("fn", HyList()))
        self.assertEqual(ret.imports, {})

        self.assertEqual(len(ret.stmts), 1)
        stmt = ret.stmts[0]
        self.assertIsInstance(stmt, ast.FunctionDef)
        self.assertIsInstance(stmt.args, ast.arguments)
        self.assertEqual(stmt.args.vararg, None)
        self.assertEqual(stmt.args.kwarg, None)
        self.assertEqual(stmt.args.defaults, [])
        self.assertEqual(stmt.decorator_list, [])
        self.assertEqual(len(stmt.body), 1)
        self.assertIsInstance(stmt.body[0], ast.Pass)

        self.assertIsInstance(ret.expr, ast.Name)

    def test_compiler_bare_names(self):
        """
        Check that the compiler doesn't drop bare names from code branches
        """
        ret = self.c.compile(self._make_expression(HySymbol("do"),
                                                   HySymbol("a"),
                                                   HySymbol("b"),
                                                   HySymbol("c")))

        # We expect two statements and a final expr.
        self.assertEqual(len(ret.stmts), 2)
        stmt = ret.stmts[0]
        self.assertIsInstance(stmt, ast.Expr)
        self.assertIsInstance(stmt.value, ast.Name)
        self.assertEqual(stmt.value.id, "a")
        stmt = ret.stmts[1]
        self.assertIsInstance(stmt, ast.Expr)
        self.assertIsInstance(stmt.value, ast.Name)
        self.assertEqual(stmt.value.id, "b")
        expr = ret.expr
        self.assertIsInstance(expr, ast.Name)
        self.assertEqual(expr.id, "c")
Esempio n. 2
0
def test_preprocessor_expression():
    """Test that macro expansion doesn't recurse"""
    obj = macroexpand(
        tokenize('(test (test "one" "two"))')[0], __name__,
        HyASTCompiler(__name__))

    assert type(obj) == List
    assert type(obj[0]) == Expression

    assert obj[0] == Expression([Symbol("test"), String("one"), String("two")])

    obj = List([String("one"), String("two")])
    obj = tokenize('(shill ["one" "two"])')[0][1]
    assert obj == macroexpand(obj, __name__, HyASTCompiler(__name__))
Esempio n. 3
0
def test_macroexpand_nan():
    # https://github.com/hylang/hy/issues/1574
    import math
    NaN = float('nan')
    x = macroexpand(HyFloat(NaN), __name__, HyASTCompiler(__name__))
    assert type(x) is HyFloat
    assert math.isnan(x)
Esempio n. 4
0
def test_preprocessor_simple():
    """ Test basic macro expansion """
    obj = macroexpand(tokenize('(test "one" "two")')[0],
                      __name__,
                      HyASTCompiler(__name__))
    assert obj == List(["one", "two"])
    assert type(obj) == List
Esempio n. 5
0
    def __init__(self, *args, **kwargs):
        '''
        Create the hy environment
        '''
        self.locals = {"__name__": "__console__", "__doc__": None}
        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__
        self.compiler = HyASTCompiler(self.module)

        self.env = {}
        super(CalystoHy, self).__init__(*args, **kwargs)
        #[load_macros(m) for m in [hy.core, hy.macros]]
        if "str" in dir(__builtins__):
            self.env.update(
                {key: getattr(__builtins__, key)
                 for key in dir(__builtins__)})
        if "keys" in dir(__builtins__):
            self.env.update(__builtins__)
        self.env["raw_input"] = self.raw_input
        self.env["read"] = self.raw_input
        self.env["input"] = self.raw_input
        # Because using eval of mode="single":
        sys.displayhook = self.displayhook
        self.complete = create_completer(self.env)
Esempio n. 6
0
def test_tag_macro_error():
    """Check if we get correct error with wrong dispatch character"""
    try:
        macroexpand(
            tokenize("(dispatch_tag_macro '- '())")[0], __name__,
            HyASTCompiler(__name__))
    except HyTypeError as e:
        assert "with the character `-`" in str(e)
Esempio n. 7
0
def test_preprocessor_exceptions():
    """ Test that macro expansion raises appropriate exceptions"""
    try:
        macroexpand(tokenize('(defn)')[0], HyASTCompiler(__name__))
        assert False
    except HyMacroExpansionError as e:
        assert "_hy_anon_fn_" not in str(e)
        assert "TypeError" not in str(e)
Esempio n. 8
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
Esempio n. 9
0
    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})
Esempio n. 10
0
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
Esempio n. 11
0
def test_preprocessor_exceptions():
    """Test that macro expansion raises appropriate exceptions"""
    with pytest.raises(HyMacroExpansionError) as excinfo:
        macroexpand(tokenize("(when)")[0], __name__, HyASTCompiler(__name__))
    assert "_hy_anon_" not in excinfo.value.msg
Esempio n. 12
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__

        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)
Esempio n. 13
0
 def setUp(self):
     self.c = HyASTCompiler()