diff src/vim9compile.c @ 22975:a943b175586a v8.2.2034

patch 8.2.2034: Vim9: list unpack in for statement not compiled yet Commit: https://github.com/vim/vim/commit/792f786aad8409ca9ab895392742643a5b6aed8f Author: Bram Moolenaar <Bram@vim.org> Date: Mon Nov 23 08:31:18 2020 +0100 patch 8.2.2034: Vim9: list unpack in for statement not compiled yet Problem: Vim9: list unpack in for statement not compiled yet. Solution: Compile list unpack. (closes https://github.com/vim/vim/issues/7345)
author Bram Moolenaar <Bram@vim.org>
date Mon, 23 Nov 2020 08:45:04 +0100
parents 4c97c0747017
children b98003d73150
line wrap: on
line diff
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -1888,6 +1888,19 @@ generate_EXECCONCAT(cctx_T *cctx, int co
     return OK;
 }
 
+    static int
+generate_UNPACK(cctx_T *cctx, int var_count, int semicolon)
+{
+    isn_T	*isn;
+
+    RETURN_OK_IF_SKIP(cctx);
+    if ((isn = generate_instr(cctx, ISN_UNPACK)) == NULL)
+	return FAIL;
+    isn->isn_arg.unpack.unp_count = var_count;
+    isn->isn_arg.unpack.unp_semicolon = semicolon;
+    return OK;
+}
+
 /*
  * Generate an instruction for any command modifiers.
  */
@@ -6323,12 +6336,12 @@ compile_endif(char_u *arg, cctx_T *cctx)
 }
 
 /*
- * compile "for var in expr"
+ * Compile "for var in expr":
  *
  * Produces instructions:
  *       PUSHNR -1
  *       STORE loop-idx		Set index to -1
- *       EVAL expr		Push result of "expr"
+ *       EVAL expr		result of "expr" on top of stack
  * top:  FOR loop-idx, end	Increment index, use list on bottom of stack
  *				- if beyond end, jump to "end"
  *				- otherwise get item from list and push it
@@ -6337,11 +6350,19 @@ compile_endif(char_u *arg, cctx_T *cctx)
  *       JUMP top		Jump back to repeat
  * end:	 DROP			Drop the result of "expr"
  *
+ * Compile "for [var1, var2] in expr" - as above, but instead of "STORE var":
+ *	 UNPACK 2		Split item in 2
+ *       STORE var1		Store item in "var1"
+ *       STORE var2		Store item in "var2"
  */
     static char_u *
-compile_for(char_u *arg, cctx_T *cctx)
-{
+compile_for(char_u *arg_start, cctx_T *cctx)
+{
+    char_u	*arg;
+    char_u	*arg_end;
     char_u	*p;
+    int		var_count = 0;
+    int		semicolon = FALSE;
     size_t	varlen;
     garray_T	*instr = &cctx->ctx_instr;
     garray_T	*stack = &cctx->ctx_type_stack;
@@ -6349,18 +6370,12 @@ compile_for(char_u *arg, cctx_T *cctx)
     lvar_T	*loop_lvar;	// loop iteration variable
     lvar_T	*var_lvar;	// variable for "var"
     type_T	*vartype;
-
-    // TODO: list of variables: "for [key, value] in dict"
-    // parse "var"
-    for (p = arg; eval_isnamec1(*p); ++p)
-	;
-    varlen = p - arg;
-    var_lvar = lookup_local(arg, varlen, cctx);
-    if (var_lvar != NULL)
-    {
-	semsg(_(e_variable_already_declared), arg);
-	return NULL;
-    }
+    type_T	*item_type = &t_any;
+    int		idx;
+
+    p = skip_var_list(arg_start, TRUE, &var_count, &semicolon, FALSE);
+    if (var_count == 0)
+	var_count = 1;
 
     // consume "in"
     p = skipwhite(p);
@@ -6371,12 +6386,12 @@ compile_for(char_u *arg, cctx_T *cctx)
     }
     p = skipwhite(p + 2);
 
-
     scope = new_scope(cctx, FOR_SCOPE);
     if (scope == NULL)
 	return NULL;
 
-    // Reserve a variable to store the loop iteration counter.
+    // Reserve a variable to store the loop iteration counter and initialize it
+    // to -1.
     loop_lvar = reserve_local(cctx, (char_u *)"", 0, FALSE, &t_number);
     if (loop_lvar == NULL)
     {
@@ -6384,16 +6399,6 @@ compile_for(char_u *arg, cctx_T *cctx)
 	drop_scope(cctx);
 	return NULL;
     }
-
-    // Reserve a variable to store "var"
-    var_lvar = reserve_local(cctx, arg, varlen, FALSE, &t_any);
-    if (var_lvar == NULL)
-    {
-	// out of memory or used as an argument
-	drop_scope(cctx);
-	return NULL;
-    }
-
     generate_STORENR(cctx, loop_lvar->lv_idx, -1);
 
     // compile "expr", it remains on the stack until "endfor"
@@ -6403,6 +6408,7 @@ compile_for(char_u *arg, cctx_T *cctx)
 	drop_scope(cctx);
 	return NULL;
     }
