From e8f3a8478a61a5fb681d098c9b1c556bb39b705a Mon Sep 17 00:00:00 2001
From: Dino Chiesa <dchiesa@google.com>
Date: Tue, 27 Feb 2024 15:03:26 -0800
Subject: [PATCH 1/2] fix: handle unusual spacing in conditions

---
 build/ConditionParser.js                     | 252 ++++++++++---------
 lib/package/plugins/CC007-ConditionSyntax.js |   4 +-
 lib/peggy/Apigee-Condition.pegjs             |   8 +-
 test/specs/CC007-Condition-Syntax-Test.js    |  56 +++++
 test/specs/ConditionParserTest.js            |   4 +-
 5 files changed, 199 insertions(+), 125 deletions(-)

diff --git a/build/ConditionParser.js b/build/ConditionParser.js
index c0070da..1d1d29f 100644
--- a/build/ConditionParser.js
+++ b/build/ConditionParser.js
@@ -529,40 +529,22 @@ function peg$parse(input, options) {
     if (s1 !== peg$FAILED) {
       s2 = [];
       s3 = peg$parsews();
-      if (s3 !== peg$FAILED) {
-        while (s3 !== peg$FAILED) {
-          s2.push(s3);
-          s3 = peg$parsews();
-        }
-      } else {
-        s2 = peg$FAILED;
+      while (s3 !== peg$FAILED) {
+        s2.push(s3);
+        s3 = peg$parsews();
       }
-      if (s2 !== peg$FAILED) {
-        s3 = peg$parseop_boolean();
-        if (s3 !== peg$FAILED) {
-          s4 = [];
+      s3 = peg$parseop_boolean();
+      if (s3 !== peg$FAILED) {
+        s4 = [];
+        s5 = peg$parsews();
+        while (s5 !== peg$FAILED) {
+          s4.push(s5);
           s5 = peg$parsews();
-          if (s5 !== peg$FAILED) {
-            while (s5 !== peg$FAILED) {
-              s4.push(s5);
-              s5 = peg$parsews();
-            }
-          } else {
-            s4 = peg$FAILED;
-          }
-          if (s4 !== peg$FAILED) {
-            s5 = peg$parseboolean_stmt1();
-            if (s5 !== peg$FAILED) {
-              peg$savedPos = s0;
-              s0 = peg$f0(s1, s3, s5);
-            } else {
-              peg$currPos = s0;
-              s0 = peg$FAILED;
-            }
-          } else {
-            peg$currPos = s0;
-            s0 = peg$FAILED;
-          }
+        }
+        s5 = peg$parseboolean_stmt1();
+        if (s5 !== peg$FAILED) {
+          peg$savedPos = s0;
+          s0 = peg$f0(s1, s3, s5);
         } else {
           peg$currPos = s0;
           s0 = peg$FAILED;
@@ -675,67 +657,79 @@ function peg$parse(input, options) {
   }
 
   function peg$parseprimary() {
-    var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9;
+    var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12;
 
     s0 = peg$currPos;
+    s1 = [];
+    s2 = peg$parsews();
+    while (s2 !== peg$FAILED) {
+      s1.push(s2);
+      s2 = peg$parsews();
+    }
     if (input.charCodeAt(peg$currPos) === 40) {
-      s1 = peg$c0;
+      s2 = peg$c0;
       peg$currPos++;
     } else {
-      s1 = peg$FAILED;
+      s2 = peg$FAILED;
       if (peg$silentFails === 0) { peg$fail(peg$e0); }
     }
-    if (s1 !== peg$FAILED) {
-      s2 = [];
-      s3 = peg$parsews();
-      while (s3 !== peg$FAILED) {
-        s2.push(s3);
-        s3 = peg$parsews();
+    if (s2 !== peg$FAILED) {
+      s3 = [];
+      s4 = peg$parsews();
+      while (s4 !== peg$FAILED) {
+        s3.push(s4);
+        s4 = peg$parsews();
       }
-      s3 = peg$parsetoken();
-      if (s3 !== peg$FAILED) {
-        s4 = [];
-        s5 = peg$parsews();
-        if (s5 !== peg$FAILED) {
-          while (s5 !== peg$FAILED) {
-            s4.push(s5);
-            s5 = peg$parsews();
+      s4 = peg$parsetoken();
+      if (s4 !== peg$FAILED) {
+        s5 = [];
+        s6 = peg$parsews();
+        if (s6 !== peg$FAILED) {
+          while (s6 !== peg$FAILED) {
+            s5.push(s6);
+            s6 = peg$parsews();
           }
         } else {
-          s4 = peg$FAILED;
+          s5 = peg$FAILED;
         }
-        if (s4 !== peg$FAILED) {
-          s5 = peg$parseop_accepts_literal();
-          if (s5 !== peg$FAILED) {
-            s6 = [];
-            s7 = peg$parsews();
-            if (s7 !== peg$FAILED) {
-              while (s7 !== peg$FAILED) {
-                s6.push(s7);
-                s7 = peg$parsews();
+        if (s5 !== peg$FAILED) {
+          s6 = peg$parseop_accepts_literal();
+          if (s6 !== peg$FAILED) {
+            s7 = [];
+            s8 = peg$parsews();
+            if (s8 !== peg$FAILED) {
+              while (s8 !== peg$FAILED) {
+                s7.push(s8);
+                s8 = peg$parsews();
               }
             } else {
-              s6 = peg$FAILED;
+              s7 = peg$FAILED;
             }
-            if (s6 !== peg$FAILED) {
-              s7 = peg$parsevalue();
-              if (s7 !== peg$FAILED) {
-                s8 = [];
-                s9 = peg$parsews();
-                while (s9 !== peg$FAILED) {
-                  s8.push(s9);
-                  s9 = peg$parsews();
+            if (s7 !== peg$FAILED) {
+              s8 = peg$parsevalue();
+              if (s8 !== peg$FAILED) {
+                s9 = [];
+                s10 = peg$parsews();
+                while (s10 !== peg$FAILED) {
+                  s9.push(s10);
+                  s10 = peg$parsews();
                 }
                 if (input.charCodeAt(peg$currPos) === 41) {
-                  s9 = peg$c1;
+                  s10 = peg$c1;
                   peg$currPos++;
                 } else {
-                  s9 = peg$FAILED;
+                  s10 = peg$FAILED;
                   if (peg$silentFails === 0) { peg$fail(peg$e1); }
                 }
-                if (s9 !== peg$FAILED) {
+                if (s10 !== peg$FAILED) {
+                  s11 = [];
+                  s12 = peg$parsews();
+                  while (s12 !== peg$FAILED) {
+                    s11.push(s12);
+                    s12 = peg$parsews();
+                  }
                   peg$savedPos = s0;
-                  s0 = peg$f3(s3, s5, s7);
+                  s0 = peg$f3(s4, s6, s8);
                 } else {
                   peg$currPos = s0;
                   s0 = peg$FAILED;
@@ -818,64 +812,76 @@ function peg$parse(input, options) {
       }
       if (s0 === peg$FAILED) {
         s0 = peg$currPos;
+        s1 = [];
+        s2 = peg$parsews();
+        while (s2 !== peg$FAILED) {
+          s1.push(s2);
+          s2 = peg$parsews();
+        }
         if (input.charCodeAt(peg$currPos) === 40) {
-          s1 = peg$c0;
+          s2 = peg$c0;
           peg$currPos++;
         } else {
-          s1 = peg$FAILED;
+          s2 = peg$FAILED;
           if (peg$silentFails === 0) { peg$fail(peg$e0); }
         }
-        if (s1 !== peg$FAILED) {
-          s2 = [];
-          s3 = peg$parsews();
-          while (s3 !== peg$FAILED) {
-            s2.push(s3);
-            s3 = peg$parsews();
+        if (s2 !== peg$FAILED) {
+          s3 = [];
+          s4 = peg$parsews();
+          while (s4 !== peg$FAILED) {
+            s3.push(s4);
+            s4 = peg$parsews();
           }
-          s3 = peg$parsetoken();
-          if (s3 !== peg$FAILED) {
-            s4 = [];
-            s5 = peg$parsews();
-            if (s5 !== peg$FAILED) {
-              while (s5 !== peg$FAILED) {
-                s4.push(s5);
-                s5 = peg$parsews();
+          s4 = peg$parsetoken();
+          if (s4 !== peg$FAILED) {
+            s5 = [];
+            s6 = peg$parsews();
+            if (s6 !== peg$FAILED) {
+              while (s6 !== peg$FAILED) {
+                s5.push(s6);
+                s6 = peg$parsews();
               }
             } else {
-              s4 = peg$FAILED;
+              s5 = peg$FAILED;
             }
-            if (s4 !== peg$FAILED) {
-              s5 = peg$parseop_accepts_variable();
-              if (s5 !== peg$FAILED) {
-                s6 = [];
-                s7 = peg$parsews();
-                if (s7 !== peg$FAILED) {
-                  while (s7 !== peg$FAILED) {
-                    s6.push(s7);
-                    s7 = peg$parsews();
+            if (s5 !== peg$FAILED) {
+              s6 = peg$parseop_accepts_variable();
+              if (s6 !== peg$FAILED) {
+                s7 = [];
+                s8 = peg$parsews();
+                if (s8 !== peg$FAILED) {
+                  while (s8 !== peg$FAILED) {
+                    s7.push(s8);
+                    s8 = peg$parsews();
                   }
                 } else {
-                  s6 = peg$FAILED;
+                  s7 = peg$FAILED;
                 }
-                if (s6 !== peg$FAILED) {
-                  s7 = peg$parsetoken();
-                  if (s7 !== peg$FAILED) {
-                    s8 = [];
-                    s9 = peg$parsews();
-                    while (s9 !== peg$FAILED) {
-                      s8.push(s9);
-                      s9 = peg$parsews();
+                if (s7 !== peg$FAILED) {
+                  s8 = peg$parsetoken();
+                  if (s8 !== peg$FAILED) {
+                    s9 = [];
+                    s10 = peg$parsews();
+                    while (s10 !== peg$FAILED) {
+                      s9.push(s10);
+                      s10 = peg$parsews();
                     }
                     if (input.charCodeAt(peg$currPos) === 41) {
-                      s9 = peg$c1;
+                      s10 = peg$c1;
                       peg$currPos++;
                     } else {
-                      s9 = peg$FAILED;
+                      s10 = peg$FAILED;
                       if (peg$silentFails === 0) { peg$fail(peg$e1); }
                     }
-                    if (s9 !== peg$FAILED) {
+                    if (s10 !== peg$FAILED) {
+                      s11 = [];
+                      s12 = peg$parsews();
+                      while (s12 !== peg$FAILED) {
+                        s11.push(s12);
+                        s12 = peg$parsews();
+                      }
                       peg$savedPos = s0;
-                      s0 = peg$f5(s3, s5, s7);
+                      s0 = peg$f5(s4, s6, s8);
                     } else {
                       peg$currPos = s0;
                       s0 = peg$FAILED;
@@ -974,18 +980,30 @@ function peg$parse(input, options) {
                 if (peg$silentFails === 0) { peg$fail(peg$e0); }
               }
               if (s1 !== peg$FAILED) {
-                s2 = peg$parsetoken();
-                if (s2 !== peg$FAILED) {
+                s2 = [];
+                s3 = peg$parsews();
+                while (s3 !== peg$FAILED) {
+                  s2.push(s3);
+                  s3 = peg$parsews();
+                }
+                s3 = peg$parsetoken();
+                if (s3 !== peg$FAILED) {
+                  s4 = [];
+                  s5 = peg$parsews();
+                  while (s5 !== peg$FAILED) {
+                    s4.push(s5);
+                    s5 = peg$parsews();
+                  }
                   if (input.charCodeAt(peg$currPos) === 41) {
-                    s3 = peg$c1;
+                    s5 = peg$c1;
                     peg$currPos++;
                   } else {
-                    s3 = peg$FAILED;
+                    s5 = peg$FAILED;
                     if (peg$silentFails === 0) { peg$fail(peg$e1); }
                   }
-                  if (s3 !== peg$FAILED) {
+                  if (s5 !== peg$FAILED) {
                     peg$savedPos = s0;
-                    s0 = peg$f8(s2);
+                    s0 = peg$f8(s3);
                   } else {
                     peg$currPos = s0;
                     s0 = peg$FAILED;
diff --git a/lib/package/plugins/CC007-ConditionSyntax.js b/lib/package/plugins/CC007-ConditionSyntax.js
index 0ccbbce..c96b8a3 100644
--- a/lib/package/plugins/CC007-ConditionSyntax.js
+++ b/lib/package/plugins/CC007-ConditionSyntax.js
@@ -1,5 +1,5 @@
 /*
-  Copyright 2019-2023 Google LLC
+  Copyright 2019-2024 Google LLC
 
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
@@ -31,7 +31,7 @@ const plugin = {
 const onCondition = function (condition, cb) {
   debug(`onCondition (${condition.getExpression()})`);
   let flagged = false;
-  const expr = condition.getExpression();
+  const expr = condition.getExpression().trim();
   try {
     parser.parse(expr);
   } catch (e) {
diff --git a/lib/peggy/Apigee-Condition.pegjs b/lib/peggy/Apigee-Condition.pegjs
index 47fec58..72d1fef 100644
--- a/lib/peggy/Apigee-Condition.pegjs
+++ b/lib/peggy/Apigee-Condition.pegjs
@@ -66,7 +66,7 @@ start
   = boolean_stmt1
 
 boolean_stmt1
-  = left:boolean_stmt2 ws+ op1:op_boolean ws+ right:boolean_stmt1 { return {operator:op1, operands:[left, right] } }
+  = left:boolean_stmt2 ws* op1:op_boolean ws* right:boolean_stmt1 { return {operator:op1, operands:[left, right] } }
   / boolean_stmt2
 
 boolean_stmt2
@@ -78,20 +78,20 @@ factor
   / primary
 
 primary
-  = "(" ws* t1:token ws+ op1:op_accepts_literal ws+ v1:value ws* ")" {
+  = ws* "(" ws* t1:token ws+ op1:op_accepts_literal ws+ v1:value ws* ")" ws* {
     return parseBinaryOp(t1,op1,v1,error);
   }
   / t1:token ws+ op1:op_accepts_literal ws+ v1:value {
     return parseBinaryOp(t1,op1,v1,error);
   }
-  / "(" ws* t1:token ws+ op1:op_accepts_variable ws+ t2:token ws* ")" {
+  / ws* "(" ws* t1:token ws+ op1:op_accepts_variable ws+ t2:token ws* ")" ws* {
     return { operator:op1, operands:[t1, t2]};
   }
   / t1:token ws+ op1:op_accepts_variable ws+ t2:token {
     return { operator:op1, operands:[t1, t2]};
   }
   / token1:token { return token1; }
-  / "(" token1:token ")" { return token1; }
+  / "(" ws* token1:token ws* ")" { return token1; }
   / "(" stmt1:boolean_stmt1 ")" { return stmt1; }
 
 op_boolean
diff --git a/test/specs/CC007-Condition-Syntax-Test.js b/test/specs/CC007-Condition-Syntax-Test.js
index 3ec54b5..df6e730 100644
--- a/test/specs/CC007-Condition-Syntax-Test.js
+++ b/test/specs/CC007-Condition-Syntax-Test.js
@@ -96,6 +96,62 @@ describe(`${testID} - ${plugin.plugin.name}`, function () {
       expression:
         'request.header.content-type = "application/json" AND request.verb = "GET"',
       expectError: false
+    },
+
+    {
+      expression:
+        'not(request.header.content-type = "application/json")AND(request.verb = "GET")',
+      expectError: false
+    },
+    {
+      expression: '(true)AND(request.verb = "GET")',
+      expectError: false
+    },
+    {
+      expression: '(true) AND(request.verb = "GET")',
+      expectError: false
+    },
+    {
+      expression: '( true ) AND( request.verb = "GET" )',
+      expectError: false
+    },
+    {
+      expression: '( true )AND( request.verb = "GET" )',
+      expectError: false
+    },
+    {
+      expression: '(true )AND(request.verb = "GET")',
+      expectError: false
+    },
+    {
+      expression: '(true ) AND(request.verb = "GET")',
+      expectError: false
+    },
+    {
+      expression: '(true ) AND( request.verb = "GET" )',
+      expectError: false
+    },
+
+    /* The following four cases differ only in spacing */
+    {
+      expression:
+        '((a MatchesPath "/b/d") or (b MatchesPath "/c/d/e")) and (request.verb = "GET")',
+      expectError: false
+    },
+    {
+      expression:
+        '((a MatchesPath "/b/d") or (b MatchesPath "/c/d/e"))and (request.verb = "GET")',
+      expectError: false
+    },
+    {
+      expression:
+        '((a MatchesPath "/b/d") or (b MatchesPath "/c/d/e") )and (request.verb = "GET") ',
+      expectError: false
+    },
+    {
+      expression:
+        '( ( a MatchesPath "/b/d") or (b MatchesPath "/c/d/e") )and (request.verb = "GET")',
+      expectError: false
     }
   ];
   cases.forEach((testcase, i) => {
diff --git a/test/specs/ConditionParserTest.js b/test/specs/ConditionParserTest.js
index 8146ec6..d90a2aa 100644
--- a/test/specs/ConditionParserTest.js
+++ b/test/specs/ConditionParserTest.js
@@ -336,7 +336,7 @@ describe("ConditionParser", function () {
         expect.fail();
       } catch (e) {
         expect(e.toString()).to.include("SyntaxError");
-        expect(e.toString()).to.include("Expected [ \\t\\n] or end");
+        expect(e.toString()).to.include("Expected ");
       }
       try {
         parser.parse(c1.slice(0, -1));
@@ -428,7 +428,7 @@ describe("ConditionParser", function () {
         expect.fail();
       } catch (e) {
         expect(e.toString()).to.include("SyntaxError");
-        expect(e.toString()).to.include('Expected ")"');
+        expect(e.toString()).to.include("Expected ");
       }
       try {
         const result = parser.parse(c1.replace('code"', 'code")'));

From 4f5d9747fc6e57601b6cdf6594b7cb96edf5ee21 Mon Sep 17 00:00:00 2001
From: Dino Chiesa <dchiesa@google.com>
Date: Tue, 27 Feb 2024 15:46:38 -0800
Subject: [PATCH 2/2] fix: handle no spaces surrounding symbolic operator forms

---
 build/ConditionParser.js          | 1257 ++++++++++++++++++++---------
 lib/peggy/Apigee-Condition.pegjs  |   67 +-
 test/specs/ConditionParserTest.js |   28 +
 3 files changed, 959 insertions(+), 393 deletions(-)

diff --git a/build/ConditionParser.js b/build/ConditionParser.js
index 1d1d29f..242d705 100644
--- a/build/ConditionParser.js
+++ b/build/ConditionParser.js
@@ -308,51 +308,81 @@ function peg$parse(input, options) {
   var peg$f4 = function(t1, op1, v1) {
     return parseBinaryOp(t1,op1,v1,error);
   };
-  var peg$f5 = function(t1, op1, t2) {
+  var peg$f5 = function(t1, op1, v1) {
+    return parseBinaryOp(t1,op1,v1,error);
+  };
+  var peg$f6 = function(t1, op1, v1) {
+    return parseBinaryOp(t1,op1,v1,error);
+  };
+  var peg$f7 = function(t1, op1, t2) {
+    return { operator:op1, operands:[t1, t2]};
+  };
+  var peg$f8 = function(t1, op1, t2) {
+    return { operator:op1, operands:[t1, t2]};
+  };
+  var peg$f9 = function(t1, op1, t2) {
     return { operator:op1, operands:[t1, t2]};
   };
-  var peg$f6 = function(t1, op1, t2) {
+  var peg$f10 = function(t1, op1, t2) {
     return { operator:op1, operands:[t1, t2]};
   };
-  var peg$f7 = function(token1) { return token1; };
-  var peg$f8 = function(token1) { return token1; };
-  var peg$f9 = function(stmt1) { return stmt1; };
-  var peg$f10 = function() {return "AND"; };
-  var peg$f11 = function() {return "OR"; };
-  var peg$f12 = function() { return "EqualsCaseInsensitive"; };
-  var peg$f13 = function() {return "StartsWith";};
-  var peg$f14 = function() { return "NotEquals"; };
-  var peg$f15 = function() { return "Equals"; };
-  var peg$f16 = function() {return "GreaterThanOrEquals";};
-  var peg$f17 = function() {return "GreaterThan";};
-  var peg$f18 = function() {return "LesserThanOrEquals";};
-  var peg$f19 = function() {return "LesserThan";};
-  var peg$f20 = function() {return "JavaRegex";};
-  var peg$f21 = function() {return "MatchesPath";};
-  var peg$f22 = function() {return "Matches";};
-  var peg$f23 = function() {return "StartsWith";};
-  var peg$f24 = function() { return "NotEquals"; };
-  var peg$f25 = function() { return "Equals"; };
-  var peg$f26 = function() {return "GreaterThanOrEquals";};
-  var peg$f27 = function() {return "GreaterThan";};
-  var peg$f28 = function() {return "LesserThanOrEquals";};
-  var peg$f29 = function() {return "LesserThan";};
-  var peg$f30 = function(int1) { return "." + chars.join(''); };
-  var peg$f31 = function() {
+  var peg$f11 = function(token1) { return token1; };
+  var peg$f12 = function(token1) { return token1; };
+  var peg$f13 = function(stmt1) { return stmt1; };
+  var peg$f14 = function() {return "AND"; };
+  var peg$f15 = function() {return "OR"; };
+  var peg$f16 = function() { return "EqualsCaseInsensitive"; };
+  var peg$f17 = function() {return "StartsWith";};
+  var peg$f18 = function() { return "NotEquals"; };
+  var peg$f19 = function() { return "Equals"; };
+  var peg$f20 = function() {return "GreaterThanOrEquals";};
+  var peg$f21 = function() {return "GreaterThan";};
+  var peg$f22 = function() {return "LesserThanOrEquals";};
+  var peg$f23 = function() {return "LesserThan";};
+  var peg$f24 = function() {return "JavaRegex";};
+  var peg$f25 = function() {return "MatchesPath";};
+  var peg$f26 = function() {return "Matches";};
+  var peg$f27 = function() { return "EqualsCaseInsensitive"; };
+  var peg$f28 = function() {return "StartsWith";};
+  var peg$f29 = function() { return "NotEquals"; };
+  var peg$f30 = function() { return "Equals"; };
+  var peg$f31 = function() {return "GreaterThanOrEquals";};
+  var peg$f32 = function() {return "GreaterThan";};
+  var peg$f33 = function() {return "LesserThanOrEquals";};
+  var peg$f34 = function() {return "LesserThan";};
+  var peg$f35 = function() {return "JavaRegex";};
+  var peg$f36 = function() {return "MatchesPath";};
+  var peg$f37 = function() {return "Matches";};
+  var peg$f38 = function() {return "StartsWith";};
+  var peg$f39 = function() { return "NotEquals"; };
+  var peg$f40 = function() { return "Equals"; };
+  var peg$f41 = function() {return "GreaterThanOrEquals";};
+  var peg$f42 = function() {return "GreaterThan";};
+  var peg$f43 = function() {return "LesserThanOrEquals";};
+  var peg$f44 = function() {return "LesserThan";};
+  var peg$f45 = function() {return "StartsWith";};
+  var peg$f46 = function() { return "NotEquals"; };
+  var peg$f47 = function() { return "Equals"; };
+  var peg$f48 = function() {return "GreaterThanOrEquals";};
+  var peg$f49 = function() {return "GreaterThan";};
+  var peg$f50 = function() {return "LesserThanOrEquals";};
+  var peg$f51 = function() {return "LesserThan";};
+  var peg$f52 = function(int1) { return "." + chars.join(''); };
+  var peg$f53 = function() {
       return { type: "Literal", value: parseFloat(text()) };
     };
-  var peg$f32 = function() {
+  var peg$f54 = function() {
       return { type: "Literal", value: parseFloat(text()) };
     };
-  var peg$f33 = function() {
+  var peg$f55 = function() {
       return { type: "Literal", value: parseInt(text()) };
     };
-  var peg$f34 = function() { return text(); };
-  var peg$f35 = function(value) { value.unshift("'"); value.push("'"); return value; };
-  var peg$f36 = function() { return null; };
-  var peg$f37 = function() { return false; };
-  var peg$f38 = function() { return true; };
-  var peg$f39 = function(value) { return value.value; };
+  var peg$f56 = function() { return text(); };
+  var peg$f57 = function(value) { value.unshift("'"); value.push("'"); return value; };
+  var peg$f58 = function() { return null; };
+  var peg$f59 = function() { return false; };
+  var peg$f60 = function() { return true; };
+  var peg$f61 = function(value) { return value.value; };
   var peg$currPos = 0;
   var peg$savedPos = 0;
   var peg$posDetailsCache = [{ line: 1, column: 1 }];
@@ -760,36 +790,66 @@ function peg$parse(input, options) {
     }
     if (s0 === peg$FAILED) {
       s0 = peg$currPos;
-      s1 = peg$parsetoken();
-      if (s1 !== peg$FAILED) {
-        s2 = [];
-        s3 = peg$parsews();
-        if (s3 !== peg$FAILED) {
-          while (s3 !== peg$FAILED) {
-            s2.push(s3);
-            s3 = peg$parsews();
-          }
-        } else {
-          s2 = peg$FAILED;
+      s1 = [];
+      s2 = peg$parsews();
+      while (s2 !== peg$FAILED) {
+        s1.push(s2);
+        s2 = peg$parsews();
+      }
+      if (input.charCodeAt(peg$currPos) === 40) {
+        s2 = peg$c0;
+        peg$currPos++;
+      } else {
+        s2 = peg$FAILED;
+        if (peg$silentFails === 0) { peg$fail(peg$e0); }
+      }
+      if (s2 !== peg$FAILED) {
+        s3 = [];
+        s4 = peg$parsews();
+        while (s4 !== peg$FAILED) {
+          s3.push(s4);
+          s4 = peg$parsews();
         }
-        if (s2 !== peg$FAILED) {
-          s3 = peg$parseop_accepts_literal();
-          if (s3 !== peg$FAILED) {
-            s4 = [];
-            s5 = peg$parsews();
-            if (s5 !== peg$FAILED) {
-              while (s5 !== peg$FAILED) {
-                s4.push(s5);
-                s5 = peg$parsews();
-              }
-            } else {
-              s4 = peg$FAILED;
+        s4 = peg$parsetoken();
+        if (s4 !== peg$FAILED) {
+          s5 = [];
+          s6 = peg$parsews();
+          while (s6 !== peg$FAILED) {
+            s5.push(s6);
+            s6 = peg$parsews();
+          }
+          s6 = peg$parseop_accepts_literal_sym();
+          if (s6 !== peg$FAILED) {
+            s7 = [];
+            s8 = peg$parsews();
+            while (s8 !== peg$FAILED) {
+              s7.push(s8);
+              s8 = peg$parsews();
             }
-            if (s4 !== peg$FAILED) {
-              s5 = peg$parsevalue();
-              if (s5 !== peg$FAILED) {
+            s8 = peg$parsevalue();
+            if (s8 !== peg$FAILED) {
+              s9 = [];
+              s10 = peg$parsews();
+              while (s10 !== peg$FAILED) {
+                s9.push(s10);
+                s10 = peg$parsews();
+              }
+              if (input.charCodeAt(peg$currPos) === 41) {
+                s10 = peg$c1;
+                peg$currPos++;
+              } else {
+                s10 = peg$FAILED;
+                if (peg$silentFails === 0) { peg$fail(peg$e1); }
+              }
+              if (s10 !== peg$FAILED) {
+                s11 = [];
+                s12 = peg$parsews();
+                while (s12 !== peg$FAILED) {
+                  s11.push(s12);
+                  s12 = peg$parsews();
+                }
                 peg$savedPos = s0;
-                s0 = peg$f4(s1, s3, s5);
+                s0 = peg$f4(s4, s6, s8);
               } else {
                 peg$currPos = s0;
                 s0 = peg$FAILED;
@@ -812,84 +872,36 @@ function peg$parse(input, options) {
       }
       if (s0 === peg$FAILED) {
         s0 = peg$currPos;
-        s1 = [];
-        s2 = peg$parsews();
-        while (s2 !== peg$FAILED) {
-          s1.push(s2);
-          s2 = peg$parsews();
-        }
-        if (input.charCodeAt(peg$currPos) === 40) {
-          s2 = peg$c0;
-          peg$currPos++;
-        } else {
-          s2 = peg$FAILED;
-          if (peg$silentFails === 0) { peg$fail(peg$e0); }
-        }
-        if (s2 !== peg$FAILED) {
-          s3 = [];
-          s4 = peg$parsews();
-          while (s4 !== peg$FAILED) {
-            s3.push(s4);
-            s4 = peg$parsews();
-          }
-          s4 = peg$parsetoken();
-          if (s4 !== peg$FAILED) {
-            s5 = [];
-            s6 = peg$parsews();
-            if (s6 !== peg$FAILED) {
-              while (s6 !== peg$FAILED) {
-                s5.push(s6);
-                s6 = peg$parsews();
-              }
-            } else {
-              s5 = peg$FAILED;
+        s1 = peg$parsetoken();
+        if (s1 !== peg$FAILED) {
+          s2 = [];
+          s3 = peg$parsews();
+          if (s3 !== peg$FAILED) {
+            while (s3 !== peg$FAILED) {
+              s2.push(s3);
+              s3 = peg$parsews();
             }
-            if (s5 !== peg$FAILED) {
-              s6 = peg$parseop_accepts_variable();
-              if (s6 !== peg$FAILED) {
-                s7 = [];
-                s8 = peg$parsews();
-                if (s8 !== peg$FAILED) {
-                  while (s8 !== peg$FAILED) {
-                    s7.push(s8);
-                    s8 = peg$parsews();
-                  }
-                } else {
-                  s7 = peg$FAILED;
+          } else {
+            s2 = peg$FAILED;
+          }
+          if (s2 !== peg$FAILED) {
+            s3 = peg$parseop_accepts_literal();
+            if (s3 !== peg$FAILED) {
+              s4 = [];
+              s5 = peg$parsews();
+              if (s5 !== peg$FAILED) {
+                while (s5 !== peg$FAILED) {
+                  s4.push(s5);
+                  s5 = peg$parsews();
                 }
-                if (s7 !== peg$FAILED) {
-                  s8 = peg$parsetoken();
-                  if (s8 !== peg$FAILED) {
-                    s9 = [];
-                    s10 = peg$parsews();
-                    while (s10 !== peg$FAILED) {
-                      s9.push(s10);
-                      s10 = peg$parsews();
-                    }
-                    if (input.charCodeAt(peg$currPos) === 41) {
-                      s10 = peg$c1;
-                      peg$currPos++;
-                    } else {
-                      s10 = peg$FAILED;
-                      if (peg$silentFails === 0) { peg$fail(peg$e1); }
-                    }
-                    if (s10 !== peg$FAILED) {
-                      s11 = [];
-                      s12 = peg$parsews();
-                      while (s12 !== peg$FAILED) {
-                        s11.push(s12);
-                        s12 = peg$parsews();
-                      }
-                      peg$savedPos = s0;
-                      s0 = peg$f5(s4, s6, s8);
-                    } else {
-                      peg$currPos = s0;
-                      s0 = peg$FAILED;
-                    }
-                  } else {
-                    peg$currPos = s0;
-                    s0 = peg$FAILED;
-                  }
+              } else {
+                s4 = peg$FAILED;
+              }
+              if (s4 !== peg$FAILED) {
+                s5 = peg$parsevalue();
+                if (s5 !== peg$FAILED) {
+                  peg$savedPos = s0;
+                  s0 = peg$f5(s1, s3, s5);
                 } else {
                   peg$currPos = s0;
                   s0 = peg$FAILED;
@@ -916,32 +928,118 @@ function peg$parse(input, options) {
           if (s1 !== peg$FAILED) {
             s2 = [];
             s3 = peg$parsews();
+            while (s3 !== peg$FAILED) {
+              s2.push(s3);
+              s3 = peg$parsews();
+            }
+            s3 = peg$parseop_accepts_literal_sym();
             if (s3 !== peg$FAILED) {
-              while (s3 !== peg$FAILED) {
-                s2.push(s3);
-                s3 = peg$parsews();
+              s4 = [];
+              s5 = peg$parsews();
+              while (s5 !== peg$FAILED) {
+                s4.push(s5);
+                s5 = peg$parsews();
+              }
+              s5 = peg$parsevalue();
+              if (s5 !== peg$FAILED) {
+                peg$savedPos = s0;
+                s0 = peg$f6(s1, s3, s5);
+              } else {
+                peg$currPos = s0;
+                s0 = peg$FAILED;
               }
+            } else {
+              peg$currPos = s0;
+              s0 = peg$FAILED;
+            }
+          } else {
+            peg$currPos = s0;
+            s0 = peg$FAILED;
+          }
+          if (s0 === peg$FAILED) {
+            s0 = peg$currPos;
+            s1 = [];
+            s2 = peg$parsews();
+            while (s2 !== peg$FAILED) {
+              s1.push(s2);
+              s2 = peg$parsews();
+            }
+            if (input.charCodeAt(peg$currPos) === 40) {
+              s2 = peg$c0;
+              peg$currPos++;
             } else {
               s2 = peg$FAILED;
+              if (peg$silentFails === 0) { peg$fail(peg$e0); }
             }
             if (s2 !== peg$FAILED) {
-              s3 = peg$parseop_accepts_variable();
-              if (s3 !== peg$FAILED) {
-                s4 = [];
-                s5 = peg$parsews();
-                if (s5 !== peg$FAILED) {
-                  while (s5 !== peg$FAILED) {
-                    s4.push(s5);
-                    s5 = peg$parsews();
+              s3 = [];
+              s4 = peg$parsews();
+              while (s4 !== peg$FAILED) {
+                s3.push(s4);
+                s4 = peg$parsews();
+              }
+              s4 = peg$parsetoken();
+              if (s4 !== peg$FAILED) {
+                s5 = [];
+                s6 = peg$parsews();
+                if (s6 !== peg$FAILED) {
+                  while (s6 !== peg$FAILED) {
+                    s5.push(s6);
+                    s6 = peg$parsews();
                   }
                 } else {
-                  s4 = peg$FAILED;
+                  s5 = peg$FAILED;
                 }
-                if (s4 !== peg$FAILED) {
-                  s5 = peg$parsetoken();
-                  if (s5 !== peg$FAILED) {
-                    peg$savedPos = s0;
-                    s0 = peg$f6(s1, s3, s5);
+                if (s5 !== peg$FAILED) {
+                  s6 = peg$parseop_accepts_variable();
+                  if (s6 !== peg$FAILED) {
+                    s7 = [];
+                    s8 = peg$parsews();
+                    if (s8 !== peg$FAILED) {
+                      while (s8 !== peg$FAILED) {
+                        s7.push(s8);
+                        s8 = peg$parsews();
+                      }
+                    } else {
+                      s7 = peg$FAILED;
+                    }
+                    if (s7 !== peg$FAILED) {
+                      s8 = peg$parsetoken();
+                      if (s8 !== peg$FAILED) {
+                        s9 = [];
+                        s10 = peg$parsews();
+                        while (s10 !== peg$FAILED) {
+                          s9.push(s10);
+                          s10 = peg$parsews();
+                        }
+                        if (input.charCodeAt(peg$currPos) === 41) {
+                          s10 = peg$c1;
+                          peg$currPos++;
+                        } else {
+                          s10 = peg$FAILED;
+                          if (peg$silentFails === 0) { peg$fail(peg$e1); }
+                        }
+                        if (s10 !== peg$FAILED) {
+                          s11 = [];
+                          s12 = peg$parsews();
+                          while (s12 !== peg$FAILED) {
+                            s11.push(s12);
+                            s12 = peg$parsews();
+                          }
+                          peg$savedPos = s0;
+                          s0 = peg$f7(s4, s6, s8);
+                        } else {
+                          peg$currPos = s0;
+                          s0 = peg$FAILED;
+                        }
+                      } else {
+                        peg$currPos = s0;
+                        s0 = peg$FAILED;
+                      }
+                    } else {
+                      peg$currPos = s0;
+                      s0 = peg$FAILED;
+                    }
                   } else {
                     peg$currPos = s0;
                     s0 = peg$FAILED;
@@ -958,52 +1056,76 @@ function peg$parse(input, options) {
               peg$currPos = s0;
               s0 = peg$FAILED;
             }
-          } else {
-            peg$currPos = s0;
-            s0 = peg$FAILED;
-          }
-          if (s0 === peg$FAILED) {
-            s0 = peg$currPos;
-            s1 = peg$parsetoken();
-            if (s1 !== peg$FAILED) {
-              peg$savedPos = s0;
-              s1 = peg$f7(s1);
-            }
-            s0 = s1;
             if (s0 === peg$FAILED) {
               s0 = peg$currPos;
+              s1 = [];
+              s2 = peg$parsews();
+              while (s2 !== peg$FAILED) {
+                s1.push(s2);
+                s2 = peg$parsews();
+              }
               if (input.charCodeAt(peg$currPos) === 40) {
-                s1 = peg$c0;
+                s2 = peg$c0;
                 peg$currPos++;
               } else {
-                s1 = peg$FAILED;
+                s2 = peg$FAILED;
                 if (peg$silentFails === 0) { peg$fail(peg$e0); }
               }
-              if (s1 !== peg$FAILED) {
-                s2 = [];
-                s3 = peg$parsews();
-                while (s3 !== peg$FAILED) {
-                  s2.push(s3);
-                  s3 = peg$parsews();
+              if (s2 !== peg$FAILED) {
+                s3 = [];
+                s4 = peg$parsews();
+                while (s4 !== peg$FAILED) {
+                  s3.push(s4);
+                  s4 = peg$parsews();
                 }
-                s3 = peg$parsetoken();
-                if (s3 !== peg$FAILED) {
-                  s4 = [];
-                  s5 = peg$parsews();
-                  while (s5 !== peg$FAILED) {
-                    s4.push(s5);
-                    s5 = peg$parsews();
-                  }
-                  if (input.charCodeAt(peg$currPos) === 41) {
-                    s5 = peg$c1;
-                    peg$currPos++;
-                  } else {
-                    s5 = peg$FAILED;
-                    if (peg$silentFails === 0) { peg$fail(peg$e1); }
+                s4 = peg$parsetoken();
+                if (s4 !== peg$FAILED) {
+                  s5 = [];
+                  s6 = peg$parsews();
+                  while (s6 !== peg$FAILED) {
+                    s5.push(s6);
+                    s6 = peg$parsews();
                   }
-                  if (s5 !== peg$FAILED) {
-                    peg$savedPos = s0;
-                    s0 = peg$f8(s3);
+                  s6 = peg$parseop_accepts_variable_sym();
+                  if (s6 !== peg$FAILED) {
+                    s7 = [];
+                    s8 = peg$parsews();
+                    while (s8 !== peg$FAILED) {
+                      s7.push(s8);
+                      s8 = peg$parsews();
+                    }
+                    s8 = peg$parsetoken();
+                    if (s8 !== peg$FAILED) {
+                      s9 = [];
+                      s10 = peg$parsews();
+                      while (s10 !== peg$FAILED) {
+                        s9.push(s10);
+                        s10 = peg$parsews();
+                      }
+                      if (input.charCodeAt(peg$currPos) === 41) {
+                        s10 = peg$c1;
+                        peg$currPos++;
+                      } else {
+                        s10 = peg$FAILED;
+                        if (peg$silentFails === 0) { peg$fail(peg$e1); }
+                      }
+                      if (s10 !== peg$FAILED) {
+                        s11 = [];
+                        s12 = peg$parsews();
+                        while (s12 !== peg$FAILED) {
+                          s11.push(s12);
+                          s12 = peg$parsews();
+                        }
+                        peg$savedPos = s0;
+                        s0 = peg$f8(s4, s6, s8);
+                      } else {
+                        peg$currPos = s0;
+                        s0 = peg$FAILED;
+                      }
+                    } else {
+                      peg$currPos = s0;
+                      s0 = peg$FAILED;
+                    }
                   } else {
                     peg$currPos = s0;
                     s0 = peg$FAILED;
@@ -1018,26 +1140,44 @@ function peg$parse(input, options) {
               }
               if (s0 === peg$FAILED) {
                 s0 = peg$currPos;
-                if (input.charCodeAt(peg$currPos) === 40) {
-                  s1 = peg$c0;
-                  peg$currPos++;
-                } else {
-                  s1 = peg$FAILED;
-                  if (peg$silentFails === 0) { peg$fail(peg$e0); }
-                }
+                s1 = peg$parsetoken();
                 if (s1 !== peg$FAILED) {
-                  s2 = peg$parseboolean_stmt1();
-                  if (s2 !== peg$FAILED) {
-                    if (input.charCodeAt(peg$currPos) === 41) {
-                      s3 = peg$c1;
-                      peg$currPos++;
-                    } else {
-                      s3 = peg$FAILED;
-                      if (peg$silentFails === 0) { peg$fail(peg$e1); }
+                  s2 = [];
+                  s3 = peg$parsews();
+                  if (s3 !== peg$FAILED) {
+                    while (s3 !== peg$FAILED) {
+                      s2.push(s3);
+                      s3 = peg$parsews();
                     }
+                  } else {
+                    s2 = peg$FAILED;
+                  }
+                  if (s2 !== peg$FAILED) {
+                    s3 = peg$parseop_accepts_variable();
                     if (s3 !== peg$FAILED) {
-                      peg$savedPos = s0;
-                      s0 = peg$f9(s2);
+                      s4 = [];
+                      s5 = peg$parsews();
+                      if (s5 !== peg$FAILED) {
+                        while (s5 !== peg$FAILED) {
+                          s4.push(s5);
+                          s5 = peg$parsews();
+                        }
+                      } else {
+                        s4 = peg$FAILED;
+                      }
+                      if (s4 !== peg$FAILED) {
+                        s5 = peg$parsetoken();
+                        if (s5 !== peg$FAILED) {
+                          peg$savedPos = s0;
+                          s0 = peg$f9(s1, s3, s5);
+                        } else {
+                          peg$currPos = s0;
+                          s0 = peg$FAILED;
+                        }
+                      } else {
+                        peg$currPos = s0;
+                        s0 = peg$FAILED;
+                      }
                     } else {
                       peg$currPos = s0;
                       s0 = peg$FAILED;
@@ -1050,6 +1190,132 @@ function peg$parse(input, options) {
                   peg$currPos = s0;
                   s0 = peg$FAILED;
                 }
+                if (s0 === peg$FAILED) {
+                  s0 = peg$currPos;
+                  s1 = peg$parsetoken();
+                  if (s1 !== peg$FAILED) {
+                    s2 = [];
+                    s3 = peg$parsews();
+                    while (s3 !== peg$FAILED) {
+                      s2.push(s3);
+                      s3 = peg$parsews();
+                    }
+                    s3 = peg$parseop_accepts_variable_sym();
+                    if (s3 !== peg$FAILED) {
+                      s4 = [];
+                      s5 = peg$parsews();
+                      while (s5 !== peg$FAILED) {
+                        s4.push(s5);
+                        s5 = peg$parsews();
+                      }
+                      s5 = peg$parsetoken();
+                      if (s5 !== peg$FAILED) {
+                        peg$savedPos = s0;
+                        s0 = peg$f10(s1, s3, s5);
+                      } else {
+                        peg$currPos = s0;
+                        s0 = peg$FAILED;
+                      }
+                    } else {
+                      peg$currPos = s0;
+                      s0 = peg$FAILED;
+                    }
+                  } else {
+                    peg$currPos = s0;
+                    s0 = peg$FAILED;
+                  }
+                  if (s0 === peg$FAILED) {
+                    s0 = peg$currPos;
+                    s1 = peg$parsetoken();
+                    if (s1 !== peg$FAILED) {
+                      peg$savedPos = s0;
+                      s1 = peg$f11(s1);
+                    }
+                    s0 = s1;
+                    if (s0 === peg$FAILED) {
+                      s0 = peg$currPos;
+                      if (input.charCodeAt(peg$currPos) === 40) {
+                        s1 = peg$c0;
+                        peg$currPos++;
+                      } else {
+                        s1 = peg$FAILED;
+                        if (peg$silentFails === 0) { peg$fail(peg$e0); }
+                      }
+                      if (s1 !== peg$FAILED) {
+                        s2 = [];
+                        s3 = peg$parsews();
+                        while (s3 !== peg$FAILED) {
+                          s2.push(s3);
+                          s3 = peg$parsews();
+                        }
+                        s3 = peg$parsetoken();
+                        if (s3 !== peg$FAILED) {
+                          s4 = [];
+                          s5 = peg$parsews();
+                          while (s5 !== peg$FAILED) {
+                            s4.push(s5);
+                            s5 = peg$parsews();
+                          }
+                          if (input.charCodeAt(peg$currPos) === 41) {
+                            s5 = peg$c1;
+                            peg$currPos++;
+                          } else {
+                            s5 = peg$FAILED;
+                            if (peg$silentFails === 0) { peg$fail(peg$e1); }
+                          }
+                          if (s5 !== peg$FAILED) {
+                            peg$savedPos = s0;
+                            s0 = peg$f12(s3);
+                          } else {
+                            peg$currPos = s0;
+                            s0 = peg$FAILED;
+                          }
+                        } else {
+                          peg$currPos = s0;
+                          s0 = peg$FAILED;
+                        }
+                      } else {
+                        peg$currPos = s0;
+                        s0 = peg$FAILED;
+                      }
+                      if (s0 === peg$FAILED) {
+                        s0 = peg$currPos;
+                        if (input.charCodeAt(peg$currPos) === 40) {
+                          s1 = peg$c0;
+                          peg$currPos++;
+                        } else {
+                          s1 = peg$FAILED;
+                          if (peg$silentFails === 0) { peg$fail(peg$e0); }
+                        }
+                        if (s1 !== peg$FAILED) {
+                          s2 = peg$parseboolean_stmt1();
+                          if (s2 !== peg$FAILED) {
+                            if (input.charCodeAt(peg$currPos) === 41) {
+                              s3 = peg$c1;
+                              peg$currPos++;
+                            } else {
+                              s3 = peg$FAILED;
+                              if (peg$silentFails === 0) { peg$fail(peg$e1); }
+                            }
+                            if (s3 !== peg$FAILED) {
+                              peg$savedPos = s0;
+                              s0 = peg$f13(s2);
+                            } else {
+                              peg$currPos = s0;
+                              s0 = peg$FAILED;
+                            }
+                          } else {
+                            peg$currPos = s0;
+                            s0 = peg$FAILED;
+                          }
+                        } else {
+                          peg$currPos = s0;
+                          s0 = peg$FAILED;
+                        }
+                      }
+                    }
+                  }
+                }
               }
             }
           }
@@ -1067,7 +1333,7 @@ function peg$parse(input, options) {
     s1 = peg$parseop_and();
     if (s1 !== peg$FAILED) {
       peg$savedPos = s0;
-      s1 = peg$f10();
+      s1 = peg$f14();
     }
     s0 = s1;
     if (s0 === peg$FAILED) {
@@ -1075,7 +1341,7 @@ function peg$parse(input, options) {
       s1 = peg$parseop_or();
       if (s1 !== peg$FAILED) {
         peg$savedPos = s0;
-        s1 = peg$f11();
+        s1 = peg$f15();
       }
       s0 = s1;
     }
@@ -1152,94 +1418,94 @@ function peg$parse(input, options) {
     return s0;
   }
 
-  function peg$parseop_accepts_literal() {
+  function peg$parseop_accepts_literal_sym() {
     var s0, s1;
 
     s0 = peg$currPos;
-    s1 = peg$parseop_equalsnocase();
+    s1 = peg$parseop_equalsnocase_sym();
     if (s1 !== peg$FAILED) {
       peg$savedPos = s0;
-      s1 = peg$f12();
+      s1 = peg$f16();
     }
     s0 = s1;
     if (s0 === peg$FAILED) {
       s0 = peg$currPos;
-      s1 = peg$parseop_startswith();
+      s1 = peg$parseop_startswith_sym();
       if (s1 !== peg$FAILED) {
         peg$savedPos = s0;
-        s1 = peg$f13();
+        s1 = peg$f17();
       }
       s0 = s1;
       if (s0 === peg$FAILED) {
         s0 = peg$currPos;
-        s1 = peg$parseop_notequals();
+        s1 = peg$parseop_notequals_sym();
         if (s1 !== peg$FAILED) {
           peg$savedPos = s0;
-          s1 = peg$f14();
+          s1 = peg$f18();
         }
         s0 = s1;
         if (s0 === peg$FAILED) {
           s0 = peg$currPos;
-          s1 = peg$parseop_equals();
+          s1 = peg$parseop_equals_sym();
           if (s1 !== peg$FAILED) {
             peg$savedPos = s0;
-            s1 = peg$f15();
+            s1 = peg$f19();
           }
           s0 = s1;
           if (s0 === peg$FAILED) {
             s0 = peg$currPos;
-            s1 = peg$parseop_greatereq();
+            s1 = peg$parseop_greatereq_sym();
             if (s1 !== peg$FAILED) {
               peg$savedPos = s0;
-              s1 = peg$f16();
+              s1 = peg$f20();
             }
             s0 = s1;
             if (s0 === peg$FAILED) {
               s0 = peg$currPos;
-              s1 = peg$parseop_greater();
+              s1 = peg$parseop_greater_sym();
               if (s1 !== peg$FAILED) {
                 peg$savedPos = s0;
-                s1 = peg$f17();
+                s1 = peg$f21();
               }
               s0 = s1;
               if (s0 === peg$FAILED) {
                 s0 = peg$currPos;
-                s1 = peg$parseop_lessereq();
+                s1 = peg$parseop_lessereq_sym();
                 if (s1 !== peg$FAILED) {
                   peg$savedPos = s0;
-                  s1 = peg$f18();
+                  s1 = peg$f22();
                 }
                 s0 = s1;
                 if (s0 === peg$FAILED) {
                   s0 = peg$currPos;
-                  s1 = peg$parseop_lesser();
+                  s1 = peg$parseop_lesser_sym();
                   if (s1 !== peg$FAILED) {
                     peg$savedPos = s0;
-                    s1 = peg$f19();
+                    s1 = peg$f23();
                   }
                   s0 = s1;
                   if (s0 === peg$FAILED) {
                     s0 = peg$currPos;
-                    s1 = peg$parseop_regexmatch();
+                    s1 = peg$parseop_regexmatch_sym();
                     if (s1 !== peg$FAILED) {
                       peg$savedPos = s0;
-                      s1 = peg$f20();
+                      s1 = peg$f24();
                     }
                     s0 = s1;
                     if (s0 === peg$FAILED) {
                       s0 = peg$currPos;
-                      s1 = peg$parseop_matchespath();
+                      s1 = peg$parseop_matchespath_sym();
                       if (s1 !== peg$FAILED) {
                         peg$savedPos = s0;
-                        s1 = peg$f21();
+                        s1 = peg$f25();
                       }
                       s0 = s1;
                       if (s0 === peg$FAILED) {
                         s0 = peg$currPos;
-                        s1 = peg$parseop_matches();
+                        s1 = peg$parseop_matches_sym();
                         if (s1 !== peg$FAILED) {
                           peg$savedPos = s0;
-                          s1 = peg$f22();
+                          s1 = peg$f26();
                         }
                         s0 = s1;
                       }
@@ -1256,64 +1522,100 @@ function peg$parse(input, options) {
     return s0;
   }
 
-  function peg$parseop_accepts_variable() {
+  function peg$parseop_accepts_literal() {
     var s0, s1;
 
     s0 = peg$currPos;
-    s1 = peg$parseop_startswith();
+    s1 = peg$parseop_equalsnocase();
     if (s1 !== peg$FAILED) {
       peg$savedPos = s0;
-      s1 = peg$f23();
+      s1 = peg$f27();
     }
     s0 = s1;
     if (s0 === peg$FAILED) {
       s0 = peg$currPos;
-      s1 = peg$parseop_notequals();
+      s1 = peg$parseop_startswith();
       if (s1 !== peg$FAILED) {
         peg$savedPos = s0;
-        s1 = peg$f24();
+        s1 = peg$f28();
       }
       s0 = s1;
       if (s0 === peg$FAILED) {
         s0 = peg$currPos;
-        s1 = peg$parseop_equals();
+        s1 = peg$parseop_notequals();
         if (s1 !== peg$FAILED) {
           peg$savedPos = s0;
-          s1 = peg$f25();
+          s1 = peg$f29();
         }
         s0 = s1;
         if (s0 === peg$FAILED) {
           s0 = peg$currPos;
-          s1 = peg$parseop_greatereq();
+          s1 = peg$parseop_equals();
           if (s1 !== peg$FAILED) {
             peg$savedPos = s0;
-            s1 = peg$f26();
+            s1 = peg$f30();
           }
           s0 = s1;
           if (s0 === peg$FAILED) {
             s0 = peg$currPos;
-            s1 = peg$parseop_greater();
+            s1 = peg$parseop_greatereq();
             if (s1 !== peg$FAILED) {
               peg$savedPos = s0;
-              s1 = peg$f27();
+              s1 = peg$f31();
             }
             s0 = s1;
             if (s0 === peg$FAILED) {
               s0 = peg$currPos;
-              s1 = peg$parseop_lessereq();
+              s1 = peg$parseop_greater();
               if (s1 !== peg$FAILED) {
                 peg$savedPos = s0;
-                s1 = peg$f28();
+                s1 = peg$f32();
               }
               s0 = s1;
               if (s0 === peg$FAILED) {
                 s0 = peg$currPos;
-                s1 = peg$parseop_lesser();
+                s1 = peg$parseop_lessereq();
                 if (s1 !== peg$FAILED) {
                   peg$savedPos = s0;
-                  s1 = peg$f29();
+                  s1 = peg$f33();
                 }
                 s0 = s1;
+                if (s0 === peg$FAILED) {
+                  s0 = peg$currPos;
+                  s1 = peg$parseop_lesser();
+                  if (s1 !== peg$FAILED) {
+                    peg$savedPos = s0;
+                    s1 = peg$f34();
+                  }
+                  s0 = s1;
+                  if (s0 === peg$FAILED) {
+                    s0 = peg$currPos;
+                    s1 = peg$parseop_regexmatch();
+                    if (s1 !== peg$FAILED) {
+                      peg$savedPos = s0;
+                      s1 = peg$f35();
+                    }
+                    s0 = s1;
+                    if (s0 === peg$FAILED) {
+                      s0 = peg$currPos;
+                      s1 = peg$parseop_matchespath();
+                      if (s1 !== peg$FAILED) {
+                        peg$savedPos = s0;
+                        s1 = peg$f36();
+                      }
+                      s0 = s1;
+                      if (s0 === peg$FAILED) {
+                        s0 = peg$currPos;
+                        s1 = peg$parseop_matches();
+                        if (s1 !== peg$FAILED) {
+                          peg$savedPos = s0;
+                          s1 = peg$f37();
+                        }
+                        s0 = s1;
+                      }
+                    }
+                  }
+                }
               }
             }
           }
@@ -1324,7 +1626,143 @@ function peg$parse(input, options) {
     return s0;
   }
 
-  function peg$parseop_equals() {
+  function peg$parseop_accepts_variable_sym() {
+    var s0, s1;
+
+    s0 = peg$currPos;
+    s1 = peg$parseop_startswith_sym();
+    if (s1 !== peg$FAILED) {
+      peg$savedPos = s0;
+      s1 = peg$f38();
+    }
+    s0 = s1;
+    if (s0 === peg$FAILED) {
+      s0 = peg$currPos;
+      s1 = peg$parseop_notequals_sym();
+      if (s1 !== peg$FAILED) {
+        peg$savedPos = s0;
+        s1 = peg$f39();
+      }
+      s0 = s1;
+      if (s0 === peg$FAILED) {
+        s0 = peg$currPos;
+        s1 = peg$parseop_equals_sym();
+        if (s1 !== peg$FAILED) {
+          peg$savedPos = s0;
+          s1 = peg$f40();
+        }
+        s0 = s1;
+        if (s0 === peg$FAILED) {
+          s0 = peg$currPos;
+          s1 = peg$parseop_greatereq_sym();
+          if (s1 !== peg$FAILED) {
+            peg$savedPos = s0;
+            s1 = peg$f41();
+          }
+          s0 = s1;
+          if (s0 === peg$FAILED) {
+            s0 = peg$currPos;
+            s1 = peg$parseop_greater_sym();
+            if (s1 !== peg$FAILED) {
+              peg$savedPos = s0;
+              s1 = peg$f42();
+            }
+            s0 = s1;
+            if (s0 === peg$FAILED) {
+              s0 = peg$currPos;
+              s1 = peg$parseop_lessereq_sym();
+              if (s1 !== peg$FAILED) {
+                peg$savedPos = s0;
+                s1 = peg$f43();
+              }
+              s0 = s1;
+              if (s0 === peg$FAILED) {
+                s0 = peg$currPos;
+                s1 = peg$parseop_lesser_sym();
+                if (s1 !== peg$FAILED) {
+                  peg$savedPos = s0;
+                  s1 = peg$f44();
+                }
+                s0 = s1;
+              }
+            }
+          }
+        }
+      }
+    }
+
+    return s0;
+  }
+
+  function peg$parseop_accepts_variable() {
+    var s0, s1;
+
+    s0 = peg$currPos;
+    s1 = peg$parseop_startswith();
+    if (s1 !== peg$FAILED) {
+      peg$savedPos = s0;
+      s1 = peg$f45();
+    }
+    s0 = s1;
+    if (s0 === peg$FAILED) {
+      s0 = peg$currPos;
+      s1 = peg$parseop_notequals();
+      if (s1 !== peg$FAILED) {
+        peg$savedPos = s0;
+        s1 = peg$f46();
+      }
+      s0 = s1;
+      if (s0 === peg$FAILED) {
+        s0 = peg$currPos;
+        s1 = peg$parseop_equals();
+        if (s1 !== peg$FAILED) {
+          peg$savedPos = s0;
+          s1 = peg$f47();
+        }
+        s0 = s1;
+        if (s0 === peg$FAILED) {
+          s0 = peg$currPos;
+          s1 = peg$parseop_greatereq();
+          if (s1 !== peg$FAILED) {
+            peg$savedPos = s0;
+            s1 = peg$f48();
+          }
+          s0 = s1;
+          if (s0 === peg$FAILED) {
+            s0 = peg$currPos;
+            s1 = peg$parseop_greater();
+            if (s1 !== peg$FAILED) {
+              peg$savedPos = s0;
+              s1 = peg$f49();
+            }
+            s0 = s1;
+            if (s0 === peg$FAILED) {
+              s0 = peg$currPos;
+              s1 = peg$parseop_lessereq();
+              if (s1 !== peg$FAILED) {
+                peg$savedPos = s0;
+                s1 = peg$f50();
+              }
+              s0 = s1;
+              if (s0 === peg$FAILED) {
+                s0 = peg$currPos;
+                s1 = peg$parseop_lesser();
+                if (s1 !== peg$FAILED) {
+                  peg$savedPos = s0;
+                  s1 = peg$f51();
+                }
+                s0 = s1;
+              }
+            }
+          }
+        }
+      }
+    }
+
+    return s0;
+  }
+
+  function peg$parseop_equals_sym() {
     var s0;
 
     if (input.substr(peg$currPos, 2) === peg$c8) {
@@ -1342,30 +1780,35 @@ function peg$parse(input, options) {
         s0 = peg$FAILED;
         if (peg$silentFails === 0) { peg$fail(peg$e9); }
       }
-      if (s0 === peg$FAILED) {
-        if (input.substr(peg$currPos, 6).toLowerCase() === peg$c10) {
-          s0 = input.substr(peg$currPos, 6);
-          peg$currPos += 6;
-        } else {
-          s0 = peg$FAILED;
-          if (peg$silentFails === 0) { peg$fail(peg$e10); }
-        }
-        if (s0 === peg$FAILED) {
-          if (input.substr(peg$currPos, 2).toLowerCase() === peg$c11) {
-            s0 = input.substr(peg$currPos, 2);
-            peg$currPos += 2;
-          } else {
-            s0 = peg$FAILED;
-            if (peg$silentFails === 0) { peg$fail(peg$e11); }
-          }
-        }
+    }
+
+    return s0;
+  }
+
+  function peg$parseop_equals() {
+    var s0;
+
+    if (input.substr(peg$currPos, 6).toLowerCase() === peg$c10) {
+      s0 = input.substr(peg$currPos, 6);
+      peg$currPos += 6;
+    } else {
+      s0 = peg$FAILED;
+      if (peg$silentFails === 0) { peg$fail(peg$e10); }
+    }
+    if (s0 === peg$FAILED) {
+      if (input.substr(peg$currPos, 2).toLowerCase() === peg$c11) {
+        s0 = input.substr(peg$currPos, 2);
+        peg$currPos += 2;
+      } else {
+        s0 = peg$FAILED;
+        if (peg$silentFails === 0) { peg$fail(peg$e11); }
       }
     }
 
     return s0;
   }
 
-  function peg$parseop_notequals() {
+  function peg$parseop_notequals_sym() {
     var s0;
 
     if (input.substr(peg$currPos, 2) === peg$c12) {
@@ -1375,29 +1818,34 @@ function peg$parse(input, options) {
       s0 = peg$FAILED;
       if (peg$silentFails === 0) { peg$fail(peg$e12); }
     }
+
+    return s0;
+  }
+
+  function peg$parseop_notequals() {
+    var s0;
+
+    if (input.substr(peg$currPos, 9).toLowerCase() === peg$c13) {
+      s0 = input.substr(peg$currPos, 9);
+      peg$currPos += 9;
+    } else {
+      s0 = peg$FAILED;
+      if (peg$silentFails === 0) { peg$fail(peg$e13); }
+    }
     if (s0 === peg$FAILED) {
-      if (input.substr(peg$currPos, 9).toLowerCase() === peg$c13) {
-        s0 = input.substr(peg$currPos, 9);
-        peg$currPos += 9;
+      if (input.substr(peg$currPos, 5).toLowerCase() === peg$c14) {
+        s0 = input.substr(peg$currPos, 5);
+        peg$currPos += 5;
       } else {
         s0 = peg$FAILED;
-        if (peg$silentFails === 0) { peg$fail(peg$e13); }
-      }
-      if (s0 === peg$FAILED) {
-        if (input.substr(peg$currPos, 5).toLowerCase() === peg$c14) {
-          s0 = input.substr(peg$currPos, 5);
-          peg$currPos += 5;
-        } else {
-          s0 = peg$FAILED;
-          if (peg$silentFails === 0) { peg$fail(peg$e14); }
-        }
+        if (peg$silentFails === 0) { peg$fail(peg$e14); }
       }
     }
 
     return s0;
   }
 
-  function peg$parseop_equalsnocase() {
+  function peg$parseop_equalsnocase_sym() {
     var s0;
 
     if (input.substr(peg$currPos, 2) === peg$c15) {
@@ -1407,20 +1855,25 @@ function peg$parse(input, options) {
       s0 = peg$FAILED;
       if (peg$silentFails === 0) { peg$fail(peg$e15); }
     }
-    if (s0 === peg$FAILED) {
-      if (input.substr(peg$currPos, 21).toLowerCase() === peg$c16) {
-        s0 = input.substr(peg$currPos, 21);
-        peg$currPos += 21;
-      } else {
-        s0 = peg$FAILED;
-        if (peg$silentFails === 0) { peg$fail(peg$e16); }
-      }
+
+    return s0;
+  }
+
+  function peg$parseop_equalsnocase() {
+    var s0;
+
+    if (input.substr(peg$currPos, 21).toLowerCase() === peg$c16) {
+      s0 = input.substr(peg$currPos, 21);
+      peg$currPos += 21;
+    } else {
+      s0 = peg$FAILED;
+      if (peg$silentFails === 0) { peg$fail(peg$e16); }
     }
 
     return s0;
   }
 
-  function peg$parseop_greater() {
+  function peg$parseop_greater_sym() {
     var s0;
 
     if (input.charCodeAt(peg$currPos) === 62) {
@@ -1430,20 +1883,25 @@ function peg$parse(input, options) {
       s0 = peg$FAILED;
       if (peg$silentFails === 0) { peg$fail(peg$e17); }
     }
-    if (s0 === peg$FAILED) {
-      if (input.substr(peg$currPos, 11).toLowerCase() === peg$c18) {
-        s0 = input.substr(peg$currPos, 11);
-        peg$currPos += 11;
-      } else {
-        s0 = peg$FAILED;
-        if (peg$silentFails === 0) { peg$fail(peg$e18); }
-      }
+
+    return s0;
+  }
+
+  function peg$parseop_greater() {
+    var s0;
+
+    if (input.substr(peg$currPos, 11).toLowerCase() === peg$c18) {
+      s0 = input.substr(peg$currPos, 11);
+      peg$currPos += 11;
+    } else {
+      s0 = peg$FAILED;
+      if (peg$silentFails === 0) { peg$fail(peg$e18); }
     }
 
     return s0;
   }
 
-  function peg$parseop_greatereq() {
+  function peg$parseop_greatereq_sym() {
     var s0;
 
     if (input.substr(peg$currPos, 2) === peg$c19) {
@@ -1453,20 +1911,25 @@ function peg$parse(input, options) {
       s0 = peg$FAILED;
       if (peg$silentFails === 0) { peg$fail(peg$e19); }
     }
-    if (s0 === peg$FAILED) {
-      if (input.substr(peg$currPos, 19).toLowerCase() === peg$c20) {
-        s0 = input.substr(peg$currPos, 19);
-        peg$currPos += 19;
-      } else {
-        s0 = peg$FAILED;
-        if (peg$silentFails === 0) { peg$fail(peg$e20); }
-      }
+
+    return s0;
+  }
+
+  function peg$parseop_greatereq() {
+    var s0;
+
+    if (input.substr(peg$currPos, 19).toLowerCase() === peg$c20) {
+      s0 = input.substr(peg$currPos, 19);
+      peg$currPos += 19;
+    } else {
+      s0 = peg$FAILED;
+      if (peg$silentFails === 0) { peg$fail(peg$e20); }
     }
 
     return s0;
   }
 
-  function peg$parseop_lesser() {
+  function peg$parseop_lesser_sym() {
     var s0;
 
     if (input.charCodeAt(peg$currPos) === 60) {
@@ -1476,20 +1939,25 @@ function peg$parse(input, options) {
       s0 = peg$FAILED;
       if (peg$silentFails === 0) { peg$fail(peg$e21); }
     }
-    if (s0 === peg$FAILED) {
-      if (input.substr(peg$currPos, 10).toLowerCase() === peg$c22) {
-        s0 = input.substr(peg$currPos, 10);
-        peg$currPos += 10;
-      } else {
-        s0 = peg$FAILED;
-        if (peg$silentFails === 0) { peg$fail(peg$e22); }
-      }
+
+    return s0;
+  }
+
+  function peg$parseop_lesser() {
+    var s0;
+
+    if (input.substr(peg$currPos, 10).toLowerCase() === peg$c22) {
+      s0 = input.substr(peg$currPos, 10);
+      peg$currPos += 10;
+    } else {
+      s0 = peg$FAILED;
+      if (peg$silentFails === 0) { peg$fail(peg$e22); }
     }
 
     return s0;
   }
 
-  function peg$parseop_lessereq() {
+  function peg$parseop_lessereq_sym() {
     var s0;
 
     if (input.substr(peg$currPos, 2) === peg$c23) {
@@ -1499,20 +1967,25 @@ function peg$parse(input, options) {
       s0 = peg$FAILED;
       if (peg$silentFails === 0) { peg$fail(peg$e23); }
     }
-    if (s0 === peg$FAILED) {
-      if (input.substr(peg$currPos, 18).toLowerCase() === peg$c24) {
-        s0 = input.substr(peg$currPos, 18);
-        peg$currPos += 18;
-      } else {
-        s0 = peg$FAILED;
-        if (peg$silentFails === 0) { peg$fail(peg$e24); }
-      }
+
+    return s0;
+  }
+
+  function peg$parseop_lessereq() {
+    var s0;
+
+    if (input.substr(peg$currPos, 18).toLowerCase() === peg$c24) {
+      s0 = input.substr(peg$currPos, 18);
+      peg$currPos += 18;
+    } else {
+      s0 = peg$FAILED;
+      if (peg$silentFails === 0) { peg$fail(peg$e24); }
     }
 
     return s0;
   }
 
-  function peg$parseop_regexmatch() {
+  function peg$parseop_regexmatch_sym() {
     var s0;
 
     if (input.substr(peg$currPos, 2) === peg$c25) {
@@ -1522,20 +1995,25 @@ function peg$parse(input, options) {
       s0 = peg$FAILED;
       if (peg$silentFails === 0) { peg$fail(peg$e25); }
     }
-    if (s0 === peg$FAILED) {
-      if (input.substr(peg$currPos, 9).toLowerCase() === peg$c26) {
-        s0 = input.substr(peg$currPos, 9);
-        peg$currPos += 9;
-      } else {
-        s0 = peg$FAILED;
-        if (peg$silentFails === 0) { peg$fail(peg$e26); }
-      }
+
+    return s0;
+  }
+
+  function peg$parseop_regexmatch() {
+    var s0;
+
+    if (input.substr(peg$currPos, 9).toLowerCase() === peg$c26) {
+      s0 = input.substr(peg$currPos, 9);
+      peg$currPos += 9;
+    } else {
+      s0 = peg$FAILED;
+      if (peg$silentFails === 0) { peg$fail(peg$e26); }
     }
 
     return s0;
   }
 
-  function peg$parseop_matches() {
+  function peg$parseop_matches_sym() {
     var s0;
 
     if (input.charCodeAt(peg$currPos) === 126) {
@@ -1545,29 +2023,34 @@ function peg$parse(input, options) {
       s0 = peg$FAILED;
       if (peg$silentFails === 0) { peg$fail(peg$e27); }
     }
+
+    return s0;
+  }
+
+  function peg$parseop_matches() {
+    var s0;
+
+    if (input.substr(peg$currPos, 7).toLowerCase() === peg$c28) {
+      s0 = input.substr(peg$currPos, 7);
+      peg$currPos += 7;
+    } else {
+      s0 = peg$FAILED;
+      if (peg$silentFails === 0) { peg$fail(peg$e28); }
+    }
     if (s0 === peg$FAILED) {
-      if (input.substr(peg$currPos, 7).toLowerCase() === peg$c28) {
-        s0 = input.substr(peg$currPos, 7);
-        peg$currPos += 7;
+      if (input.substr(peg$currPos, 4).toLowerCase() === peg$c29) {
+        s0 = input.substr(peg$currPos, 4);
+        peg$currPos += 4;
       } else {
         s0 = peg$FAILED;
-        if (peg$silentFails === 0) { peg$fail(peg$e28); }
-      }
-      if (s0 === peg$FAILED) {
-        if (input.substr(peg$currPos, 4).toLowerCase() === peg$c29) {
-          s0 = input.substr(peg$currPos, 4);
-          peg$currPos += 4;
-        } else {
-          s0 = peg$FAILED;
-          if (peg$silentFails === 0) { peg$fail(peg$e29); }
-        }
+        if (peg$silentFails === 0) { peg$fail(peg$e29); }
       }
     }
 
     return s0;
   }
 
-  function peg$parseop_matchespath() {
+  function peg$parseop_matchespath_sym() {
     var s0;
 
     if (input.substr(peg$currPos, 2) === peg$c30) {
@@ -1577,29 +2060,34 @@ function peg$parse(input, options) {
       s0 = peg$FAILED;
       if (peg$silentFails === 0) { peg$fail(peg$e30); }
     }
+
+    return s0;
+  }
+
+  function peg$parseop_matchespath() {
+    var s0;
+
+    if (input.substr(peg$currPos, 11).toLowerCase() === peg$c31) {
+      s0 = input.substr(peg$currPos, 11);
+      peg$currPos += 11;
+    } else {
+      s0 = peg$FAILED;
+      if (peg$silentFails === 0) { peg$fail(peg$e31); }
+    }
     if (s0 === peg$FAILED) {
-      if (input.substr(peg$currPos, 11).toLowerCase() === peg$c31) {
-        s0 = input.substr(peg$currPos, 11);
-        peg$currPos += 11;
+      if (input.substr(peg$currPos, 8).toLowerCase() === peg$c32) {
+        s0 = input.substr(peg$currPos, 8);
+        peg$currPos += 8;
       } else {
         s0 = peg$FAILED;
-        if (peg$silentFails === 0) { peg$fail(peg$e31); }
-      }
-      if (s0 === peg$FAILED) {
-        if (input.substr(peg$currPos, 8).toLowerCase() === peg$c32) {
-          s0 = input.substr(peg$currPos, 8);
-          peg$currPos += 8;
-        } else {
-          s0 = peg$FAILED;
-          if (peg$silentFails === 0) { peg$fail(peg$e32); }
-        }
+        if (peg$silentFails === 0) { peg$fail(peg$e32); }
       }
     }
 
     return s0;
   }
 
-  function peg$parseop_startswith() {
+  function peg$parseop_startswith_sym() {
     var s0;
 
     if (input.substr(peg$currPos, 2) === peg$c33) {
@@ -1609,14 +2097,19 @@ function peg$parse(input, options) {
       s0 = peg$FAILED;
       if (peg$silentFails === 0) { peg$fail(peg$e33); }
     }
-    if (s0 === peg$FAILED) {
-      if (input.substr(peg$currPos, 10).toLowerCase() === peg$c34) {
-        s0 = input.substr(peg$currPos, 10);
-        peg$currPos += 10;
-      } else {
-        s0 = peg$FAILED;
-        if (peg$silentFails === 0) { peg$fail(peg$e34); }
-      }
+
+    return s0;
+  }
+
+  function peg$parseop_startswith() {
+    var s0;
+
+    if (input.substr(peg$currPos, 10).toLowerCase() === peg$c34) {
+      s0 = input.substr(peg$currPos, 10);
+      peg$currPos += 10;
+    } else {
+      s0 = peg$FAILED;
+      if (peg$silentFails === 0) { peg$fail(peg$e34); }
     }
 
     return s0;
@@ -1637,7 +2130,7 @@ function peg$parse(input, options) {
       s2 = peg$parseDecimalIntegerLiteral();
       if (s2 !== peg$FAILED) {
         peg$savedPos = s0;
-        s0 = peg$f30(s2);
+        s0 = peg$f52(s2);
       } else {
         peg$currPos = s0;
         s0 = peg$FAILED;
@@ -1693,7 +2186,7 @@ function peg$parse(input, options) {
         }
         if (s4 !== peg$FAILED) {
           peg$savedPos = s0;
-          s0 = peg$f31();
+          s0 = peg$f53();
         } else {
           peg$currPos = s0;
           s0 = peg$FAILED;
@@ -1735,7 +2228,7 @@ function peg$parse(input, options) {
         }
         if (s2 !== peg$FAILED) {
           peg$savedPos = s0;
-          s0 = peg$f32();
+          s0 = peg$f54();
         } else {
           peg$currPos = s0;
           s0 = peg$FAILED;
@@ -1759,7 +2252,7 @@ function peg$parse(input, options) {
         s2 = peg$parseDecimalIntegerLiteral();
         if (s2 !== peg$FAILED) {
           peg$savedPos = s0;
-          s0 = peg$f33();
+          s0 = peg$f55();
         } else {
           peg$currPos = s0;
           s0 = peg$FAILED;
@@ -1872,7 +2365,7 @@ function peg$parse(input, options) {
         }
       }
       peg$savedPos = s0;
-      s0 = peg$f34();
+      s0 = peg$f56();
     } else {
       peg$currPos = s0;
       s0 = peg$FAILED;
@@ -1920,7 +2413,7 @@ function peg$parse(input, options) {
       }
       if (s3 !== peg$FAILED) {
         peg$savedPos = s0;
-        s0 = peg$f35(s2);
+        s0 = peg$f57(s2);
       } else {
         peg$currPos = s0;
         s0 = peg$FAILED;
@@ -1940,7 +2433,7 @@ function peg$parse(input, options) {
       }
       if (s1 !== peg$FAILED) {
         peg$savedPos = s0;
-        s1 = peg$f36();
+        s1 = peg$f58();
       }
       s0 = s1;
       if (s0 === peg$FAILED) {
@@ -1954,7 +2447,7 @@ function peg$parse(input, options) {
         }
         if (s1 !== peg$FAILED) {
           peg$savedPos = s0;
-          s1 = peg$f37();
+          s1 = peg$f59();
         }
         s0 = s1;
         if (s0 === peg$FAILED) {
@@ -1968,7 +2461,7 @@ function peg$parse(input, options) {
           }
           if (s1 !== peg$FAILED) {
             peg$savedPos = s0;
-            s1 = peg$f38();
+            s1 = peg$f60();
           }
           s0 = s1;
           if (s0 === peg$FAILED) {
@@ -1976,7 +2469,7 @@ function peg$parse(input, options) {
             s1 = peg$parseDecimalLiteral();
             if (s1 !== peg$FAILED) {
               peg$savedPos = s0;
-              s1 = peg$f39(s1);
+              s1 = peg$f61(s1);
             }
             s0 = s1;
           }
diff --git a/lib/peggy/Apigee-Condition.pegjs b/lib/peggy/Apigee-Condition.pegjs
index 72d1fef..8e709b4 100644
--- a/lib/peggy/Apigee-Condition.pegjs
+++ b/lib/peggy/Apigee-Condition.pegjs
@@ -81,15 +81,27 @@ primary
   = ws* "(" ws* t1:token ws+ op1:op_accepts_literal ws+ v1:value ws* ")" ws* {
     return parseBinaryOp(t1,op1,v1,error);
   }
+  / ws* "(" ws* t1:token ws* op1:op_accepts_literal_sym ws* v1:value ws* ")" ws* {
+    return parseBinaryOp(t1,op1,v1,error);
+  }
   / t1:token ws+ op1:op_accepts_literal ws+ v1:value {
     return parseBinaryOp(t1,op1,v1,error);
   }
+  / t1:token ws* op1:op_accepts_literal_sym ws* v1:value {
+    return parseBinaryOp(t1,op1,v1,error);
+  }
   / ws* "(" ws* t1:token ws+ op1:op_accepts_variable ws+ t2:token ws* ")" ws* {
     return { operator:op1, operands:[t1, t2]};
   }
+  / ws* "(" ws* t1:token ws* op1:op_accepts_variable_sym ws* t2:token ws* ")" ws* {
+    return { operator:op1, operands:[t1, t2]};
+  }
   / t1:token ws+ op1:op_accepts_variable ws+ t2:token {
     return { operator:op1, operands:[t1, t2]};
   }
+  / t1:token ws* op1:op_accepts_variable_sym ws* t2:token {
+    return { operator:op1, operands:[t1, t2]};
+  }
   / token1:token { return token1; }
   / "(" ws* token1:token ws* ")" { return token1; }
   / "(" stmt1:boolean_stmt1 ")" { return stmt1; }
@@ -107,6 +119,19 @@ op_or = "or"i / "||"
 // Ordering is important. For operators that share a common prefix,
 // list the longer, more specific operator, first.
 
+op_accepts_literal_sym
+  = op_equalsnocase_sym { return "EqualsCaseInsensitive"; }
+  / op_startswith_sym {return "StartsWith";}
+  / op_notequals_sym { return "NotEquals"; }
+  / op_equals_sym { return "Equals"; }
+  / op_greatereq_sym {return "GreaterThanOrEquals";}
+  / op_greater_sym {return "GreaterThan";}
+  / op_lessereq_sym {return "LesserThanOrEquals";}
+  / op_lesser_sym {return "LesserThan";}
+  / op_regexmatch_sym {return "JavaRegex";}
+  / op_matchespath_sym {return "MatchesPath";}
+  / op_matches_sym {return "Matches";}
+
 op_accepts_literal
   = op_equalsnocase { return "EqualsCaseInsensitive"; }
   / op_startswith {return "StartsWith";}
@@ -120,6 +145,15 @@ op_accepts_literal
   / op_matchespath {return "MatchesPath";}
   / op_matches {return "Matches";}
 
+op_accepts_variable_sym
+  = op_startswith_sym {return "StartsWith";}
+  / op_notequals_sym { return "NotEquals"; }
+  / op_equals_sym { return "Equals"; }
+  / op_greatereq_sym {return "GreaterThanOrEquals";}
+  / op_greater_sym {return "GreaterThan";}
+  / op_lessereq_sym {return "LesserThanOrEquals";}
+  / op_lesser_sym {return "LesserThan";}
+
 op_accepts_variable
   = op_startswith {return "StartsWith";}
   / op_notequals { return "NotEquals"; }
@@ -130,27 +164,38 @@ op_accepts_variable
   / op_lesser {return "LesserThan";}
 
 
-op_equals = "==" / "=" / "Equals"i / "Is"i
+op_equals_sym = "==" / "="
+op_equals = "Equals"i / "Is"i
 
-op_notequals = "!=" / "NotEquals"i / "IsNot"i
+op_notequals_sym = "!="
+op_notequals = "NotEquals"i / "IsNot"i
 
-op_equalsnocase = ":=" / "EqualsCaseInsensitive"i
+op_equalsnocase_sym = ":="
+op_equalsnocase = "EqualsCaseInsensitive"i
 
-op_greater =  ">" / "GreaterThan"i
+op_greater_sym = ">"
+op_greater = "GreaterThan"i
 
-op_greatereq =  ">=" / "GreaterThanOrEquals"i
+op_greatereq_sym = ">="
+op_greatereq = "GreaterThanOrEquals"i
 
-op_lesser =  "<" / "LesserThan"i
+op_lesser_sym = "<"
+op_lesser = "LesserThan"i
 
-op_lessereq =  "<=" / "LesserThanOrEquals"i
+op_lessereq_sym = "<="
+op_lessereq = "LesserThanOrEquals"i
 
-op_regexmatch = "~~" / "JavaRegex"i
+op_regexmatch_sym = "~~"
+op_regexmatch = "JavaRegex"i
 
-op_matches = "~" / "Matches"i / "Like"i
+op_matches_sym = "~"
+op_matches = "Matches"i / "Like"i
 
-op_matchespath = "~/" / "MatchesPath"i / "LikePath"i
+op_matchespath_sym = "~/"
+op_matchespath = "MatchesPath"i / "LikePath"i
 
-op_startswith =  "=|" / "StartsWith"i
+op_startswith_sym =  "=|"
+op_startswith = "StartsWith"i
 
 
 frac_string
diff --git a/test/specs/ConditionParserTest.js b/test/specs/ConditionParserTest.js
index d90a2aa..7698adb 100644
--- a/test/specs/ConditionParserTest.js
+++ b/test/specs/ConditionParserTest.js
@@ -21,6 +21,34 @@ const expect = require("chai").expect;
 //const debug = require("debug")(`apigeelint:ConditionParser`);
 
 describe("ConditionParser", function () {
+  describe("Spacing", function () {
+    const testcases = [
+      [`request.verb="GET"`, `request.verb = "GET"`, "Equals"],
+      [`request.verb=="GET"`, `request.verb == "GET"`, "Equals"],
+      [`request.verb=="GET"`, `request.verb =="GET"`, "Equals"],
+      [`request.verb!="GET"`, `request.verb != "GET"`, "NotEquals"],
+      [`request.verb!="GET"`, `request.verb!= "GET"`, "NotEquals"],
+      ["A>250", "A > 250", "GreaterThan"],
+      ["A<250", "A < 250", "LesserThan"],
+      ["A>=250", "A >= 250", "GreaterThanOrEquals"],
+      ["A<=250", "A <= 250", "LesserThanOrEquals"],
+      [`A:="seven"`, `A := "seven"`, "EqualsCaseInsensitive"],
+      [`A=|"seven"`, `A =| "seven"`, "StartsWith"],
+      [`A~"seven"`, `A ~ "seven"`, "Matches"],
+      [`A~~"^foo(a|b)$"`, `A ~~ "^foo(a|b)$"`, "JavaRegex"],
+      [`proxy.pathsuffix~/"/a/b"`, `proxy.pathsuffix ~/ "/a/b"`, "MatchesPath"]
+    ];
+
+    testcases.forEach((testcase) => {
+      it(`treats [${testcase[0]}] and [${testcase[1]}] expressions as equivalent`, function () {
+        const result1 = parser.parse(testcase[0]);
+        const result2 = parser.parse(testcase[1]);
+        expect(JSON.stringify(result1)).to.equal(JSON.stringify(result2));
+        expect(result1.operator).to.equal(testcase[2]);
+      });
+    });
+  });
+
   describe("Parens", function () {
     it("treats parenthesized and non-parenthesized atoms as equivalent", function () {
       const c1 = "valid";