diff --git a/hy/compiler.py b/hy/compiler.py index 043d9a0..cd00530 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -1676,17 +1676,78 @@ class HyASTCompiler(object): @builds("and") @builds("or") - @checkargs(min=2) def compile_logical_or_and_and_operator(self, expression): - ops = {"and": ast.And, - "or": ast.Or} + ops = {"and": (ast.And, "True"), + "or": (ast.Or, "None")} operator = expression.pop(0) - values, ret, _ = self._compile_collect(expression) + opnode, default = ops[operator] + root_line, root_column = operator.start_line, operator.start_column + if len(expression) == 0: + return ast.Name(id=default, + ctx=ast.Load(), + lineno=root_line, + col_offset=root_column) + elif len(expression) == 1: + return self.compile(expression[0]) + ret = Result() + values = list(map(self.compile, expression)) + has_stmt = any(value.stmts for value in values) + if has_stmt: + # Compile it to an if...else sequence + var = self.get_anon_var() + name = ast.Name(id=var, + ctx=ast.Store(), + lineno=root_line, + col_offset=root_column) + expr_name = ast.Name(id=var, + ctx=ast.Load(), + lineno=root_line, + col_offset=root_column) - ret += ast.BoolOp(op=ops[operator](), - lineno=operator.start_line, - col_offset=operator.start_column, - values=values) + def make_assign(value, node=None): + if node is None: + line, column = root_line, root_column + else: + line, column = node.lineno, node.col_offset + return ast.Assign(targets=[ast.Name(id=var, + ctx=ast.Store(), + lineno=line, + col_offset=column)], + value=value, + lineno=line, + col_offset=column) + root = [] + current = root + for i, value in enumerate(values): + if value.stmts: + node = value.stmts[0] + current.extend(value.stmts) + else: + node = value.expr + current.append(make_assign(value.force_expr, value.force_expr)) + if i == len(values)-1: + # Skip a redundant 'if'. + break + if operator == "and": + cond = expr_name + elif operator == "or": + cond = ast.UnaryOp(op=ast.Not(), + operand=expr_name, + lineno=node.lineno, + col_offset=node.col_offset) + current.append(ast.If(test=cond, + body=[], + lineno=node.lineno, + col_offset=node.col_offset, + orelse=[])) + current = current[-1].body + ret = sum(root, ret) + ret += Result(expr=expr_name, temp_variables=[expr_name, name]) + else: + ret += ast.BoolOp(op=opnode(), + lineno=root_line, + col_offset=root_column, + values=[value.force_expr for value in values]) return ret @builds("=") diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index 08804c0..6f9f6c3 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -782,19 +782,35 @@ (defn test-and [] "NATIVE: test the and function" (let [[and123 (and 1 2 3)] - [and-false (and 1 False 3)]] + [and-false (and 1 False 3)] + [and-true (and)] + [and-single (and 1)]] (assert (= and123 3)) - (assert (= and-false False)))) + (assert (= and-false False)) + (assert (= and-true True)) + (assert (= and-single 1))) + ; short circuiting + (setv a 1) + (and 0 (setv a 2)) + (assert (= a 1))) (defn test-or [] "NATIVE: test the or function" (let [[or-all-true (or 1 2 3 True "string")] [or-some-true (or False "hello")] - [or-none-true (or False False)]] + [or-none-true (or False False)] + [or-false (or)] + [or-single (or 1)]] (assert (= or-all-true 1)) (assert (= or-some-true "hello")) - (assert (= or-none-true False)))) + (assert (= or-none-true False)) + (assert (= or-false nil)) + (assert (= or-single 1))) + ; short circuiting + (setv a 1) + (or 1 (setv a 2)) + (assert (= a 1))) (defn test-if-return-branching []