[Python-checkins] r69045 - in python/branches/py3k-issue1717: Lib/test/test_parser.py Modules/parsermodule.c

mark.dickinson python-checkins at python.org
Wed Jan 28 12:41:14 CET 2009


Author: mark.dickinson
Date: Wed Jan 28 12:41:13 2009
New Revision: 69045

Log:
Put (rich) comparisons back in for parser module, and add tests
for those comparisons.

(The parser module docs say that comparisons between ST objects
should be supported.)


Modified:
   python/branches/py3k-issue1717/Lib/test/test_parser.py
   python/branches/py3k-issue1717/Modules/parsermodule.c

Modified: python/branches/py3k-issue1717/Lib/test/test_parser.py
==============================================================================
--- python/branches/py3k-issue1717/Lib/test/test_parser.py	(original)
+++ python/branches/py3k-issue1717/Lib/test/test_parser.py	Wed Jan 28 12:41:13 2009
@@ -2,6 +2,7 @@
 import os
 import unittest
 import sys
+import operator
 from test import support
 
 #
@@ -496,12 +497,81 @@
               file=sys.stderr)
         self.assertRaises(MemoryError, parser.expr, e)
 
+class STObjectTestCase(unittest.TestCase):
+    """Test operations on ST objects themselves"""
+
+    def test_comparisons(self):
+        # ST objects should support order and equality comparisons
+        st1 = parser.expr('2 + 3')
+        st2 = parser.suite('x = 2; y = x + 3')
+        st3 = parser.expr('list(x**3 for x in range(20))')
+        st1_copy = parser.expr('2 + 3')
+        st2_copy = parser.suite('x = 2; y = x + 3')
+        st3_copy = parser.expr('list(x**3 for x in range(20))')
+
+        # exercise fast path for object identity
+        self.assertEquals(st1 == st1, True)
+        self.assertEquals(st2 == st2, True)
+        self.assertEquals(st3 == st3, True)
+        # slow path equality
+        self.assertEqual(st1, st1_copy)
+        self.assertEqual(st2, st2_copy)
+        self.assertEqual(st3, st3_copy)
+        self.assertEquals(st1 == st2, False)
+        self.assertEquals(st1 == st3, False)
+        self.assertEquals(st2 == st3, False)
+        self.assertEquals(st1 != st1, False)
+        self.assertEquals(st2 != st2, False)
+        self.assertEquals(st3 != st3, False)
+        self.assertEquals(st1 != st1_copy, False)
+        self.assertEquals(st2 != st2_copy, False)
+        self.assertEquals(st3 != st3_copy, False)
+        self.assertEquals(st2 != st1, True)
+        self.assertEquals(st1 != st3, True)
+        self.assertEquals(st3 != st2, True)
+        # we don't particularly care what the ordering is;  just that
+        # it's usable and self-consistent
+        self.assertEquals(st1 < st2, not (st2 <= st1))
+        self.assertEquals(st1 < st3, not (st3 <= st1))
+        self.assertEquals(st2 < st3, not (st3 <= st2))
+        self.assertEquals(st1 < st2, st2 > st1)
+        self.assertEquals(st1 < st3, st3 > st1)
+        self.assertEquals(st2 < st3, st3 > st2)
+        self.assertEquals(st1 <= st2, st2 >= st1)
+        self.assertEquals(st3 <= st1, st1 >= st3)
+        self.assertEquals(st2 <= st3, st3 >= st2)
+        # transitivity
+        bottom = min(st1, st2, st3)
+        top = max(st1, st2, st3)
+        mid = sorted([st1, st2, st3])[1]
+        self.assert_(bottom < mid)
+        self.assert_(bottom < top)
+        self.assert_(mid < top)
+        self.assert_(bottom <= mid)
+        self.assert_(bottom <= top)
+        self.assert_(mid <= top)
+        self.assert_(bottom <= bottom)
+        self.assert_(mid <= mid)
+        self.assert_(top <= top)
+        # interaction with other types
+        self.assertEquals(st1 == 1588.602459, False)
+        self.assertEquals('spanish armada' != st2, True)
+        self.assertRaises(TypeError, operator.ge, st3, None)
+        self.assertRaises(TypeError, operator.le, False, st1)
+        self.assertRaises(TypeError, operator.lt, st1, 1815)
+        self.assertRaises(TypeError, operator.gt, b'waterloo', st2)
+
+
+    # XXX tests for pickling and unpickling of ST objects should go here
+
+
 def test_main():
     support.run_unittest(
         RoundtripLegalSyntaxTestCase,
         IllegalSyntaxTestCase,
         CompileTestCase,
         ParserStackLimitTestCase,
+        STObjectTestCase,
     )
 
 