+    arg_end = arg;
 
     // Now that we know the type of "var", check that it is a list, now or at
     // runtime.
@@ -6412,16 +6418,78 @@ compile_for(char_u *arg, cctx_T *cctx)
 	drop_scope(cctx);
 	return NULL;
     }
+
     if (vartype->tt_type == VAR_LIST && vartype->tt_member->tt_type != VAR_ANY)
-	var_lvar->lv_type = vartype->tt_member;
+    {
+	if (var_count == 1)
+	    item_type = vartype->tt_member;
+	else if (vartype->tt_member->tt_type == VAR_LIST
+		      && vartype->tt_member->tt_member->tt_type != VAR_ANY)
+	    item_type = vartype->tt_member->tt_member;
+    }
 
     // "for_end" is set when ":endfor" is found
     scope->se_u.se_for.fs_top_label = instr->ga_len;
-
     generate_FOR(cctx, loop_lvar->lv_idx);
-    generate_STORE(cctx, ISN_STORE, var_lvar->lv_idx, NULL);
-
-    return arg;
+
+    arg = arg_start;
+    if (var_count > 1)
+    {
+	generate_UNPACK(cctx, var_count, semicolon);
+	arg = skipwhite(arg + 1);	// skip white after '['
+
+	// the list item is replaced by a number of items
+	if (ga_grow(stack, var_count - 1) == FAIL)
+	{
+	    drop_scope(cctx);
+	    return NULL;
+	}
+	--stack->ga_len;
+	for (idx = 0; idx < var_count; ++idx)
+	{
+	    ((type_T **)stack->ga_data)[stack->ga_len] =
+				(semicolon && idx == 0) ? vartype : item_type;
+	    ++stack->ga_len;
+	}
+    }
+
+    for (idx = 0; idx < var_count; ++idx)
+    {
+	// TODO: use skip_var_one, also assign to @r, $VAR, etc.
+	p = arg;
+	while (eval_isnamec(*p))
+	    ++p;
+	varlen = p - arg;
+	var_lvar = lookup_local(arg, varlen, cctx);
+	if (var_lvar != NULL)
+	{
+	    semsg(_(e_variable_already_declared), arg);
+	    drop_scope(cctx);
+	    return NULL;
+	}
+
+	// Reserve a variable to store "var".
+	// TODO: check for type
+	var_lvar = reserve_local(cctx, arg, varlen, FALSE, &t_any);
+	if (var_lvar == NULL)
+	{
+	    // out of memory or used as an argument
+	    drop_scope(cctx);
+	    return NULL;
+	}
+
+	if (semicolon && idx == var_count - 1)
+	    var_lvar->lv_type = vartype;
+	else
+	    var_lvar->lv_type = item_type;
+	generate_STORE(cctx, ISN_STORE, var_lvar->lv_idx, NULL);
+
+	if (*p == ',' || *p == ';')
+	    ++p;
+	arg = skipwhite(p);
+    }
+
+    return arg_end;
 }
 
 /*
@@ -7957,6 +8025,7 @@ delete_instr(isn_T *isn)
 	case ISN_STRSLICE:
 	case ISN_THROW:
 	case ISN_TRY:
+	case ISN_UNPACK:
 	    // nothing allocated
 	    break;
     }