diff -r 70e75e8cd224 django/template/defaulttags.py
--- a/django/template/defaulttags.py	Thu Dec 03 15:11:14 2009 +0000
+++ b/django/template/defaulttags.py	Sun Dec 06 01:04:04 2009 +0000
@@ -11,6 +11,7 @@
 from django.template import Node, NodeList, Template, Context, Variable
 from django.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, SINGLE_BRACE_START, SINGLE_BRACE_END, COMMENT_TAG_START, COMMENT_TAG_END
 from django.template import get_library, Library, InvalidTemplateLibrary
+from django.template.smartif import IfParser, Literal
 from django.conf import settings
 from django.utils.encoding import smart_str, smart_unicode
 from django.utils.itercompat import groupby
@@ -227,10 +228,9 @@
         return self.nodelist_false.render(context)
 
 class IfNode(Node):
-    def __init__(self, bool_exprs, nodelist_true, nodelist_false, link_type):
-        self.bool_exprs = bool_exprs
+    def __init__(self, var, nodelist_true, nodelist_false=None):
         self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
-        self.link_type = link_type
+        self.var = var
 
     def __repr__(self):
         return "<If node>"
@@ -250,28 +250,11 @@
         return nodes
 
     def render(self, context):
-        if self.link_type == IfNode.LinkTypes.or_:
-            for ifnot, bool_expr in self.bool_exprs:
-                try:
-                    value = bool_expr.resolve(context, True)
-                except VariableDoesNotExist:
-                    value = None
-                if (value and not ifnot) or (ifnot and not value):
-                    return self.nodelist_true.render(context)
+        if self.var.resolve(context):
+            return self.nodelist_true.render(context)
+        if self.nodelist_false:
             return self.nodelist_false.render(context)
-        else:
-            for ifnot, bool_expr in self.bool_exprs:
-                try:
-                    value = bool_expr.resolve(context, True)
-                except VariableDoesNotExist:
-                    value = None
-                if not ((value and not ifnot) or (ifnot and not value)):
-                    return self.nodelist_false.render(context)
-            return self.nodelist_true.render(context)
-
-    class LinkTypes:
-        and_ = 0,
-        or_ = 1
+        return ''
 
 class RegroupNode(Node):
     def __init__(self, target, expression, var_name):
@@ -761,6 +744,27 @@
     return do_ifequal(parser, token, True)
 ifnotequal = register.tag(ifnotequal)
 
+class TemplateLiteral(Literal):
+    def __init__(self, value, text):
+        self.value = value
+        self.text = text # for better error messages
+
+    def display(self):
+        return self.text
+
+    def resolve(self, context):
+        return self.value.resolve(context, ignore_failures=True)
+
+class TemplateIfParser(IfParser):
+    error_class = TemplateSyntaxError
+
+    def __init__(self, parser, *args, **kwargs):
+        self.template_parser = parser
+        return super(TemplateIfParser, self).__init__(*args, **kwargs)
+
+    def create_var(self, value):
+        return TemplateLiteral(self.template_parser.compile_filter(value), value)
+
 #@register.tag(name="if")
 def do_if(parser, token):
     """
@@ -805,47 +809,21 @@
             There are some athletes and absolutely no coaches.
         {% endif %}
 
-    ``if`` tags do not allow ``and`` and ``or`` clauses with the same tag,
-    because the order of logic would be ambigous. For example, this is
-    invalid::
+    Comparison operators are also available, and the use of filters is also
+    allowed, for example:
 
-        {% if athlete_list and coach_list or cheerleader_list %}
+        {% if articles|length >= 5 %}...{% endif %}
 
-    If you need to combine ``and`` and ``or`` to do advanced logic, just use
-    nested if tags. For example::
+    Arguments and operators _must_ have a space between them, so
+    ``{% if 1>2 %}`` is not a valid if tag.
 
-        {% if athlete_list %}
-            {% if coach_list or cheerleader_list %}
-                We have athletes, and either coaches or cheerleaders!
-            {% endif %}
-        {% endif %}
+    All supported operators are: ``or``, ``and``, ``in``, ``=`` (or ``==``),
+    ``!=``, ``>``, ``>=``, ``<`` and ``<=``.
+
+    Operator precedence follows Python.
     """
