patch 9.1.1232: Vim script is missing the tuple data type

Problem:  Vim script is missing the tuple data type
Solution: Add support for the tuple data type
          (Yegappan Lakshmanan)

closes: #16776

Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/list.c b/src/list.c
index 36ce494..c48c751 100644
--- a/src/list.c
+++ b/src/list.c
@@ -1520,15 +1520,13 @@
 
     rettv->v_type = VAR_STRING;
 
-    if (in_vim9script()
-	    && (check_for_list_arg(argvars, 0) == FAIL
-		|| check_for_opt_string_arg(argvars, 1) == FAIL))
+    if (check_for_list_or_tuple_arg(argvars, 0) == FAIL
+	    || check_for_opt_string_arg(argvars, 1) == FAIL)
 	return;
 
-    if (check_for_list_arg(argvars, 0) == FAIL)
-	return;
-
-    if (argvars[0].vval.v_list == NULL)
+    if ((argvars[0].v_type == VAR_LIST && argvars[0].vval.v_list == NULL)
+	    || (argvars[0].v_type == VAR_TUPLE
+		&& argvars[0].vval.v_tuple == NULL))
 	return;
 
     if (argvars[1].v_type == VAR_UNKNOWN)
@@ -1539,7 +1537,10 @@
     if (sep != NULL)
     {
 	ga_init2(&ga, sizeof(char), 80);
-	list_join(&ga, argvars[0].vval.v_list, sep, TRUE, FALSE, 0);
+	if (argvars[0].v_type == VAR_LIST)
+	    list_join(&ga, argvars[0].vval.v_list, sep, TRUE, FALSE, 0);
+	else
+	    tuple_join(&ga, argvars[0].vval.v_tuple, sep, TRUE, FALSE, 0);
 	ga_append(&ga, NUL);
 	rettv->vval.v_string = (char_u *)ga.ga_data;
     }
@@ -1778,6 +1779,63 @@
 }
 
 /*
+ * "list2tuple()" function
+ */
+    void
+f_list2tuple(typval_T *argvars, typval_T *rettv)
+{
+    list_T	*l;
+    listitem_T	*li;
+    tuple_T	*tuple;
+
+    rettv->v_type = VAR_TUPLE;
+    rettv->vval.v_tuple = NULL;
+
+    if (check_for_list_arg(argvars, 0) == FAIL)
+	return;
+
+    l = argvars[0].vval.v_list;
+    if (l == NULL)
+	return;  // empty list results in empty tuple
+
+    CHECK_LIST_MATERIALIZE(l);
+
+    if (rettv_tuple_set_with_items(rettv, list_len(l)) == FAIL)
+	return;
+
+    tuple = rettv->vval.v_tuple;
+    FOR_ALL_LIST_ITEMS(l, li)
+    {
+	copy_tv(&li->li_tv, TUPLE_ITEM(tuple, TUPLE_LEN(tuple)));
+	tuple->tv_items.ga_len++;
+    }
+}
+
+/*
+ * "tuple2list()" function
+ */
+    void
+f_tuple2list(typval_T *argvars, typval_T *rettv)
+{
+    list_T	*l;
+    tuple_T	*tuple;
+
+    if (rettv_list_alloc(rettv) == FAIL)
+	return;
+
+    if (check_for_tuple_arg(argvars, 0) == FAIL)
+	return;
+
+    tuple = argvars[0].vval.v_tuple;
+    if (tuple == NULL)
+	return;  // empty tuple results in empty list
+
+    l = rettv->vval.v_list;
+    for (int i = 0; i < tuple_len(tuple); i++)
+	list_append_tv(l, TUPLE_ITEM(tuple, i));
+}
+
+/*
  * Remove item argvars[1] from List argvars[0]. If argvars[2] is supplied, then
  * remove the range of items from argvars[1] to argvars[2] (inclusive).
  */
