Mercurial > vim
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; }