-    bits = token.contents.split()
-    del bits[0]
-    if not bits:
-        raise TemplateSyntaxError("'if' statement requires at least one argument")
-    # Bits now looks something like this: ['a', 'or', 'not', 'b', 'or', 'c.d']
-    bitstr = ' '.join(bits)
-    boolpairs = bitstr.split(' and ')
-    boolvars = []
-    if len(boolpairs) == 1:
-        link_type = IfNode.LinkTypes.or_
-        boolpairs = bitstr.split(' or ')
-    else:
-        link_type = IfNode.LinkTypes.and_
-        if ' or ' in bitstr:
-            raise TemplateSyntaxError, "'if' tags can't mix 'and' and 'or'"
-    for boolpair in boolpairs:
-        if ' ' in boolpair:
-            try:
-                not_, boolvar = boolpair.split()
-            except ValueError:
-                raise TemplateSyntaxError, "'if' statement improperly formatted"
-            if not_ != 'not':
-                raise TemplateSyntaxError, "Expected 'not' in if statement"
-            boolvars.append((True, parser.compile_filter(boolvar)))
-        else:
-            boolvars.append((False, parser.compile_filter(boolpair)))
+    bits = token.split_contents()[1:]
+    var = TemplateIfParser(parser, bits).parse()
     nodelist_true = parser.parse(('else', 'endif'))
     token = parser.next_token()
     if token.contents == 'else':
@@ -853,7 +831,7 @@
         parser.delete_first_token()
     else:
         nodelist_false = NodeList()
-    return IfNode(boolvars, nodelist_true, nodelist_false, link_type)
+    return IfNode(var, nodelist_true, nodelist_false)
 do_if = register.tag("if", do_if)
 
 #@register.tag