Modified: python/branches/py3k-issue1717/Modules/parsermodule.c
==============================================================================
--- python/branches/py3k-issue1717/Modules/parsermodule.c	(original)
+++ python/branches/py3k-issue1717/Modules/parsermodule.c	Wed Jan 28 12:41:13 2009
@@ -169,6 +169,7 @@
 
 
 static void parser_free(PyST_Object *st);
+static PyObject* parser_richcompare(PyObject *left, PyObject *right, int op);
 static PyObject* parser_compilest(PyST_Object *, PyObject *, PyObject *);
 static PyObject* parser_isexpr(PyST_Object *, PyObject *, PyObject *);
 static PyObject* parser_issuite(PyST_Object *, PyObject *, PyObject *);
@@ -222,7 +223,7 @@
     "Intermediate representation of a Python parse tree.",
     0,                                  /* tp_traverse */
     0,                                  /* tp_clear */
-    0,                                  /* tp_richcompare */
+    parser_richcompare,                 /* tp_richcompare */
     0,                                  /* tp_weaklistoffset */
     0,                                  /* tp_iter */
     0,                                  /* tp_iternext */
@@ -230,6 +231,102 @@
 };  /* PyST_Type */
 
 
+/* PyST_Type isn't subclassable, so just check ob_type */
+#define PyST_Object_Check(v) ((v)->ob_type == &PyST_Type)
+
+static int
+parser_compare_nodes(node *left, node *right)
+{
+    int j;
+
+    if (TYPE(left) < TYPE(right))
+        return (-1);
+
+    if (TYPE(right) < TYPE(left))
+        return (1);
+
+    if (ISTERMINAL(TYPE(left)))
+        return (strcmp(STR(left), STR(right)));
+
+    if (NCH(left) < NCH(right))
+        return (-1);
+
+    if (NCH(right) < NCH(left))
+        return (1);
+
+    for (j = 0; j < NCH(left); ++j) {
+        int v = parser_compare_nodes(CHILD(left, j), CHILD(right, j));
+
+        if (v != 0)
+            return (v);
+    }
+    return (0);
+}
+
+/*  parser_richcompare(PyObject* left, PyObject* right, int op)
+ *
+ *  Comparison function used by the Python operators ==, !=, <, >, <=, >=
+ *  This really just wraps a call to parser_compare_nodes() with some easy
+ *  checks and protection code.
+ *
+ */
+
+#define TEST_COND(cond) ((cond) ? Py_True : Py_False)
+
+static PyObject *
+parser_richcompare(PyObject *left, PyObject *right, int op)
+{
+    int result;
+    PyObject *v;
+
+    /* neither argument should be NULL, unless something's gone wrong */
+    if (left == NULL || right == NULL) {
+        PyErr_BadInternalCall();
+        return NULL;
+    }
+
+    /* both arguments should be instances of PyST_Object */
+    if (!PyST_Object_Check(left) || !PyST_Object_Check(right)) {
+        v = Py_NotImplemented;
+        goto finished;
+    }
+
+    if (left == right)
+        /* if arguments are identical, they're equal */
+        result = 0;
+    else
+        result = parser_compare_nodes(((PyST_Object *)left)->st_node,
+                                      ((PyST_Object *)right)->st_node);
+
+    /* Convert return value to a Boolean */
+    switch (op) {
+    case Py_EQ:
+        v = TEST_COND(result == 0);
+        break;
+    case Py_NE:
+        v = TEST_COND(result != 0);
+        break;
+    case Py_LE:
+        v = TEST_COND(result <= 0);
+        break;
+    case Py_GE:
+        v = TEST_COND(result >= 0);
+        break;
+    case Py_LT:
+        v = TEST_COND(result < 0);
+        break;
+    case Py_GT:
+        v = TEST_COND(result > 0);
+        break;
+    default:
+        PyErr_BadArgument();
+        return NULL;
+    }
+  finished:
+    Py_INCREF(v);
+    return v;
+}
+
 /*  parser_newstobject(node* st)
  *
  *  Allocates a new Python object representing an ST.  This is simply the


More information about the Python-checkins mailing list