diff --git a/NEWS b/NEWS index 01d139b..d5d7dc5 100644 --- a/NEWS +++ b/NEWS @@ -7,11 +7,18 @@ Changes from 0.12.1 * Added bytestring literals, which create `bytes` objects under Python 3 and `str` objects under Python 2 * Commas and underscores are allowed in numeric literals + * Many more operators (e.g., `**`, `//`, `not`, `in`) can be used + as first-class functions + * The semantics of binary operators when applied to fewer or more + than two arguments have been made more logical + * `(** a b c d)` is now equivalent to `(** a (** b (** c d)))`, + not `(** (** (** a b) c) d)` * `setv` always returns None * xor: If exactly one argument is true, return it * hy.core.reserved is now hy.extra.reserved [ Bug Fixes ] + * All shadowed operators have the same arities as real operators * Shadowed comparison operators now use `and` instead of `&` for chained comparisons * partition no longer prematurely exhausts input iterators diff --git a/hy/compiler.py b/hy/compiler.py index 1bf3bdf..2db237d 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -371,7 +371,7 @@ def checkargs(exact=None, min=None, max=None, even=None, multiple=None): class HyASTCompiler(object): def __init__(self, module_name): - self.allow_builtins = False + self.allow_builtins = module_name.startswith("hy.core") self.anon_fn_count = 0 self.anon_var_count = 0 self.imports = defaultdict(set) @@ -1876,7 +1876,7 @@ class HyASTCompiler(object): col_offset=e.start_column) @builds("=") - @builds("!=") + @builds("is") @builds("<") @builds("<=") @builds(">") @@ -1884,33 +1884,25 @@ class HyASTCompiler(object): @checkargs(min=1) def compile_compare_op_expression(self, expression): if len(expression) == 2: - rval = "True" - if expression[0] == "!=": - rval = "False" - return ast.Name(id=rval, + return ast.Name(id="True", ctx=ast.Load(), lineno=expression.start_line, col_offset=expression.start_column) return self._compile_compare_op_expression(expression) - @builds("is") - @builds("in") + @builds("!=") @builds("is_not") - @builds("not_in") @checkargs(min=2) def compile_compare_op_expression_coll(self, expression): return self._compile_compare_op_expression(expression) - @builds("%") - @builds("**") - @builds("<<") - @builds(">>") - @builds("|") - @builds("^") - @builds("&") - @builds_if("@", PY35) - @checkargs(min=2) - def compile_maths_expression(self, expression): + @builds("in") + @builds("not_in") + @checkargs(2) + def compile_compare_op_expression_binary(self, expression): + return self._compile_compare_op_expression(expression) + + def _compile_maths_expression(self, expression): ops = {"+": ast.Add, "/": ast.Div, "//": ast.FloorDiv, @@ -1926,14 +1918,18 @@ class HyASTCompiler(object): if PY35: ops.update({"@": ast.MatMult}) - inv = expression.pop(0) - op = ops[inv] + op = ops[expression.pop(0)] + right_associative = op == ast.Pow + if right_associative: + expression = expression[::-1] ret = self.compile(expression.pop(0)) for child in expression: left_expr = ret.force_expr ret += self.compile(child) right_expr = ret.force_expr + if right_associative: + left_expr, right_expr = right_expr, left_expr ret += ast.BinOp(left=left_expr, op=op(), right=right_expr, @@ -1941,27 +1937,46 @@ class HyASTCompiler(object): col_offset=child.start_column) return ret - @builds("*") - @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("^") + @checkargs(2) + def compile_maths_expression_exactly_2(self, expression): + return self._compile_maths_expression(expression) + + @builds("*") + @builds("|") def compile_maths_expression_mul(self, expression): - if len(expression) > 2: - return self.compile_maths_expression(expression) + id_elem = {"*": 1, "|": 0}[expression[0]] + if len(expression) == 1: + return ast.Num(n=long_type(id_elem), + lineno=expression.start_line, + col_offset=expression.start_column) + elif len(expression) == 2: + return self.compile(expression[1]) else: - id_op = {"*": HyInteger(1), "/": HyInteger(1), "//": HyInteger(1)} + return self._compile_maths_expression(expression) - op = expression.pop(0) - arg = expression.pop(0) if expression else id_op[op] - expr = HyExpression([ - HySymbol(op), - id_op[op], - arg - ]).replace(expression) - return self.compile_maths_expression(expr) + @builds("/") + @checkargs(min=1) + def compile_maths_expression_div(self, expression): + if len(expression) == 2: + expression = HyExpression([HySymbol("/"), + HyInteger(1), + expression[1]]).replace(expression) + return self._compile_maths_expression(expression) - def compile_maths_expression_additive(self, expression): + def _compile_maths_expression_additive(self, expression): if len(expression) > 2: - return self.compile_maths_expression(expression) + return self._compile_maths_expression(expression) else: op = {"+": ast.UAdd, "-": ast.USub}[expression.pop(0)]() arg = expression.pop(0) @@ -1972,6 +1987,17 @@ class HyASTCompiler(object): col_offset=arg.start_column) return ret + @builds("&") + @builds_if("@", PY35) + @checkargs(min=1) + def compile_maths_expression_unary_idempotent(self, expression): + if len(expression) == 2: + # Used as a unary operator, this operator simply + # returns its argument. + return self.compile(expression[1]) + else: + return self._compile_maths_expression(expression) + @builds("+") def compile_maths_expression_add(self, expression): if len(expression) == 1: @@ -1980,12 +2006,12 @@ class HyASTCompiler(object): lineno=expression.start_line, col_offset=expression.start_column) else: - return self.compile_maths_expression_additive(expression) + return self._compile_maths_expression_additive(expression) @builds("-") @checkargs(min=1) def compile_maths_expression_sub(self, expression): - return self.compile_maths_expression_additive(expression) + return self._compile_maths_expression_additive(expression) @builds("+=") @builds("/=") diff --git a/hy/core/shadow.hy b/hy/core/shadow.hy index f5f9b4c..f7d3466 100644 --- a/hy/core/shadow.hy +++ b/hy/core/shadow.hy @@ -22,72 +22,144 @@ ;;;; Hy shadow functions (import operator) +(import [hy._compat [PY35]]) (defn + [&rest args] "Shadow + operator for when we need to import / map it against something" (if + (= (len args) 0) + 0 (= (len args) 1) - (operator.pos (get args 0)) - args - (reduce operator.add args) - (raise (TypeError "Need at least 1 argument to add/concatenate")))) + (+ (first args)) + ; else + (reduce operator.add args))) - -(defn - [&rest args] +(defn - [a1 &rest a-rest] "Shadow - operator for when we need to import / map it against something" - (if - (= (len args) 1) - (- (get args 0)) - args - (reduce operator.sub args) - (raise (TypeError "Need at least 1 argument to subtract")))) - + (if a-rest + (reduce operator.sub a-rest a1) + (- a1))) (defn * [&rest args] "Shadow * operator for when we need to import / map it against something" - (if (= (len args) 0) - 1 ; identity - (reduce operator.mul args))) - - -(defn / [&rest args] - "Shadow / operator for when we need to import / map it against something" (if + (= (len args) 0) + 1 (= (len args) 1) - (operator.truediv 1 (get args 0)) - args - (reduce operator.truediv args) - (raise (TypeError "Need at least 1 argument to divide")))) + (first args) + ; else + (reduce operator.mul args))) +(defn ** [a1 a2 &rest a-rest] + ; We use `-foldr` instead of `reduce` because exponentiation + ; is right-associative. + (-foldr operator.pow (+ (, a1 a2) a-rest))) +(defn -foldr [f xs] + (reduce (fn [x y] (f y x)) (cut xs None None -1))) -(defn comp-op [op args] +(defn / [a1 &rest a-rest] + "Shadow / operator for when we need to import / map it against something" + (if a-rest + (reduce operator.truediv a-rest a1) + (/ 1 a1))) + +(defn // [a1 a2 &rest a-rest] + (reduce operator.floordiv (+ (, a2) a-rest) a1)) + +(defn % [x y] + (% x y)) + +(if PY35 (defn @ [a1 &rest a-rest] + (reduce operator.matmul a-rest a1))) + +(defn << [a1 a2 &rest a-rest] + (reduce operator.lshift (+ (, a2) a-rest) a1)) + +(defn >> [a1 a2 &rest a-rest] + (reduce operator.rshift (+ (, a2) a-rest) a1)) + +(defn & [a1 &rest a-rest] + (if a-rest + (reduce operator.and_ a-rest a1) + a1)) + +(defn | [&rest args] + (if + (= (len args) 0) + 0 + (= (len args) 1) + (first args) + ; else + (reduce operator.or_ args))) + +(defn ^ [x y] + (^ x y)) + +(defn ~ [x] + (~ x)) + +(defn comp-op [op a1 a-rest] "Helper for shadow comparison operators" - (if (< (len args) 2) - (raise (TypeError "Need at least 2 arguments to compare")) + (if a-rest (reduce (fn [x y] (and x y)) - (list-comp (op x y) - [(, x y) (zip args (cut args 1))])))) -(defn < [&rest args] + (list-comp (op x y) [(, x y) (zip (+ (, a1) a-rest) a-rest)])) + True)) +(defn < [a1 &rest a-rest] "Shadow < operator for when we need to import / map it against something" - (comp-op operator.lt args)) -(defn <= [&rest args] + (comp-op operator.lt a1 a-rest)) +(defn <= [a1 &rest a-rest] "Shadow <= operator for when we need to import / map it against something" - (comp-op operator.le args)) -(defn = [&rest args] + (comp-op operator.le a1 a-rest)) +(defn = [a1 &rest a-rest] "Shadow = operator for when we need to import / map it against something" - (comp-op operator.eq args)) -(defn != [&rest args] + (comp-op operator.eq a1 a-rest)) +(defn is [a1 &rest a-rest] + (comp-op operator.is_ a1 a-rest)) +(defn != [a1 a2 &rest a-rest] "Shadow != operator for when we need to import / map it against something" - (comp-op operator.ne args)) -(defn >= [&rest args] + (comp-op operator.ne a1 (+ (, a2) a-rest))) +(defn is-not [a1 a2 &rest a-rest] + (comp-op operator.is-not a1 (+ (, a2) a-rest))) +(defn >= [a1 &rest a-rest] "Shadow >= operator for when we need to import / map it against something" - (comp-op operator.ge args)) -(defn > [&rest args] + (comp-op operator.ge a1 a-rest)) +(defn > [a1 &rest a-rest] "Shadow > operator for when we need to import / map it against something" - (comp-op operator.gt args)) + (comp-op operator.gt a1 a-rest)) -; TODO figure out a way to shadow "is", "is_not", "and", "or" +(defn and [&rest args] + (if + (= (len args) 0) + True + (= (len args) 1) + (first args) + ; else + (reduce (fn [x y] (and x y)) args))) +(defn or [&rest args] + (if + (= (len args) 0) + None + (= (len args) 1) + (first args) + ; else + (reduce (fn [x y] (or x y)) args))) -(setv *exports* ['+ '- '* '/ '< '<= '= '!= '>= '>]) +(defn not [x] + (not x)) + +(defn in [x y] + (in x y)) + +(defn not-in [x y] + (not-in x y)) + +(setv *exports* [ + '+ '- '* '** '/ '// '% '@ + '<< '>> '& '| '^ '~ + '< '> '<= '>= '= '!= + 'and 'or 'not + 'is 'is-not 'in 'not-in]) +(if (not PY35) + (.remove *exports* '@)) diff --git a/tests/__init__.py b/tests/__init__.py index 74b8390..15a102e 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -12,7 +12,7 @@ from .native_tests.when import * # noqa from .native_tests.with_decorator import * # noqa from .native_tests.core import * # noqa from .native_tests.reader_macros import * # noqa -from .native_tests.shadow import * # noqa +from .native_tests.operators import * # noqa from .native_tests.with_test import * # noqa from .native_tests.extra.anaphoric import * # noqa from .native_tests.contrib.loop import * # noqa diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index dfcede1..906b252 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -498,7 +498,7 @@ def test_compile_error(): try: can_compile("(fn [] (in [1 2 3]))") except HyTypeError as e: - assert(e.message == "`in' needs at least 2 arguments, got 1.") + assert(e.message == "`in' needs 2 arguments, got 1") else: assert(False) diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index 013ef1e..7b11edb 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -253,57 +253,6 @@ (assert (= fact 120))) -(defn test-not [] - "NATIVE: test not" - (assert (not (= 1 2))) - (assert (= True (not False))) - (assert (= False (not 42))) ) - - -(defn test-inv [] - "NATIVE: test inv" - (assert (= (~ 1) -2)) - (assert (= (~ -2) 1))) - - -(defn test-in [] - "NATIVE: test in" - (assert (in "a" ["a" "b" "c" "d"])) - (assert (not-in "f" ["a" "b" "c" "d"]))) - - -(defn test-noteq [] - "NATIVE: not eq" - (assert (!= 2 3)) - (assert (not (!= 1)))) - - -(defn test-eq [] - "NATIVE: eq" - (assert (= 1 1)) - (assert (= 1))) - - -(defn test-numops [] - "NATIVE: test numpos" - (assert (> 5 4 3 2 1)) - (assert (> 1)) - (assert (< 1 2 3 4 5)) - (assert (< 1)) - (assert (<= 5 5 5 5 )) - (assert (<= 1)) - (assert (>= 5 5 5 5 )) - (assert (>= 1))) - - -(defn test-is [] - "NATIVE: test is can deal with None" - (setv a None) - (assert (is a None)) - (assert (is-not a "b")) - (assert (none? a))) - - (defn test-branching [] "NATIVE: test if branching" (if True diff --git a/tests/native_tests/operators.hy b/tests/native_tests/operators.hy new file mode 100644 index 0000000..1f60650 --- /dev/null +++ b/tests/native_tests/operators.hy @@ -0,0 +1,286 @@ +(import [hy._compat [PY35]]) + +(defmacro op-and-shadow-test [op &rest body] + ; Creates two tests with the given `body`, one where all occurrences + ; of the symbol `f` are syntactically replaced with `op` (a test of + ; the real operator), and one where the body is preceded by an + ; assignment of `op` to `f` (a test of the shadow operator). + ; + ; `op` can also be a list of operators, in which case two tests are + ; created for each operator. + (import [hy [HySymbol HyString]] [hy.contrib.walk [prewalk]]) + (setv defns []) + (for [o (if (coll? op) op [op])] + (.append defns `(defn ~(HySymbol (+ "test_operator_" o "_real")) [] + (setv f-name ~(HyString o)) + ~@(prewalk :form body :f (fn [x] + (if (and (symbol? x) (= x "f")) o x))))) + (.append defns `(defn ~(HySymbol (+ "test_operator_" o "_shadow")) [] + (setv f-name ~(HyString o)) + (setv f ~o) + ~@body))) + `(do ~@defns)) + +(defmacro forbid [expr] + `(assert (try + (eval '~expr) + (except [TypeError] True) + (else (raise AssertionError))))) + + +(op-and-shadow-test + + + (assert (= (f) 0)) + + (defclass C [object] [__pos__ (fn [self] "called __pos__")]) + (assert (= (f (C)) "called __pos__")) + + (assert (= (f 1 2) 3)) + (assert (= (f 1 2 3 4) 10)) + (assert (= (f 1 2 3 4 5) 15)) + + ; with strings + (assert (= (f "a" "b" "c") + "abc")) + ; with lists + (assert (= (f ["a"] ["b"] ["c"]) + ["a" "b" "c"]))) + + +(op-and-shadow-test - + (forbid (f)) + (assert (= (f 1) -1)) + (assert (= (f 2 1) 1)) + (assert (= (f 2 1 1) 0))) + + +(op-and-shadow-test * + (assert (= (f) 1)) + (assert (= (f 3) 3)) + (assert (= (f 3 3) 9)) + (assert (= (f 2 3 4) 24)) + (assert (= (f "ke" 4) "kekekeke")) + (assert (= (f [1 2 3] 2) [1 2 3 1 2 3]))) + + +(op-and-shadow-test ** + (forbid (f)) + (forbid (f 1)) + (assert (= (f 3 2) 9)) + (assert (= (f 5 4 3 2) (** 5 (** 4 (** 3 2)))))) + ; Exponentiation is right-associative. + + +(op-and-shadow-test / + (forbid (f)) + (assert (= (f 2) .5)) + (assert (= (f 3 2) 1.5)) + (assert (= (f 8 2) 4)) + (assert (= (f 8 2 2) 2)) + (assert (= (f 8 2 2 2) 1))) + + +(op-and-shadow-test // + (forbid (f)) + (forbid (f 1)) + (assert (= (f 16 5) 3)) + (assert (= (f 8 2) 4)) + (assert (= (f 8 2 2) 2))) + + +(op-and-shadow-test % + (forbid (f)) + (forbid (f 1)) + (assert (= (f 16 5) 1)) + (assert (= (f 8 2) 0)) + (assert (= (f "aa %s bb" 15) "aa 15 bb")) + (assert (= (f "aa %s bb %s cc" (, "X" "Y")) "aa X bb Y cc")) + (forbid (f 1 2 3))) + + +(when PY35 (op-and-shadow-test @ + (defclass C [object] [ + __init__ (fn [self content] (setv self.content content)) + __matmul__ (fn [self other] (C (+ self.content other.content)))]) + (forbid (f)) + (assert (do (setv c (C "a")) (is (f c) c))) + (assert (= (. (f (C "b") (C "c")) content) "bc")) + (assert (= (. (f (C "d") (C "e") (C "f")) content) "def")))) + + +(op-and-shadow-test << + (forbid (f)) + (forbid (f 1)) + (assert (= (f 0b101 2) 0b10100)) + (assert (= (f 0b101 2 3) 0b10100000))) + + +(op-and-shadow-test >> + (forbid (f)) + (forbid (f 1)) + (assert (= (f 0b101 2) 0b1)) + (assert (= (f 0b101000010 2 3) 0b1010))) + + +(op-and-shadow-test & + (forbid (f)) + ; Binary AND has no identity element for the set of all + ; nonnegative integers, because we can't have a 1 in every bit + ; when there are infinitely many bits. + (assert (= (f 17) 17)) + (assert (= (f 0b0011 0b0101) 0b0001)) + (assert (= (f 0b111 0b110 0b100) 0b100))) + + +(op-and-shadow-test | + (assert (= (f) 0)) + (assert (= (f 17) 17)) + (assert (= (f 0b0011 0b0101) 0b0111)) + (assert (= (f 0b11100 0b11000 0b10010) 0b11110))) + + +(op-and-shadow-test ^ + (forbid (f)) + (forbid (f 17)) + (assert (= (f 0b0011 0b0101) 0b0110)) + (forbid (f 0b111 0b110 0b100))) + ; `xor` with 3 arguments is kill (https://github.com/hylang/hy/pull/1102), + ; so we don't allow `^` with 3 arguments, either. + + +(op-and-shadow-test ~ + (forbid (f)) + (assert (= (f (chr 0b00101111) + (chr 0b11010000)))) + (forbid (f (chr 0b00101111) (chr 0b11010000)))) + + +(op-and-shadow-test < + + (forbid (f)) + (assert (is (f "hello") True)) + (assert (is (f 1 2) True)) + (assert (is (f 2 1) False)) + (assert (is (f 1 1) False)) + (assert (is (f 1 2 3) True)) + (assert (is (f 3 2 1) False)) + (assert (is (f 1 3 2) False)) + (assert (is (f 1 2 2) False)) + + ; Make sure chained comparisons use `and`, not `&`. + ; https://github.com/hylang/hy/issues/1191 + (defclass C [object] [ + __init__ (fn [self x] + (setv self.x x)) + __lt__ (fn [self other] + self.x)]) + (assert (= (f (C "a") (C "b") (C "c")) "b"))) + + +(op-and-shadow-test > + (forbid (f)) + (assert (is (f "hello") True)) + (assert (is (f 1 2) False)) + (assert (is (f 2 1) True)) + (assert (is (f 1 1) False)) + (assert (is (f 1 2 3) False)) + (assert (is (f 3 2 1) True)) + (assert (is (f 1 3 2) False)) + (assert (is (f 2 1 1) False))) + + +(op-and-shadow-test <= + (forbid (f)) + (assert (is (f "hello") True)) + (assert (is (f 1 2) True)) + (assert (is (f 2 1) False)) + (assert (is (f 1 1) True)) + (assert (is (f 1 2 3) True)) + (assert (is (f 3 2 1) False)) + (assert (is (f 1 3 2) False)) + (assert (is (f 1 2 2) True))) + + +(op-and-shadow-test >= + (forbid (f)) + (assert (is (f "hello") True)) + (assert (is (f 1 2) False)) + (assert (is (f 2 1) True)) + (assert (is (f 1 1) True)) + (assert (is (f 1 2 3) False)) + (assert (is (f 3 2 1) True)) + (assert (is (f 1 3 2) False)) + (assert (is (f 2 1 1) True))) + + +(op-and-shadow-test [= is] + (forbid (f)) + (assert (is (f "hello") True)) + (defclass C) + (setv x (get {"is" (C) "=" 0} f-name)) + (setv y (get {"is" (C) "=" 1} f-name)) + (assert (is (f x x) True)) + (assert (is (f y y) True)) + (assert (is (f x y) False)) + (assert (is (f y x) False)) + (assert (is (f x x x x x) True)) + (assert (is (f x x x y x) False)) + (setv n None) + (assert (is (f n None) True)) + (assert (is (f n "b") False))) + + +(op-and-shadow-test [!= is-not] + (forbid (f)) + (forbid (f "hello")) + (defclass C) + (setv x (get {"is_not" (C) "!=" 0} f-name)) + (setv y (get {"is_not" (C) "!=" 1} f-name)) + (setv z (get {"is_not" (C) "!=" 2} f-name)) + (assert (is (f x x) False)) + (assert (is (f y y) False)) + (assert (is (f x y) True)) + (assert (is (f y x) True)) + (assert (is (f x y z) True)) + (assert (is (f x x x) False)) + (assert (is (f x y x) True)) + (assert (is (f x x y) False))) + + +(op-and-shadow-test and + (assert (is (f) True)) + (assert (= (f 17) 17)) + (assert (= (f 1 2) 2)) + (assert (= (f 1 0) 0)) + (assert (= (f 0 2) 0)) + (assert (= (f 0 0) 0)) + (assert (= (f 1 2 3) 3)) + (assert (= (f 1 0 3) 0)) + (assert (= (f "a" 1 True [1]) [1]))) + + +(op-and-shadow-test or + (assert (is (f) None)) + (assert (= (f 17) 17)) + (assert (= (f 1 2) 1)) + (assert (= (f 1 0) 1)) + (assert (= (f 0 2) 2)) + (assert (= (f 0 0) 0)) + (assert (= (f 1 2 3) 1)) + (assert (= (f 0 0 3) 3)) + (assert (= (f "" None 0 False []) []))) + + +(op-and-shadow-test not + (forbid (f)) + (assert (is (f "hello") False)) + (assert (is (f 0) True)) + (assert (is (f None) True))) + + +(op-and-shadow-test [in not-in] + (forbid (f)) + (forbid (f 3)) + (assert (is (f 3 [1 2]) (!= f-name "in"))) + (assert (is (f 2 [1 2]) (= f-name "in"))) + (forbid (f 2 [1 2] [3 4]))) diff --git a/tests/native_tests/shadow.hy b/tests/native_tests/shadow.hy deleted file mode 100644 index 72ac0b9..0000000 --- a/tests/native_tests/shadow.hy +++ /dev/null @@ -1,101 +0,0 @@ -(defn test-shadow-addition [] - "NATIVE: test shadow addition" - (setv x +) - (assert (try - (x) - (except [TypeError] True) - (else (raise AssertionError)))) - (assert (= (x 1 2 3 4) 10)) - (assert (= (x 1 2 3 4 5) 15)) - ; with strings - (assert (= (x "a" "b" "c") - "abc")) - ; with lists - (assert (= (x ["a"] ["b"] ["c"]) - ["a" "b" "c"]))) - - -(defn test-shadow-subtraction [] - "NATIVE: test shadow subtraction" - (setv x -) - (assert (try - (x) - (except [TypeError] True) - (else (raise AssertionError)))) - (assert (= (x 1) -1)) - (assert (= (x 2 1) 1)) - (assert (= (x 2 1 1) 0))) - - -(defn test-shadow-multiplication [] - "NATIVE: test shadow multiplication" - (setv x *) - (assert (= (x) 1)) - (assert (= (x 3) 3)) - (assert (= (x 3 3) 9))) - - -(defn test-shadow-division [] - "NATIVE: test shadow division" - (setv x /) - (assert (try - (x) - (except [TypeError] True) - (else (raise AssertionError)))) - (assert (= (x 1) 1)) - (assert (= (x 8 2) 4)) - (assert (= (x 8 2 2) 2)) - (assert (= (x 8 2 2 2) 1))) - - -(defn test-shadow-compare [] - "NATIVE: test shadow compare" - - (for [x [< <= = != >= >]] - (assert (try - (x) - (except [TypeError] True) - (else (raise AssertionError)))) - (assert (try - (x 1) - (except [TypeError] True) - (else (raise AssertionError))))) - - (for [(, x y) [[< >=] - [<= >] - [= !=]]] - (for [args [[1 2] - [2 1] - [1 1] - [2 2]]] - (assert (= (apply x args) (not (apply y args)))))) - - (setv s-lt < - s-gt > - s-le <= - s-ge >= - s-eq = - s-ne !=) - (assert (apply s-lt [1 2 3])) - (assert (not (apply s-lt [3 2 1]))) - (assert (apply s-gt [3 2 1])) - (assert (not (apply s-gt [1 2 3]))) - (assert (apply s-le [1 1 2 2 3 3])) - (assert (not (apply s-le [1 1 2 2 1 1]))) - (assert (apply s-ge [3 3 2 2 1 1])) - (assert (not (apply s-ge [3 3 2 2 3 3]))) - (assert (apply s-eq [1 1 1 1 1])) - (assert (not (apply s-eq [1 1 2 1 1]))) - (assert (apply s-ne [1 2 3 4 5])) - (assert (not (apply s-ne [1 1 2 3 4]))) - - ; Make sure chained comparisons use `and`, not `&`. - ; https://github.com/hylang/hy/issues/1191 - (defclass C [object] [ - __init__ (fn [self x] - (setv self.x x)) - __lt__ (fn [self other] - self.x)]) - (assert (= (< (C "a") (C "b") (C "c")) "b")) - (setv f <) - (assert (= (f (C "a") (C "b") (C "c")) "b")))