diff -r 70e75e8cd224 django/template/smartif.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/django/template/smartif.py	Sun Dec 06 01:04:04 2009 +0000
@@ -0,0 +1,181 @@
+"""
+Parser and utilities for the smart 'if' tag
+"""
+
+# Using a simple top down parser, as described here:
+#    http://effbot.org/zone/simple-top-down-parsing.htm.
+# 'led' = left denotation
+# 'nud' = null denotation
+# 'bp' = binding power (left = lbp, right = rbp)
+
+class TokenBase(object):
+    """
+    Base class for operators and literals, mainly for debugging and for throwing
+    syntax errors.
+    """
+    id = None # node/token type name
+    value = None # used by literals
+    first = second = None # used by tree nodes
+
+    def nud(self, parser):
+        # Null denotation - called in prefix context
+        raise parser.error_class(
+            "Not expecting '%s' in this position in if tag." % self.id
+        )
+
+    def led(self, left, parser):
+        # Left denotation - called in infix context
+        raise parser.error_class(
+            "Not expecting '%s' as infix operator in if tag." % self.id
+        )
+
+    def display(self):
+        """
+        What to display in error messages for this node
+        """
+        return self.id
+
+    def __repr__(self):
+        if self.id == "literal":
+            return "(%s %r)" % (self.id, self.value)
+        out = [self.id, self.first, self.second]
+        out = map(str, filter(None, out))
+        return "(" + " ".join(out) + ")"
+
+
+def infix(bp, func):
+    """
+    Create an infix operator, given a binding power and a function that
+    evaluates the node
+    """
+    class Operator(TokenBase):
+        lbp = bp
+
+        def led(self, left, parser):
+            self.first = left
+            self.second = parser.expression(bp)
+            return self
+
+        def resolve(self, context):
+            return func(self.first.resolve(context), self.second.resolve(context))
+
+    return Operator
+
+
+def prefix(bp, func):
+    """
+    Create a prefix operator, given a binding power and a function that
+    evaluates the node.
+    """
+    class Operator(TokenBase):
+        lbp = bp
+
+        def nud(self, parser):
+            self.first = parser.expression(bp)
+            self.second = None
+            return self
+
+        def resolve(self, context):
+            return func(self.first.resolve(context))
+
+    return Operator
+
+
+# Operator precedence follows Python.
+# NB - we can get slightly more accurate syntax error messages by not using the
+# same object for '==' and '='.
+
+OPERATORS = {
+    'or': infix(6, lambda x, y: x or y),
+    'and': infix(7, lambda x, y: x and y),
+    'not': prefix(8, lambda x: not x),
+    'in': infix(9, lambda x, y: y is not None and x in y),
+    '=': infix(10, lambda x, y: x == y),
+    '==': infix(10, lambda x, y: x == y),
+    '!=': infix(10, lambda x, y: x != y),
+    '>': infix(10, lambda x, y: x > y),
+    '>=': infix(10, lambda x, y: x >= y),
+    '<': infix(10, lambda x, y: x < y),
+    '<=': infix(10, lambda x, y: x <= y),
+}
+
+# Assign 'id' to each:
+for key, op in OPERATORS.items():
+    op.id = key
+
+
+class Literal(TokenBase):
+    """
+    A basic self-resolvable object similar to a Django template variable.
+    """
+    # IfParser uses Literal in create_var, but TemplateIfParser overrides
+    # create_var so that a proper implementation that actually resolves
+    # variables, filters etc are used.
+    id = "literal"
+    lbp = 0
+    def __init__(self, value):
+        self.value = value
+
+    def display(self):
+        return repr(self.value)
+
+    def nud(self, parser):
+        return self
+
+    def resolve(self, context):
+        return self.value
+
+
+class EndToken(TokenBase):
+    lbp = 0
+
+    def nud(self, parser):
+        raise parser.error_class("Unexpected end of expression in if tag.")
+
+EndToken = EndToken()
+
+
+class IfParser(object):
+    error_class = ValueError
+
+    def __init__(self, tokens):
+        self.tokens = map(self.translate_tokens, tokens)
+        self.pos = 0
+        self.current_token = self.next()
+
+    def translate_tokens(self, token):
+        try:
+            op = OPERATORS[token]
+        except (KeyError, TypeError):
+            return self.create_var(token)
+        else:
+            return op()
+
+    def next(self):
+        if self.pos >= len(self.tokens):
+            return EndToken
+        else:
+            retval = self.tokens[self.pos]
+            self.pos += 1
+            return retval
+
+    def parse(self):
+        retval = self.expression()
+        # Check that we have exhausted all the tokens
+        if self.current_token is not EndToken:
+            raise self.error_class("Unused '%s' at end of if expression." %
+                                   self.current_token.display())
+        return retval
+
+    def expression(self, rbp=0):
+        t = self.current_token
+        self.current_token = self.next()
+        left = t.nud(self)
+        while rbp < self.current_token.lbp:
+            t = self.current_token
+            self.current_token = self.next()
+            left = t.led(left, self)
+        return left
+
+    def create_var(self, value):
+        return Literal(value)
diff -r 70e75e8cd224 docs/ref/templates/builtins.txt
--- a/docs/ref/templates/builtins.txt	Thu Dec 03 15:11:14 2009 +0000
+++ b/docs/ref/templates/builtins.txt	Sun Dec 06 01:04:04 2009 +0000
@@ -313,6 +313,9 @@
 As you can see, the ``if`` tag can take an optional ``{% else %}`` clause that
 will be displayed if the test fails.
 
+Boolean operators
+^^^^^^^^^^^^^^^^^
+
 ``if`` tags may use ``and``, ``or`` or ``not`` to test a number of variables or
 to negate a given variable::
 
@@ -338,24 +341,153 @@
         There are some athletes and absolutely no coaches.
     {% endif %}
 
-``if`` tags don't allow ``and`` and ``or`` clauses within the same tag, because
-the order of logic would be ambiguous. For example, this is invalid::
+.. versionchanged:: 1.2
+
+If you use ``and`` and ``or`` clauses within the same tag, it is interpreted
+left to right, e.g.::
 
     {% if athlete_list and coach_list or cheerleader_list %}
 
-If you need to combine ``and`` and ``or`` to do advanced logic, just use nested
-``if`` tags. For example::
+will be interpreted like:
 
