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")
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__))
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)
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
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)
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)
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)
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 __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 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 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
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 setUp(self): self.c = HyASTCompiler()