Implement chained comparisons
This commit is contained in:
parent
2074e4bd2f
commit
170febb2e8
1
NEWS.rst
1
NEWS.rst
@ -17,6 +17,7 @@ New Features
|
||||
------------------------------
|
||||
* Added special forms ``py`` to ``pys`` that allow Hy programs to include
|
||||
inline Python code.
|
||||
* Added a special form ``cmp`` for chained comparisons.
|
||||
* All augmented assignment operators (except `%=` and `^=`) now allow
|
||||
more than two arguments.
|
||||
* PEP 3107 and PEP 526 function and variable annotations are now supported.
|
||||
|
@ -299,6 +299,38 @@ as the user enters *k*.
|
||||
(print "Try again")))
|
||||
|
||||
|
||||
cmp
|
||||
---
|
||||
|
||||
``cmp`` creates a :ref:`comparison expression <py:comparisons>`. It isn't
|
||||
required for unchained comparisons, which have only one comparison operator,
|
||||
nor for chains of the same operator. For those cases, you can use the
|
||||
comparison operators directly with Hy's usual prefix syntax, as in ``(= x 1)``
|
||||
or ``(< 1 2 3)``. The use of ``cmp`` is to construct chains of heterogeneous
|
||||
operators, such as ``x <= y < z``. It uses an infix syntax with the general
|
||||
form
|
||||
|
||||
::
|
||||
|
||||
(cmp ARG OP ARG OP ARG…)
|
||||
|
||||
Hence, ``(cmp x <= y < z)`` is equivalent to ``(and (<= x y) (< y z))``,
|
||||
including short-circuiting, except that ``y`` is only evaluated once.
|
||||
|
||||
Each ``ARG`` is an arbitrary form, which does not itself use infix syntax. Use
|
||||
:ref:`py-specialform` if you want fully Python-style operator syntax. You can
|
||||
also nest ``cmp`` forms, although this is rarely useful. Each ``OP`` is a
|
||||
literal comparison operator; other forms that resolve to a comparison operator
|
||||
are not allowed.
|
||||
|
||||
At least two ``ARG``\ s and one ``OP`` are required, and every ``OP`` must be
|
||||
followed by an ``ARG``.
|
||||
|
||||
As elsewhere in Hy, the equality operator is spelled ``=``, not ``==`` as in
|
||||
Python.
|
||||
|
||||
|
||||
|
||||
comment
|
||||
-------
|
||||
|
||||
|
@ -1256,12 +1256,18 @@ class HyASTCompiler(object):
|
||||
values=[value.force_expr for value in values])
|
||||
return ret
|
||||
|
||||
c_ops = {"=": ast.Eq, "!=": ast.NotEq,
|
||||
_c_ops = {"=": ast.Eq, "!=": ast.NotEq,
|
||||
"<": ast.Lt, "<=": ast.LtE,
|
||||
">": ast.Gt, ">=": ast.GtE,
|
||||
"is": ast.Is, "is-not": ast.IsNot,
|
||||
"in": ast.In, "not-in": ast.NotIn}
|
||||
c_ops = {ast_str(k): v for k, v in c_ops.items()}
|
||||
_c_ops = {ast_str(k): v for k, v in _c_ops.items()}
|
||||
def _get_c_op(self, sym):
|
||||
k = ast_str(sym)
|
||||
if k not in self._c_ops:
|
||||
raise self._syntax_error(sym,
|
||||
"Illegal comparison operator: " + str(sym))
|
||||
return self._c_ops[k]()
|
||||
|
||||
@special(["=", "is", "<", "<=", ">", ">="], [oneplus(FORM)])
|
||||
@special(["!=", "is-not"], [times(2, Inf, FORM)])
|
||||
@ -1271,11 +1277,23 @@ class HyASTCompiler(object):
|
||||
return (self.compile(args[0]) +
|
||||
asty.Name(expr, id="True", ctx=ast.Load()))
|
||||
|
||||
ops = [self.c_ops[ast_str(root)]() for _ in args[1:]]
|
||||
ops = [self._get_c_op(root) for _ in args[1:]]
|
||||
exprs, ret, _ = self._compile_collect(args)
|
||||
return ret + asty.Compare(
|
||||
expr, left=exprs[0], ops=ops, comparators=exprs[1:])
|
||||
|
||||
@special("cmp", [FORM, many(SYM + FORM)])
|
||||
def compile_chained_comparison(self, expr, root, arg1, args):
|
||||
ret = self.compile(arg1)
|
||||
arg1 = ret.force_expr
|
||||
|
||||
ops = [self._get_c_op(op) for op, _ in args]
|
||||
args, ret2, _ = self._compile_collect(
|
||||
[x for _, x in args])
|
||||
|
||||
return ret + ret2 + asty.Compare(expr,
|
||||
left=arg1, ops=ops, comparators=args)
|
||||
|
||||
# The second element of each tuple below is an aggregation operator
|
||||
# that's used for augmented assignment with three or more arguments.
|
||||
m_ops = {"+": (ast.Add, "+"),
|
||||
|
@ -305,6 +305,21 @@
|
||||
(assert (= (f {"x" {"y" {"z" 12}}} "x" "y" "z") 12)))
|
||||
|
||||
|
||||
(defn test-chained-comparison []
|
||||
(assert (cmp 2 = (+ 1 1) = (- 3 1)))
|
||||
(assert (not (cmp 2 = (+ 1 1) = (+ 3 1))))
|
||||
|
||||
(assert (cmp 2 = 2 > 1))
|
||||
(assert (cmp 2 = (+ 1 1) > 1))
|
||||
(setv x 2)
|
||||
(assert (cmp 2 = x > 1))
|
||||
(assert (cmp 2 = x > (> 4 3)))
|
||||
(assert (not (cmp (> 4 3) = x > 1)))
|
||||
|
||||
(assert (cmp 1 in [1] in [[1] [2 3]] not-in [5]))
|
||||
(assert (not (cmp 1 in [1] not-in [[1] [2 3]] not-in [5]))))
|
||||
|
||||
|
||||
(defn test-augassign []
|
||||
(setv b 2 c 3 d 4)
|
||||
(defmacro same-as [expr1 expr2 expected-value]
|
||||
|
Loading…
Reference in New Issue
Block a user