-    {% if athlete_list %}
-        {% if coach_list or cheerleader_list %}
-            We have athletes, and either coaches or cheerleaders!
-        {% endif %}
+.. code-block:: python
+
+    if (athlete_list and coach_list) or cheerleader_list
+
+Use of actual brackets in the ``if`` tag is invalid syntax.  If you need them to
+indicate precedence, you should use nested ``if`` tags.
+
+.. versionadded:: 1.2
+
+
+``if`` tags may also use the operators ``==``, ``!=``, ``<``, ``>``,
+``<=``, ``>=`` and ``in`` which work as follows:
+
+
+``==`` operator
+^^^^^^^^^^^^^^^
+
+Equality. Example::
+
+    {% if somevar == "x" %}
+      This appears if variable somevar equals the string "x"
     {% endif %}
 
-Multiple uses of the same logical operator are fine, as long as you use the
-same operator. For example, this is valid::
+``!=`` operator
+^^^^^^^^^^^^^^^
 
-    {% if athlete_list or coach_list or parent_list or teacher_list %}
+Inequality. Example::
+
+    {% if somevar != "x" %}
+      This appears if variable somevar does not equal the string "x",
+      or if somevar is not found in the context
+    {% endif %}
+
+``<`` operator
+^^^^^^^^^^^^^^
+
+Less than. Example::
+
+    {% if somevar < 100 %}
+      This appears if variable somevar is less than 100.
+    {% endif %}
+
+``>`` operator
+^^^^^^^^^^^^^^
+
+Greater than. Example::
+
+    {% if somevar > 0 %}
+      This appears if variable somevar is greater than 0.
+    {% endif %}
+
+``<=`` operator
+^^^^^^^^^^^^^^^
+
+Less than or equal to. Example::
+
+    {% if somevar <= 100 %}
+      This appears if variable somevar is less than 100 or equal to 100.
+    {% endif %}
+
+``>=`` operator
+^^^^^^^^^^^^^^^
+
+Greater than or equal to. Example::
+
+    {% if somevar >= 1 %}
+      This appears if variable somevar is greater than 1 or equal to 1.
+    {% endif %}
+
+``in`` operator
+^^^^^^^^^^^^^^^
+
+Contained within. This operator is supported by many Python containers to test
+whether the given value is in the container.  The following are some examples of
+how ``x in y`` will be interpreted::
+
+    {% if "bc" in "abcdef" %}
+      This appears since "bc" is a substring of "abcdef"
+    {% endif %}
+
+    {% if "hello" in greetings %}
+      If greetings is a list or set, one element of which is the string
+      "hello", this will appear.
+    {% endif %}
+
+    {% if user in users %}
+      If users is a QuerySet, this will appear if user is an
+      instance that belongs to the QuerySet.
+    {% endif %}
+
+
+The comparison operators cannot be 'chained' like in Python or in mathematical
+notation. For example, instead of using::
+
+    {% if a > b > c %}  (WRONG)
+
+you should use::
+
+    {% if a > b and b > c %}
+
+
+Filters
+^^^^^^^
+
+You can also use filters in the ``if`` expression. For example::
+
+    {% if messages|length >= 100 %}
+       You have lots of messages today!
+    {% endif %}
+
+Complex expressions
+^^^^^^^^^^^^^^^^^^^
+
+All of the above can be combined to form complex expressions. For such
+expressions, it can be important to know how the operators are grouped when the
+expression is evaluated - that is, the precedence rules.  The precedence of the
+operators, from lowest to highest, is as follows:
+
+ * ``or``
+ * ``and``
+ * ``not``
+ * ``in``
+ * ``==``, ``!=``, ``<``, ``>``,``<=``, ``>=``
+
+(This follows Python exactly). So, for example, the following complex if tag:
+
+    {% if a == b or c == d and e %}
+
+...will be interpreted as:
+
+.. code-block:: python
+
+    (a == b) or ((c == d) and e)
+
+If you need different precedence, you will need to use nested if tags. Sometimes
+that is better for clarity anyway, for the sake of those who do not know the
+precedence rules.
+
 
 .. templatetag:: ifchanged
 
@@ -427,6 +559,9 @@
 ``False``.  If you need to test if something is true or false, use the ``if``
 tag instead.
 
