From 57b5fa49b1304ad507f71517f5934844b840ac6a Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Sat, 21 Apr 2018 13:39:49 -0700 Subject: [PATCH] Unify illegal special forms --- hy/compiler.py | 34 ++++++++++++++-------------------- tests/compilers/test_ast.py | 4 ++-- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/hy/compiler.py b/hy/compiler.py index c97ced0..d5d085b 100755 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -62,12 +62,6 @@ def load_stdlib(): _stdlib[e] = module -_compile_table = {} -_decoratables = (ast.FunctionDef, ast.ClassDef) -if PY35: - _decoratables += (ast.AsyncFunctionDef,) - - def ast_str(x, piecewise=False): if piecewise: return ".".join(ast_str(s) if s else "" for s in x.split(".")) @@ -75,6 +69,17 @@ def ast_str(x, piecewise=False): return x if PY3 else x.encode('UTF8') +_compile_table = {} +_decoratables = (ast.FunctionDef, ast.ClassDef) +if PY35: + _decoratables += (ast.AsyncFunctionDef,) +# _bad_roots are fake special operators, which are used internally +# by other special forms (e.g., `except` in `try`) but can't be +# used to construct special forms themselves. +_bad_roots = tuple(ast_str(x) for x in ( + "unquote", "unquote-splice", "unpack-mapping", "except")) + + def builds(*types, **kwargs): # A decorator that adds the decorated method to _compile_table for # compiling `types`, but only if kwargs['iff'] (if provided) is @@ -433,6 +438,9 @@ class HyASTCompiler(object): def compile_atom(self, atom_type, atom): if isinstance(atom_type, string_types): atom_type = ast_str(atom_type) + if atom_type in _bad_roots: + raise HyTypeError(atom, "The special form '{}' " + "is not allowed here".format(atom_type)) if atom_type in _compile_table: # _compile_table[atom_type] is a method for compiling this # type of atom, so call it. If it has an extra parameter, @@ -672,11 +680,6 @@ class HyASTCompiler(object): ret.add_imports("hy", imports) return ret - @builds("unquote", "unquote-splice") - def compile_unquote(self, expr): - raise HyTypeError(expr, - "`%s' can't be used at the top-level" % expr[0]) - @special("unpack-iterable", [FORM]) def compile_unpack_iterable(self, expr, root, arg): if not PY3: @@ -685,10 +688,6 @@ class HyASTCompiler(object): ret += asty.Starred(expr, value=ret.force_expr, ctx=ast.Load()) return ret - @builds("unpack-mapping") - def compile_unpack_mapping(self, expr): - raise HyTypeError(expr, "`unpack-mapping` isn't allowed here") - @special([(not PY3, "exec*")], [FORM, maybe(FORM), maybe(FORM)]) # Under Python 3, `exec` is a function rather than a statement type, so Hy # doesn't need a special form for it. @@ -804,11 +803,6 @@ class HyASTCompiler(object): expr, body=body, handlers=handlers, orelse=orelse) return handler_results + x + returnable - @builds("except") - def magic_internal_form(self, expr): - raise HyTypeError(expr, - "Error: `%s' can't be used like that." % (expr[0])) - def _compile_catch_expression(self, expr, var, exceptions, body): # exceptions catch should be either: # [[list of exceptions]] diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index 16948f5..d7e7875 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -585,11 +585,11 @@ def test_setv_builtins(): def test_top_level_unquote(): with pytest.raises(HyTypeError) as excinfo: can_compile("(unquote)") - assert excinfo.value.message == "`unquote' can't be used at the top-level" + assert excinfo.value.message == "The special form 'unquote' is not allowed here" with pytest.raises(HyTypeError) as excinfo: can_compile("(unquote-splice)") - assert excinfo.value.message == "`unquote-splice' can't be used at the top-level" + assert excinfo.value.message == "The special form 'unquote-splice' is not allowed here" def test_lots_of_comment_lines():