From cf7ff802c95355ffe16ff061d66d8a30f3d15d4d Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Wed, 30 Aug 2017 08:47:53 -0700 Subject: [PATCH] Make hy.compiler.builds more flexible I removed the "Hypster" error message and the test for it because it can only catch compiler bugs that should themselves be caught by Hy's tests. --- hy/compiler.py | 109 +++++++++---------------------- tests/compilers/test_compiler.py | 12 ---- 2 files changed, 31 insertions(+), 90 deletions(-) diff --git a/hy/compiler.py b/hy/compiler.py index 7bf2aed..db1de24 100755 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -91,28 +91,20 @@ def ast_str(foobar): return "hy_%s" % (str(foobar).replace("-", "_")) -def builds(_type): - - unpythonic_chars = ["-"] - really_ok = ["-"] - if any(x in unpythonic_chars for x in str_type(_type)): - if _type not in really_ok: - raise TypeError("Dear Hypster: `build' needs to be *post* " - "translated strings... `%s' sucks." % (_type)) +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 + # true. + if not kwargs.get('iff', True): + return lambda fn: fn def _dec(fn): - _compile_table[_type] = fn + for t in types: + _compile_table[t] = fn return fn return _dec -def builds_if(_type, condition): - if condition: - return builds(_type) - else: - return lambda fn: fn - - def spoof_positions(obj): if not isinstance(obj, HyObject) or isinstance(obj, HyCons): return @@ -762,8 +754,7 @@ class HyASTCompiler(object): return imports, HyExpression([HySymbol(name), form]).replace(form), False - @builds("quote") - @builds("quasiquote") + @builds("quote", "quasiquote") @checkargs(exact=1) def compile_quote(self, entries): if entries[0] == "quote": @@ -776,8 +767,7 @@ class HyASTCompiler(object): ret.add_imports("hy", imports) return ret - @builds("unquote") - @builds("unquote_splicing") + @builds("unquote", "unquote_splicing") def compile_unquote(self, expr): raise HyTypeError(expr, "`%s' can't be used at the top-level" % expr[0]) @@ -796,7 +786,7 @@ class HyASTCompiler(object): def compile_unpack_mapping(self, expr): raise HyTypeError(expr, "`unpack-mapping` isn't allowed here") - @builds_if("exec*", not PY3) + @builds("exec*", iff=(not PY3)) # Under Python 3, `exec` is a function rather than a statement type, so Hy # doesn't need a special form for it. @checkargs(min=1, max=3) @@ -1106,13 +1096,10 @@ class HyASTCompiler(object): return ret + asty.Assert(expr, test=e, msg=msg) @builds("global") - @builds("nonlocal") + @builds("nonlocal", iff=PY3) @checkargs(min=1) def compile_global_or_nonlocal(self, expr): form = expr.pop(0) - if form == "nonlocal" and not PY3: - raise HyCompileError( - "nonlocal only supported in python 3!") names = [] while len(expr) > 0: identifier = expr.pop(0) @@ -1126,7 +1113,7 @@ class HyASTCompiler(object): return node(expr, names=names) @builds("yield") - @builds_if("yield_from", PY3) + @builds("yield_from", iff=PY3) @checkargs(max=1) def compile_yield_expression(self, expr): ret = Result(contains_yield=(not PY3)) @@ -1377,9 +1364,7 @@ class HyASTCompiler(object): return gen_res + cond, gen - @builds("list_comp") - @builds("set_comp") - @builds("genexpr") + @builds("list_comp", "set_comp", "genexpr") @checkargs(min=2, max=3) def compile_comprehension(self, expr): # (list-comp expr (target iter) cond?) @@ -1418,8 +1403,7 @@ class HyASTCompiler(object): value=value.force_expr, generators=gen) - @builds("not") - @builds("~") + @builds("not", "~") @checkargs(1) def compile_unary_operator(self, expression): ops = {"not": ast.Not, @@ -1480,8 +1464,7 @@ class HyASTCompiler(object): raise HyTypeError(entry, "unrecognized (require) syntax") return Result() - @builds("and") - @builds("or") + @builds("and", "or") def compile_logical_or_and_and_operator(self, expression): ops = {"and": (ast.And, "True"), "or": (ast.Or, "None")} @@ -1548,26 +1531,19 @@ class HyASTCompiler(object): return ret + asty.Compare( e, left=exprs[0], ops=ops, comparators=exprs[1:]) - @builds("=") - @builds("is") - @builds("<") - @builds("<=") - @builds(">") - @builds(">=") + @builds("=", "is", "<", "<=", ">", ">=") @checkargs(min=1) def compile_compare_op_expression(self, expression): if len(expression) == 2: return asty.Name(expression, id="True", ctx=ast.Load()) return self._compile_compare_op_expression(expression) - @builds("!=") - @builds("is_not") + @builds("!=", "is_not") @checkargs(min=2) def compile_compare_op_expression_coll(self, expression): return self._compile_compare_op_expression(expression) - @builds("in") - @builds("not_in") + @builds("in", "not_in") @checkargs(2) def compile_compare_op_expression_binary(self, expression): return self._compile_compare_op_expression(expression) @@ -1602,23 +1578,17 @@ class HyASTCompiler(object): return ret - @builds("**") - @builds("//") - @builds("<<") - @builds(">>") - @builds("&") + @builds("**", "//", "<<", ">>", "&") @checkargs(min=2) def compile_maths_expression_2_or_more(self, expression): return self._compile_maths_expression(expression) - @builds("%") - @builds("^") + @builds("%", "^") @checkargs(2) def compile_maths_expression_exactly_2(self, expression): return self._compile_maths_expression(expression) - @builds("*") - @builds("|") + @builds("*", "|") def compile_maths_expression_mul(self, expression): id_elem = {"*": 1, "|": 0}[expression[0]] if len(expression) == 1: @@ -1647,7 +1617,7 @@ class HyASTCompiler(object): expression, op=op, operand=ret.force_expr) @builds("&") - @builds_if("@", PY35) + @builds("@", iff=PY35) @checkargs(min=1) def compile_maths_expression_unary_idempotent(self, expression): if len(expression) == 2: @@ -1670,19 +1640,9 @@ class HyASTCompiler(object): def compile_maths_expression_sub(self, expression): return self._compile_maths_expression_additive(expression) - @builds("+=") - @builds("/=") - @builds("//=") - @builds("*=") - @builds("_=") - @builds("%=") - @builds("**=") - @builds("<<=") - @builds(">>=") - @builds("|=") - @builds("^=") - @builds("&=") - @builds_if("@=", PY35) + @builds("+=", "/=", "//=", "*=", "_=", "%=", "**=", "<<=", ">>=", "|=", + "^=", "&=") + @builds("@=", iff=PY35) @checkargs(2) def compile_augassign_expression(self, expression): ops = {"+=": ast.Add, @@ -1793,8 +1753,7 @@ class HyASTCompiler(object): expression, func=func.expr, args=args, keywords=keywords, starargs=oldpy_star, kwargs=oldpy_kw) - @builds("def") - @builds("setv") + @builds("def", "setv") def compile_def_expression(self, expression): root = expression.pop(0) if not expression: @@ -1908,8 +1867,7 @@ class HyASTCompiler(object): return ret - @builds("fn") - @builds("fn*") + @builds("fn", "fn*") # The starred version is for internal use (particularly, in the # definition of `defn`). It ensures that a FunctionDef is # produced rather than a Lambda. @@ -2174,9 +2132,7 @@ class HyASTCompiler(object): def compile_cons(self, cons): raise HyTypeError(cons, "Can't compile a top-level cons cell") - @builds(HyInteger) - @builds(HyFloat) - @builds(HyComplex) + @builds(HyInteger, HyFloat, HyComplex) def compile_numeric_literal(self, number, building): f = {HyInteger: long_type, HyFloat: float, @@ -2214,16 +2170,13 @@ class HyASTCompiler(object): return asty.Name(symbol, id=ast_str(symbol), ctx=ast.Load()) - @builds(HyString) - @builds(HyKeyword) - @builds(HyBytes) + @builds(HyString, HyKeyword, HyBytes) def compile_string(self, string, building): node = asty.Bytes if PY3 and building is HyBytes else asty.Str f = bytes_type if building is HyBytes else str_type return node(string, s=f(string)) - @builds(HyList) - @builds(HySet) + @builds(HyList, HySet) def compile_list(self, expression, building): elts, ret, _ = self._compile_collect(expression) node = {HyList: asty.List, HySet: asty.Set}[building] diff --git a/tests/compilers/test_compiler.py b/tests/compilers/test_compiler.py index 90a1d2a..b27d596 100644 --- a/tests/compilers/test_compiler.py +++ b/tests/compilers/test_compiler.py @@ -9,18 +9,6 @@ from hy.models import HyExpression, HyList, HySymbol, HyInteger from hy._compat import PY3 -def test_builds_with_dash(): - assert callable(compiler.builds("foobar")) - assert callable(compiler.builds("foo_bar")) - assert callable(compiler.builds("-")) - try: - compiler.builds("foobar-with-dash-") - except TypeError as e: - assert "*post* translated strings" in str(e) - else: - assert False - - def make_expression(*args): h = HyExpression(args) h.start_line = 1