+.. versionadded:: 1.2
+   An alternative to the ``ifequal`` tag is to use the :ttag:`if` tag and the ``==`` operator.
+
 .. templatetag:: ifnotequal
 
 ifnotequal
@@ -434,6 +569,9 @@
 
 Just like ``ifequal``, except it tests that the two arguments are not equal.
 
+.. versionadded:: 1.2
+   An alternative to the ``ifnotequal`` tag is to use the :ttag:`if` tag and the ``!=`` operator.
+
 .. templatetag:: include
 
 include
diff -r 70e75e8cd224 docs/releases/1.2.txt
--- a/docs/releases/1.2.txt	Thu Dec 03 15:11:14 2009 +0000
+++ b/docs/releases/1.2.txt	Sun Dec 06 01:04:04 2009 +0000
@@ -42,6 +42,15 @@
  * All of the CSRF has moved from contrib to core (with backwards compatible
    imports in the old locations, which are deprecated).
 
+:ttag:`if` tag changes
+----------------------
+
+Due to new features in the :ttag:`if` template tag, it no longer accepts 'and',
+'or' and 'not' as valid **variable** names.  Previously that worked in some
+cases even though these strings were normally treated as keywords.  Now, the
+keyword status is always enforced, and template code like ``{% if not %}`` or
+``{% if and %}`` will throw a TemplateSyntaxError.
+
 ``LazyObject``
 --------------
 
@@ -155,3 +164,36 @@
 :ref:`memory<topic-email-memory-backend>` - you can even configure all
 e-mail to be :ref:`thrown away<topic-email-dummy-backend>`.
 
+'Smart' if tag
+--------------
+
+The :ttag:`if` tag has been upgraded to be much more powerful.  First, support
+for comparison operators has been added. No longer will you have to type:
+
+.. code-block:: html+django
+
+    {% ifnotequal a b %}
+     ...
+    {% endifnotequal %}
+
+...as you can now do:
+
+.. code-block:: html+django
+
+    {% if a != b %}
+     ...
+    {% endif %}
+
+The operators supported are ``==``, ``!=``, ``<``, ``>``, ``<=``, ``>=`` and
+``in``, all of which work like the Python operators, in addition to ``and``,
+``or`` and ``not`` which were already supported.
+
+Also, filters may now be used in the ``if`` expression. For example:
+
+.. code-block:: html+django
+
+      <div
+        {% if user.email|lower == message.recipient|lower %}
+          class="highlight"
+        {% endif %}
+      >{{ message }}</div>
diff -r 70e75e8cd224 docs/topics/templates.txt
--- a/docs/topics/templates.txt	Thu Dec 03 15:11:14 2009 +0000
+++ b/docs/topics/templates.txt	Sun Dec 06 01:04:04 2009 +0000
@@ -187,8 +187,8 @@
                 <li>{{ athlete.name }}</li>
             {% endfor %}
             </ul>
-        
-    :ttag:`if` and :ttag:`else`
+
+    :ttag:`if` and ``else``
         Evaluates a variable, and if that variable is "true" the contents of the
         block are displayed::
 
@@ -200,20 +200,15 @@
 
         In the above, if ``athlete_list`` is not empty, the number of athletes
         will be displayed by the ``{{ athlete_list|length }}`` variable.
-        
-    :ttag:`ifequal` and :ttag:`ifnotequal`
-        Display some contents if two arguments are or are not equal. For example::
 
-            {% ifequal athlete.name coach.name %}
-                ...
-            {% endifequal %}
+        You can also use filters and various operators in the ``if`` tag::
 
-        Or::
+            {% if athlete_list|length > 1 %}
+               Team: {% for athlete in athlete_list %} ... {% endfor %}
+            {% else %}
+               Athlete: {{ athlete_list.0.name }}
+            {% endif %}
 
-            {% ifnotequal athlete.name "Joe" %}
-                ...
-            {% endifnotequal %}
-    
     :ttag:`block` and :ttag:`extends`
         Set up `template inheritance`_ (see below), a powerful way
         of cutting down on "boilerplate" in templates.