@@ -2560,10 +2618,16 @@
 	copy_tv(&argvars[0], rettv);
 
     if (in_vim9script()
-	    && (check_for_list_or_dict_or_blob_or_string_arg(argvars, 0)
+	    && (check_for_list_tuple_dict_blob_or_string_arg(argvars, 0)
 								== FAIL))
 	return;
 
+    if (argvars[0].v_type == VAR_TUPLE && filtermap != FILTERMAP_FOREACH)
+    {
+	semsg(_(e_cannot_use_tuple_with_function_str), func_name);
+	return;
+    }
+
     if (filtermap == FILTERMAP_MAP && in_vim9script())
     {
 	// Check that map() does not change the declared type of the list or
@@ -2577,11 +2641,17 @@
 
     if (argvars[0].v_type != VAR_BLOB
 	    && argvars[0].v_type != VAR_LIST
+	    && argvars[0].v_type != VAR_TUPLE
 	    && argvars[0].v_type != VAR_DICT
 	    && argvars[0].v_type != VAR_STRING)
     {
-	semsg(_(e_argument_of_str_must_be_list_string_dictionary_or_blob),
-								    func_name);
+	char *msg;
+
+	if (filtermap == FILTERMAP_FOREACH)
+	    msg = e_argument_of_str_must_be_list_tuple_string_dictionary_or_blob;
+	else
+	    msg = e_argument_of_str_must_be_list_string_dictionary_or_blob;
+	semsg(_(msg), func_name);
 	return;
     }
 
@@ -2611,6 +2681,8 @@
 							    arg_errmsg, rettv);
     else if (argvars[0].v_type == VAR_STRING)
 	string_filter_map(tv_get_string(&argvars[0]), filtermap, expr, rettv);
+    else if (argvars[0].v_type == VAR_TUPLE)
+	tuple_foreach(argvars[0].vval.v_tuple, filtermap, expr);
     else // argvars[0].v_type == VAR_LIST
 	list_filter_map(argvars[0].vval.v_list, filtermap, type, func_name,
 						      arg_errmsg, expr, rettv);
@@ -2743,7 +2815,7 @@
     int		error = FALSE;
 
     if (in_vim9script()
-	    && (check_for_string_or_list_or_dict_arg(argvars, 0) == FAIL
+	    && (check_for_string_list_tuple_or_dict_arg(argvars, 0) == FAIL
 		|| check_for_opt_bool_arg(argvars, 2) == FAIL
 		|| (argvars[2].v_type != VAR_UNKNOWN
 		    && check_for_opt_number_arg(argvars, 3) == FAIL)))
@@ -2765,6 +2837,16 @@
 	if (!error)
 	    n = list_count(argvars[0].vval.v_list, &argvars[1], idx, ic);
     }
+    else if (!error && argvars[0].v_type == VAR_TUPLE)
+    {
+	long idx = 0;
+
+	if (argvars[2].v_type != VAR_UNKNOWN
+		&& argvars[3].v_type != VAR_UNKNOWN)
+	    idx = (long)tv_get_number_chk(&argvars[3], &error);
+	if (!error)
+	    n = tuple_count(argvars[0].vval.v_tuple, &argvars[1], idx, ic);
+    }
     else if (!error && argvars[0].v_type == VAR_DICT)
     {
 	if (argvars[2].v_type != VAR_UNKNOWN
@@ -3033,7 +3115,7 @@
     void
 f_reverse(typval_T *argvars, typval_T *rettv)
 {
-    if (check_for_string_or_list_or_blob_arg(argvars, 0) == FAIL)
+    if (check_for_string_or_list_or_tuple_or_blob_arg(argvars, 0) == FAIL)
 	return;
 
     if (argvars[0].v_type == VAR_BLOB)
@@ -3048,6 +3130,8 @@
     }
     else if (argvars[0].v_type == VAR_LIST)
 	list_reverse(argvars[0].vval.v_list, rettv);
+    else if (argvars[0].v_type == VAR_TUPLE)
+	tuple_reverse(argvars[0].vval.v_tuple, rettv);
 }
 
 /*
@@ -3153,6 +3237,7 @@
 
 /*
  * "reduce(list, { accumulator, element -> value } [, initial])" function
+ * "reduce(tuple, { accumulator, element -> value } [, initial])" function
  * "reduce(blob, { accumulator, element -> value } [, initial])"
  * "reduce(string, { accumulator, element -> value } [, initial])"
  */
@@ -3161,18 +3246,9 @@
 {
     char_u	*func_name;
 
-    if (in_vim9script()
-		   && check_for_string_or_list_or_blob_arg(argvars, 0) == FAIL)
+    if (check_for_string_or_list_or_tuple_or_blob_arg(argvars, 0) == FAIL)
 	return;
 
-    if (argvars[0].v_type != VAR_STRING
-	    && argvars[0].v_type != VAR_LIST
-	    && argvars[0].v_type != VAR_BLOB)
-    {
-	emsg(_(e_string_list_or_blob_required));
-	return;
-    }
-
     if (argvars[1].v_type == VAR_FUNC)
 	func_name = argvars[1].vval.v_string;
     else if (argvars[1].v_type == VAR_PARTIAL)
@@ -3187,6 +3263,8 @@
 
     if (argvars[0].v_type == VAR_LIST)
 	list_reduce(argvars, &argvars[1], rettv);
+    else if (argvars[0].v_type == VAR_TUPLE)
+	tuple_reduce(argvars, &argvars[1], rettv);
     else if (argvars[0].v_type == VAR_STRING)
 	string_reduce(argvars, &argvars[1], rettv);
     else
@@ -3202,6 +3280,7 @@
     if (in_vim9script()
 	    && ((argvars[0].v_type != VAR_STRING
 		    && argvars[0].v_type != VAR_LIST
+		    && argvars[0].v_type != VAR_TUPLE
 		    && argvars[0].v_type != VAR_BLOB
 		    && check_for_list_arg(argvars, 0) == FAIL)
 		|| check_for_number_arg(argvars, 1) == FAIL