Fix shift/reduce conflict in grammar (#1719)

- The grammar had a shift/reduce conflict due to the ambiguity of the
  `IN` keyword. This is resolved by adding generic rule and manually
  resolving to the correct specific rule.
- Added additional test cases.
diff --git a/regress/expected/list_comprehension.out b/regress/expected/list_comprehension.out
index 8b7a645..5b17211 100644
--- a/regress/expected/list_comprehension.out
+++ b/regress/expected/list_comprehension.out
@@ -571,6 +571,15 @@
 ERROR:  could not find rte for i
 LINE 1: ...$$ RETURN [i IN range(0, 10, 2) WHERE i>5 | i^2], i $$) AS (...
                                                              ^
+-- Invalid list comprehension
+SELECT * FROM cypher('list_comprehension', $$ RETURN [1 IN range(0, 10, 2) WHERE 2>5] $$) AS (result agtype);
+ERROR:  Syntax error at or near IN
+LINE 1: SELECT * FROM cypher('list_comprehension', $$ RETURN [1 IN r...
+                                                    ^
+SELECT * FROM cypher('list_comprehension', $$ RETURN [1 IN range(0, 10, 2) | 1] $$) AS (result agtype);
+ERROR:  Syntax error at or near IN
+LINE 1: SELECT * FROM cypher('list_comprehension', $$ RETURN [1 IN r...
+                                                    ^
 SELECT * FROM drop_graph('list_comprehension', true);
 NOTICE:  drop cascades to 4 other objects
 DETAIL:  drop cascades to table list_comprehension._ag_label_vertex
diff --git a/regress/sql/list_comprehension.sql b/regress/sql/list_comprehension.sql
index b4596e5..6a33b64 100644
--- a/regress/sql/list_comprehension.sql
+++ b/regress/sql/list_comprehension.sql
@@ -145,4 +145,8 @@
 SELECT * FROM cypher('list_comprehension', $$ RETURN [i IN range(0, 10, 2)],i $$) AS (result agtype, i agtype);
 SELECT * FROM cypher('list_comprehension', $$ RETURN [i IN range(0, 10, 2) WHERE i>5 | i^2], i $$) AS (result agtype, i agtype);
 
+-- Invalid list comprehension
+SELECT * FROM cypher('list_comprehension', $$ RETURN [1 IN range(0, 10, 2) WHERE 2>5] $$) AS (result agtype);
+SELECT * FROM cypher('list_comprehension', $$ RETURN [1 IN range(0, 10, 2) | 1] $$) AS (result agtype);
+
 SELECT * FROM drop_graph('list_comprehension', true);
\ No newline at end of file
diff --git a/src/backend/parser/cypher_gram.y b/src/backend/parser/cypher_gram.y
index a1878af..359c3a8 100644
--- a/src/backend/parser/cypher_gram.y
+++ b/src/backend/parser/cypher_gram.y
@@ -139,9 +139,6 @@
 /* common */
 %type <node> where_opt
 
-/* list comprehension optional mapping expression */
-%type <node> mapping_expr_opt
-
 /* pattern */
 %type <list> pattern simple_path_opt_parens simple_path
 %type <node> path anonymous_path
@@ -258,7 +255,12 @@
                                          char *opr_name, int location);
 
 // list_comprehension
-static Node *build_list_comprehension_node(char *var_name, Node *expr,
+static Node *verify_rule_as_list_comprehension(Node *expr, Node *expr2,
+                                               Node *where, Node *mapping_expr,
+                                               int var_loc, int expr_loc,
+                                               int where_loc, int mapping_loc);
+
+static Node *build_list_comprehension_node(ColumnRef *var_name, Node *expr,
                                            Node *where, Node *mapping_expr,
                                            int var_loc, int expr_loc,
                                            int where_loc,int mapping_loc);
@@ -2128,20 +2130,51 @@
 
             $$ = (Node *)n;
         }
-    | '[' list_comprehension ']'
-        {
-            $$ = $2;
-        }
+    | list_comprehension
     ;
 
-mapping_expr_opt:
-    /* empty */
+/*
+ * This grammar rule is generic to some extent. It can
+ * evaluate to either IN operator or list comprehension.
+ * This avoids shift/reduce errors between the two rules.
+ */
+list_comprehension:
+    '[' expr IN expr ']'
         {
-            $$ = NULL;
+            Node *n = $2;
+            Node *result = NULL;
+            
+            /*
+             * If the first expr is a ColumnRef(variable), then the rule
+             * should evaluate as a list comprehension. Otherwise, it should
+             * evaluate as an IN operator.
+             */
+            if (nodeTag(n) == T_ColumnRef)
+            {
+                ColumnRef *cref = (ColumnRef *)n;
+                result = build_list_comprehension_node(cref, $4, NULL, NULL,
+                                                       @2, @4, 0, 0);
+            }
+            else
+            {
+                result = (Node *)makeSimpleA_Expr(AEXPR_IN, "=", n, $4, @3);
+            }
+            $$ = result;
         }
-    | '|' expr
+    | '[' expr IN expr WHERE expr ']'
         {
-            $$ = $2;
+            $$ = verify_rule_as_list_comprehension($2, $4, $6, NULL,
+                                                   @2, @4, @6, 0);
+        }
+    | '[' expr IN expr '|' expr ']'
+        {
+            $$ = verify_rule_as_list_comprehension($2, $4, NULL, $6,
+                                                   @2, @4, 0, @6);
+        }
+    | '[' expr IN expr WHERE expr '|' expr ']'
+        {
+            $$ = verify_rule_as_list_comprehension($2, $4, $6, $8,
+                                                   @2, @4, @6, @8);
         }
     ;
 
@@ -2206,14 +2239,6 @@
         }
     ;
 
-list_comprehension:
-    var_name IN expr where_opt mapping_expr_opt
-        {
-            $$ = build_list_comprehension_node($1, $3, $4, $5,
-                                               @1, @3, @4, @5);
-        }
-;
-
 expr_var:
     var_name
         {
@@ -3179,15 +3204,57 @@
     return cr;
 }
 
+// Helper function to verify that the rule is a list comprehension
+static Node *verify_rule_as_list_comprehension(Node *expr, Node *expr2,
+                                               Node *where, Node *mapping_expr,
+                                               int var_loc, int expr_loc,
+                                               int where_loc, int mapping_loc)
+{
+    Node *result = NULL;
+    
+    /*
+     * If the first expression is a ColumnRef, then we can build a
+     * list_comprehension node.
+     * Else its an invalid use of IN operator.
+     */
+    if (nodeTag(expr) == T_ColumnRef)
+    {
+        ColumnRef *cref = (ColumnRef *)expr;
+        result = build_list_comprehension_node(cref, expr2, where,
+                                               mapping_expr, var_loc,
+                                               expr_loc, where_loc,
+                                               mapping_loc);
+    }
+    else
+    {
+        ereport(ERROR,
+                (errcode(ERRCODE_SYNTAX_ERROR),
+                 errmsg("Syntax error at or near IN")));
+    }
+    return result;
+}
+
 /* helper function to build a list_comprehension grammar node */
-static Node *build_list_comprehension_node(char *var_name, Node *expr,
+static Node *build_list_comprehension_node(ColumnRef *cref, Node *expr,
                                            Node *where, Node *mapping_expr,
                                            int var_loc, int expr_loc,
                                            int where_loc, int mapping_loc)
 {
     ResTarget *res = NULL;
     cypher_unwind *unwind = NULL;
-    ColumnRef *cref = NULL;
+    char *var_name = NULL;
+    String *val;
+
+    // Extract name from cref
+    val = linitial(cref->fields);
+
+    if (!IsA(val, String))
+    {
+        ereport(ERROR,
+                (errmsg_internal("unexpected Node for cypher_clause")));
+    }
+
+    var_name = val->sval;
 
     /*
      * Build the ResTarget node for the UNWIND variable var_name attached to
@@ -3201,15 +3268,6 @@
     /* build the UNWIND node */
     unwind = make_ag_node(cypher_unwind);
     unwind->target = res;
-
-    /*
-     * We need to make a ColumnRef of var_name so that it can be used as an expr
-     * for the where clause part of unwind.
-     */
-    cref = makeNode(ColumnRef);
-    cref->fields = list_make1(makeString(var_name));
-    cref->location = var_loc;
-
     unwind->where = where;
 
     /* if there is a mapping function, add its arg to collect */