diff -r 70e75e8cd224 tests/regressiontests/templates/smartif.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/regressiontests/templates/smartif.py	Sun Dec 06 01:04:04 2009 +0000
@@ -0,0 +1,46 @@
+import unittest
+from django.template.smartif import IfParser, Literal
+
+class SmartIfTests(unittest.TestCase):
+
+    def assertCalcEqual(self, expected, tokens):
+        self.assertEqual(expected, IfParser(tokens).parse().resolve({}))
+
+    # We only test things here that are difficult to test elsewhere
+    # Many other tests are found in the main tests for builtin template tags
+    # Test parsing via the printed parse tree
+    def test_not(self):
+        var = IfParser(["not", False]).parse()
+        self.assertEqual("(not (literal False))", repr(var))
+        self.assert_(var.resolve({}))
+
+        self.assertFalse(IfParser(["not", True]).parse().resolve({}))
+
+    def test_or(self):
+        var = IfParser([True, "or", False]).parse()
+        self.assertEqual("(or (literal True) (literal False))", repr(var))
+        self.assert_(var.resolve({}))
+
+    def test_in(self):
+        list_ = [1,2,3]
+        self.assertCalcEqual(True, [1, 'in', list_])
+        self.assertCalcEqual(False, [1, 'in', None])
+        self.assertCalcEqual(False, [None, 'in', list_])
+
+    def test_precedence(self):
+        # (False and False) or True == True   <- we want this one, like Python
+        # False and (False or True) == False
+        self.assertCalcEqual(True, [False, 'and', False, 'or', True])
+
+        # True or (False and False) == True   <- we want this one, like Python
+        # (True or False) and False == False
+        self.assertCalcEqual(True, [True, 'or', False, 'and', False])
+
+        # (1 or 1) == 2  -> False
+        # 1 or (1 == 2)  -> True   <- we want this one
+        self.assertCalcEqual(True, [1, 'or', 1, '==', 2])
+
+        self.assertCalcEqual(True, [True, '==', True, 'or', True, '==', False])
+
+        self.assertEqual("(or (and (== (literal 1) (literal 2)) (literal 3)) (literal 4))",
+                         repr(IfParser([1, '==', 2, 'and', 3, 'or', 4]).parse()))
diff -r 70e75e8cd224 tests/regressiontests/templates/tests.py
--- a/tests/regressiontests/templates/tests.py	Thu Dec 03 15:11:14 2009 +0000
+++ b/tests/regressiontests/templates/tests.py	Sun Dec 06 01:04:04 2009 +0000
@@ -24,6 +24,7 @@
 from custom import custom_filters
 from parser import filter_parsing, variable_parsing
 from unicode import unicode_tests
+from smartif import *
 
 try:
     from loaders import *
@@ -534,6 +535,27 @@
             'if-tag02': ("{% if foo %}yes{% else %}no{% endif %}", {"foo": False}, "no"),
             'if-tag03': ("{% if foo %}yes{% else %}no{% endif %}", {}, "no"),
 
+            # Filters
+            'if-tag-filter01': ("{% if foo|length == 5 %}yes{% else %}no{% endif %}", {'foo': 'abcde'}, "yes"),
+            'if-tag-filter02': ("{% if foo|upper == 'ABC' %}yes{% else %}no{% endif %}", {}, "no"),
+
+            # Equality
+            'if-tag-eq01': ("{% if foo == bar %}yes{% else %}no{% endif %}", {}, "yes"),
+            'if-tag-eq02': ("{% if foo == bar %}yes{% else %}no{% endif %}", {'foo': 1}, "no"),
+            'if-tag-eq03': ("{% if foo == bar %}yes{% else %}no{% endif %}", {'foo': 1, 'bar': 1}, "yes"),
+            'if-tag-eq04': ("{% if foo == bar %}yes{% else %}no{% endif %}", {'foo': 1, 'bar': 2}, "no"),
+            'if-tag-eq05': ("{% if foo == '' %}yes{% else %}no{% endif %}", {}, "no"),
+
+            # Comparison
+            'if-tag-gt-01': ("{% if 2 > 1 %}yes{% else %}no{% endif %}", {}, "yes"),
+            'if-tag-gt-02': ("{% if 1 > 1 %}yes{% else %}no{% endif %}", {}, "no"),
+            'if-tag-gte-01': ("{% if 1 >= 1 %}yes{% else %}no{% endif %}", {}, "yes"),
+            'if-tag-gte-02': ("{% if 1 >= 2 %}yes{% else %}no{% endif %}", {}, "no"),
+            'if-tag-lt-01': ("{% if 1 < 2 %}yes{% else %}no{% endif %}", {}, "yes"),
+            'if-tag-lt-02': ("{% if 1 < 1 %}yes{% else %}no{% endif %}", {}, "no"),
+            'if-tag-lte-01': ("{% if 1 <= 1 %}yes{% else %}no{% endif %}", {}, "yes"),
+            'if-tag-lte-02': ("{% if 2 <= 1 %}yes{% else %}no{% endif %}", {}, "no"),
+
             # AND
             'if-tag-and01': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'yes'),
             'if-tag-and02': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'no'),
