mirror of
https://github.com/vim/vim.git
synced 2025-09-23 03:43:49 -04:00
patch 8.2.2719: Vim9: appending to dict item doesn't work in a :def function
Problem: Vim9: appending to dict item doesn't work in a :def function. Solution: Implement assignment with operator on indexed item.
This commit is contained in:
@@ -1116,7 +1116,6 @@ enddef
|
|||||||
|
|
||||||
def Test_assign_dict_with_op()
|
def Test_assign_dict_with_op()
|
||||||
var lines =<< trim END
|
var lines =<< trim END
|
||||||
vim9script
|
|
||||||
var ds: dict<string> = {a: 'x'}
|
var ds: dict<string> = {a: 'x'}
|
||||||
ds['a'] ..= 'y'
|
ds['a'] ..= 'y'
|
||||||
ds.a ..= 'z'
|
ds.a ..= 'z'
|
||||||
@@ -1148,8 +1147,46 @@ def Test_assign_dict_with_op()
|
|||||||
dn.a %= 6
|
dn.a %= 6
|
||||||
assert_equal(2, dn.a)
|
assert_equal(2, dn.a)
|
||||||
END
|
END
|
||||||
# TODO: this should also work with a :def function
|
CheckDefAndScriptSuccess(lines)
|
||||||
CheckScriptSuccess(lines)
|
enddef
|
||||||
|
|
||||||
|
def Test_assign_list_with_op()
|
||||||
|
var lines =<< trim END
|
||||||
|
var ls: list<string> = ['x']
|
||||||
|
ls[0] ..= 'y'
|
||||||
|
assert_equal('xy', ls[0])
|
||||||
|
|
||||||
|
var ln: list<number> = [9]
|
||||||
|
ln[0] += 2
|
||||||
|
assert_equal(11, ln[0])
|
||||||
|
|
||||||
|
ln[0] -= 3
|
||||||
|
assert_equal(8, ln[0])
|
||||||
|
|
||||||
|
ln[0] *= 2
|
||||||
|
assert_equal(16, ln[0])
|
||||||
|
|
||||||
|
ln[0] /= 3
|
||||||
|
assert_equal(5, ln[0])
|
||||||
|
|
||||||
|
ln[0] %= 3
|
||||||
|
assert_equal(2, ln[0])
|
||||||
|
END
|
||||||
|
CheckDefAndScriptSuccess(lines)
|
||||||
|
enddef
|
||||||
|
|
||||||
|
def Test_assign_with_op_fails()
|
||||||
|
var lines =<< trim END
|
||||||
|
var s = 'abc'
|
||||||
|
s[1] += 'x'
|
||||||
|
END
|
||||||
|
CheckDefAndScriptFailure2(lines, 'E1141:', 'E689:', 2)
|
||||||
|
|
||||||
|
lines =<< trim END
|
||||||
|
var s = 'abc'
|
||||||
|
s[1] ..= 'x'
|
||||||
|
END
|
||||||
|
CheckDefAndScriptFailure2(lines, 'E1141:', 'E689:', 2)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
def Test_assign_lambda()
|
def Test_assign_lambda()
|
||||||
|
@@ -750,6 +750,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
2719,
|
||||||
/**/
|
/**/
|
||||||
2718,
|
2718,
|
||||||
/**/
|
/**/
|
||||||
|
@@ -2649,6 +2649,112 @@ clear_ppconst(ppconst_T *ppconst)
|
|||||||
ppconst->pp_used = 0;
|
ppconst->pp_used = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compile getting a member from a list/dict/string/blob. Stack has the
|
||||||
|
* indexable value and the index.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
compile_member(int is_slice, cctx_T *cctx)
|
||||||
|
{
|
||||||
|
type_T **typep;
|
||||||
|
garray_T *stack = &cctx->ctx_type_stack;
|
||||||
|
vartype_T vtype;
|
||||||
|
type_T *valtype;
|
||||||
|
|
||||||
|
// We can index a list and a dict. If we don't know the type
|
||||||
|
// we can use the index value type.
|
||||||
|
// TODO: If we don't know use an instruction to figure it out at
|
||||||
|
// runtime.
|
||||||
|
typep = ((type_T **)stack->ga_data) + stack->ga_len
|
||||||
|
- (is_slice ? 3 : 2);
|
||||||
|
vtype = (*typep)->tt_type;
|
||||||
|
valtype = ((type_T **)stack->ga_data)[stack->ga_len - 1];
|
||||||
|
// If the index is a string, the variable must be a Dict.
|
||||||
|
if (*typep == &t_any && valtype == &t_string)
|
||||||
|
vtype = VAR_DICT;
|
||||||
|
if (vtype == VAR_STRING || vtype == VAR_LIST || vtype == VAR_BLOB)
|
||||||
|
{
|
||||||
|
if (need_type(valtype, &t_number, -1, 0, cctx, FALSE, FALSE) == FAIL)
|
||||||
|
return FAIL;
|
||||||
|
if (is_slice)
|
||||||
|
{
|
||||||
|
valtype = ((type_T **)stack->ga_data)[stack->ga_len - 2];
|
||||||
|
if (need_type(valtype, &t_number, -2, 0, cctx,
|
||||||
|
FALSE, FALSE) == FAIL)
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vtype == VAR_DICT)
|
||||||
|
{
|
||||||
|
if (is_slice)
|
||||||
|
{
|
||||||
|
emsg(_(e_cannot_slice_dictionary));
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
if ((*typep)->tt_type == VAR_DICT)
|
||||||
|
{
|
||||||
|
*typep = (*typep)->tt_member;
|
||||||
|
if (*typep == &t_unknown)
|
||||||
|
// empty dict was used
|
||||||
|
*typep = &t_any;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (need_type(*typep, &t_dict_any, -2, 0, cctx,
|
||||||
|
FALSE, FALSE) == FAIL)
|
||||||
|
return FAIL;
|
||||||
|
*typep = &t_any;
|
||||||
|
}
|
||||||
|
if (may_generate_2STRING(-1, cctx) == FAIL)
|
||||||
|
return FAIL;
|
||||||
|
if (generate_instr_drop(cctx, ISN_MEMBER, 1) == FAIL)
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
else if (vtype == VAR_STRING)
|
||||||
|
{
|
||||||
|
*typep = &t_string;
|
||||||
|
if ((is_slice
|
||||||
|
? generate_instr_drop(cctx, ISN_STRSLICE, 2)
|
||||||
|
: generate_instr_drop(cctx, ISN_STRINDEX, 1)) == FAIL)
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
else if (vtype == VAR_BLOB)
|
||||||
|
{
|
||||||
|
emsg("Sorry, blob index and slice not implemented yet");
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
else if (vtype == VAR_LIST || *typep == &t_any)
|
||||||
|
{
|
||||||
|
if (is_slice)
|
||||||
|
{
|
||||||
|
if (generate_instr_drop(cctx,
|
||||||
|
vtype == VAR_LIST ? ISN_LISTSLICE : ISN_ANYSLICE,
|
||||||
|
2) == FAIL)
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((*typep)->tt_type == VAR_LIST)
|
||||||
|
{
|
||||||
|
*typep = (*typep)->tt_member;
|
||||||
|
if (*typep == &t_unknown)
|
||||||
|
// empty list was used
|
||||||
|
*typep = &t_any;
|
||||||
|
}
|
||||||
|
if (generate_instr_drop(cctx,
|
||||||
|
vtype == VAR_LIST ? ISN_LISTINDEX : ISN_ANYINDEX, 1) == FAIL)
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
emsg(_(e_string_list_dict_or_blob_required));
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generate an instruction to load script-local variable "name", without the
|
* Generate an instruction to load script-local variable "name", without the
|
||||||
* leading "s:".
|
* leading "s:".
|
||||||
@@ -3934,10 +4040,6 @@ compile_subscript(
|
|||||||
}
|
}
|
||||||
else if (**arg == '[')
|
else if (**arg == '[')
|
||||||
{
|
{
|
||||||
garray_T *stack = &cctx->ctx_type_stack;
|
|
||||||
type_T **typep;
|
|
||||||
type_T *valtype;
|
|
||||||
vartype_T vtype;
|
|
||||||
int is_slice = FALSE;
|
int is_slice = FALSE;
|
||||||
|
|
||||||
// list index: list[123]
|
// list index: list[123]
|
||||||
@@ -4004,99 +4106,8 @@ compile_subscript(
|
|||||||
}
|
}
|
||||||
*arg = *arg + 1;
|
*arg = *arg + 1;
|
||||||
|
|
||||||
// We can index a list and a dict. If we don't know the type
|
if (compile_member(is_slice, cctx) == FAIL)
|
||||||
// we can use the index value type.
|
|
||||||
// TODO: If we don't know use an instruction to figure it out at
|
|
||||||
// runtime.
|
|
||||||
typep = ((type_T **)stack->ga_data) + stack->ga_len
|
|
||||||
- (is_slice ? 3 : 2);
|
|
||||||
vtype = (*typep)->tt_type;
|
|
||||||
valtype = ((type_T **)stack->ga_data)[stack->ga_len - 1];
|
|
||||||
// If the index is a string, the variable must be a Dict.
|
|
||||||
if (*typep == &t_any && valtype == &t_string)
|
|
||||||
vtype = VAR_DICT;
|
|
||||||
if (vtype == VAR_STRING || vtype == VAR_LIST || vtype == VAR_BLOB)
|
|
||||||
{
|
|
||||||
if (need_type(valtype, &t_number, -1, 0, cctx,
|
|
||||||
FALSE, FALSE) == FAIL)
|
|
||||||
return FAIL;
|
|
||||||
if (is_slice)
|
|
||||||
{
|
|
||||||
valtype = ((type_T **)stack->ga_data)[stack->ga_len - 2];
|
|
||||||
if (need_type(valtype, &t_number, -2, 0, cctx,
|
|
||||||
FALSE, FALSE) == FAIL)
|
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vtype == VAR_DICT)
|
|
||||||
{
|
|
||||||
if (is_slice)
|
|
||||||
{
|
|
||||||
emsg(_(e_cannot_slice_dictionary));
|
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
if ((*typep)->tt_type == VAR_DICT)
|
|
||||||
{
|
|
||||||
*typep = (*typep)->tt_member;
|
|
||||||
if (*typep == &t_unknown)
|
|
||||||
// empty dict was used
|
|
||||||
*typep = &t_any;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (need_type(*typep, &t_dict_any, -2, 0, cctx,
|
|
||||||
FALSE, FALSE) == FAIL)
|
|
||||||
return FAIL;
|
|
||||||
*typep = &t_any;
|
|
||||||
}
|
|
||||||
if (may_generate_2STRING(-1, cctx) == FAIL)
|
|
||||||
return FAIL;
|
|
||||||
if (generate_instr_drop(cctx, ISN_MEMBER, 1) == FAIL)
|
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
else if (vtype == VAR_STRING)
|
|
||||||
{
|
|
||||||
*typep = &t_string;
|
|
||||||
if ((is_slice
|
|
||||||
? generate_instr_drop(cctx, ISN_STRSLICE, 2)
|
|
||||||
: generate_instr_drop(cctx, ISN_STRINDEX, 1)) == FAIL)
|
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
else if (vtype == VAR_BLOB)
|
|
||||||
{
|
|
||||||
emsg("Sorry, blob index and slice not implemented yet");
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
|
||||||
else if (vtype == VAR_LIST || *typep == &t_any)
|
|
||||||
{
|
|
||||||
if (is_slice)
|
|
||||||
{
|
|
||||||
if (generate_instr_drop(cctx,
|
|
||||||
vtype == VAR_LIST ? ISN_LISTSLICE : ISN_ANYSLICE,
|
|
||||||
2) == FAIL)
|
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ((*typep)->tt_type == VAR_LIST)
|
|
||||||
{
|
|
||||||
*typep = (*typep)->tt_member;
|
|
||||||
if (*typep == &t_unknown)
|
|
||||||
// empty list was used
|
|
||||||
*typep = &t_any;
|
|
||||||
}
|
|
||||||
if (generate_instr_drop(cctx,
|
|
||||||
vtype == VAR_LIST ? ISN_LISTINDEX : ISN_ANYINDEX,
|
|
||||||
1) == FAIL)
|
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
emsg(_(e_string_list_dict_or_blob_required));
|
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (*p == '.' && p[1] != '.')
|
else if (*p == '.' && p[1] != '.')
|
||||||
{
|
{
|
||||||
@@ -5905,9 +5916,11 @@ compile_lhs(
|
|||||||
lhs->lhs_type = lhs->lhs_lvar->lv_type;
|
lhs->lhs_type = lhs->lhs_lvar->lv_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oplen == 3 && !heredoc && lhs->lhs_dest != dest_global
|
if (oplen == 3 && !heredoc
|
||||||
&& lhs->lhs_type->tt_type != VAR_STRING
|
&& lhs->lhs_dest != dest_global
|
||||||
&& lhs->lhs_type->tt_type != VAR_ANY)
|
&& !lhs->lhs_has_index
|
||||||
|
&& lhs->lhs_type->tt_type != VAR_STRING
|
||||||
|
&& lhs->lhs_type->tt_type != VAR_ANY)
|
||||||
{
|
{
|
||||||
emsg(_(e_can_only_concatenate_to_string));
|
emsg(_(e_can_only_concatenate_to_string));
|
||||||
return FAIL;
|
return FAIL;
|
||||||
@@ -5993,26 +6006,21 @@ compile_lhs(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Assignment to a list or dict member, or ":unlet" for the item, using the
|
* For an assignment with an index, compile the "idx" in "var[idx]" or "key" in
|
||||||
* information in "lhs".
|
* "var.key".
|
||||||
* Returns OK or FAIL.
|
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
compile_assign_unlet(
|
compile_assign_index(
|
||||||
char_u *var_start,
|
char_u *var_start,
|
||||||
lhs_T *lhs,
|
lhs_T *lhs,
|
||||||
int is_assign,
|
int is_assign,
|
||||||
type_T *rhs_type,
|
int *range,
|
||||||
cctx_T *cctx)
|
cctx_T *cctx)
|
||||||
{
|
{
|
||||||
char_u *p;
|
|
||||||
int r;
|
|
||||||
vartype_T dest_type;
|
|
||||||
size_t varlen = lhs->lhs_varlen;
|
size_t varlen = lhs->lhs_varlen;
|
||||||
garray_T *stack = &cctx->ctx_type_stack;
|
char_u *p;
|
||||||
int range = FALSE;
|
int r = OK;
|
||||||
|
|
||||||
// Compile the "idx" in "var[idx]" or "key" in "var.key".
|
|
||||||
p = var_start + varlen;
|
p = var_start + varlen;
|
||||||
if (*p == '[')
|
if (*p == '[')
|
||||||
{
|
{
|
||||||
@@ -6027,7 +6035,7 @@ compile_assign_unlet(
|
|||||||
semsg(_(e_cannot_use_range_with_assignment_str), p);
|
semsg(_(e_cannot_use_range_with_assignment_str), p);
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
range = TRUE;
|
*range = TRUE;
|
||||||
p = skipwhite(p);
|
p = skipwhite(p);
|
||||||
if (!IS_WHITE_OR_NUL(p[-1]) || !IS_WHITE_OR_NUL(p[1]))
|
if (!IS_WHITE_OR_NUL(p[-1]) || !IS_WHITE_OR_NUL(p[1]))
|
||||||
{
|
{
|
||||||
@@ -6053,7 +6061,29 @@ compile_assign_unlet(
|
|||||||
|
|
||||||
r = generate_PUSHS(cctx, key);
|
r = generate_PUSHS(cctx, key);
|
||||||
}
|
}
|
||||||
if (r == FAIL)
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Assignment to a list or dict member, or ":unlet" for the item, using the
|
||||||
|
* information in "lhs".
|
||||||
|
* Returns OK or FAIL.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
compile_assign_unlet(
|
||||||
|
char_u *var_start,
|
||||||
|
lhs_T *lhs,
|
||||||
|
int is_assign,
|
||||||
|
type_T *rhs_type,
|
||||||
|
cctx_T *cctx)
|
||||||
|
{
|
||||||
|
char_u *p;
|
||||||
|
vartype_T dest_type;
|
||||||
|
size_t varlen = lhs->lhs_varlen;
|
||||||
|
garray_T *stack = &cctx->ctx_type_stack;
|
||||||
|
int range = FALSE;
|
||||||
|
|
||||||
|
if (compile_assign_index(var_start, lhs, is_assign, &range, cctx) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
if (lhs->lhs_type == &t_any)
|
if (lhs->lhs_type == &t_any)
|
||||||
@@ -6330,9 +6360,17 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
|
|||||||
|
|
||||||
if (lhs.lhs_has_index)
|
if (lhs.lhs_has_index)
|
||||||
{
|
{
|
||||||
// TODO: get member from list or dict
|
int range = FALSE;
|
||||||
emsg("Index with operation not supported yet");
|
|
||||||
goto theend;
|
// Get member from list or dict. First compile the
|
||||||
|
// index value.
|
||||||
|
if (compile_assign_index(var_start, &lhs,
|
||||||
|
TRUE, &range, cctx) == FAIL)
|
||||||
|
goto theend;
|
||||||
|
|
||||||
|
// Get the member.
|
||||||
|
if (compile_member(FALSE, cctx) == FAIL)
|
||||||
|
goto theend;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user