@@ -554,14 +576,13 @@
             'if-tag-or07': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': True}, 'yes'),
             'if-tag-or08': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'bar': True}, 'yes'),
 
-            # TODO: multiple ORs
+            # multiple ORs
+            'if-tag-or09': ("{% if foo or bar or baz %}yes{% else %}no{% endif %}", {'baz': True}, 'yes'),
 
             # NOT
             'if-tag-not01': ("{% if not foo %}no{% else %}yes{% endif %}", {'foo': True}, 'yes'),
-            'if-tag-not02': ("{% if not %}yes{% else %}no{% endif %}", {'foo': True}, 'no'),
-            'if-tag-not03': ("{% if not %}yes{% else %}no{% endif %}", {'not': True}, 'yes'),
-            'if-tag-not04': ("{% if not not %}no{% else %}yes{% endif %}", {'not': True}, 'yes'),
-            'if-tag-not05': ("{% if not not %}no{% else %}yes{% endif %}", {}, 'no'),
+            'if-tag-not02': ("{% if not not foo %}no{% else %}yes{% endif %}", {'foo': True}, 'no'),
+            # not03 to not05 removed, now TemplateSyntaxErrors
 
             'if-tag-not06': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {}, 'no'),
             'if-tag-not07': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'no'),
@@ -599,12 +620,21 @@
             'if-tag-not34': ("{% if not foo or not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'yes'),
             'if-tag-not35': ("{% if not foo or not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'yes'),
 
-            # AND and OR raises a TemplateSyntaxError
-            'if-tag-error01': ("{% if foo or bar and baz %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, template.TemplateSyntaxError),
+            # Various syntax errors
+            'if-tag-error01': ("{% if %}yes{% endif %}", {}, template.TemplateSyntaxError),
             'if-tag-error02': ("{% if foo and %}yes{% else %}no{% endif %}", {'foo': True}, template.TemplateSyntaxError),
             'if-tag-error03': ("{% if foo or %}yes{% else %}no{% endif %}", {'foo': True}, template.TemplateSyntaxError),
             'if-tag-error04': ("{% if not foo and %}yes{% else %}no{% endif %}", {'foo': True}, template.TemplateSyntaxError),
             'if-tag-error05': ("{% if not foo or %}yes{% else %}no{% endif %}", {'foo': True}, template.TemplateSyntaxError),
+            'if-tag-error06': ("{% if abc def %}yes{% endif %}", {}, template.TemplateSyntaxError),
+            'if-tag-error07': ("{% if not %}yes{% endif %}", {}, template.TemplateSyntaxError),
+            'if-tag-error08': ("{% if and %}yes{% endif %}", {}, template.TemplateSyntaxError),
+            'if-tag-error09': ("{% if or %}yes{% endif %}", {}, template.TemplateSyntaxError),
+            'if-tag-error10': ("{% if == %}yes{% endif %}", {}, template.TemplateSyntaxError),
+            'if-tag-error11': ("{% if 1 == %}yes{% endif %}", {}, template.TemplateSyntaxError),
+            'if-tag-error12': ("{% if a not b %}yes{% endif %}", {}, template.TemplateSyntaxError),
+
+            # Additional, more precise parsing tests are in SmartIfTests
 
             ### IFCHANGED TAG #########################################################
             'ifchanged01': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}', {'num': (1,2,3)}, '123'),
