mirror of
https://github.com/vim/vim.git
synced 2025-07-26 11:04:33 -04:00
patch 9.1.0450: evalc. code too complex
Problem: eval.c code too complex Solution: refactor eval6() and eval9() functions into several smaller functions (Yegappan Lakshmanan) closes: #14875 Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
parent
51024bbc1a
commit
51c45e89b5
27
src/dict.c
27
src/dict.c
@ -1091,6 +1091,33 @@ failret:
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Evaluate a literal dictionary: #{key: val, key: val}
|
||||
* "*arg" points to the "#".
|
||||
* On return, "*arg" points to the character after the Dict.
|
||||
* Return OK or FAIL. Returns NOTDONE for {expr}.
|
||||
*/
|
||||
int
|
||||
eval_lit_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
|
||||
{
|
||||
int vim9script = in_vim9script();
|
||||
int ret = OK;
|
||||
|
||||
if (vim9script)
|
||||
{
|
||||
ret = vim9_bad_comment(*arg) ? FAIL : NOTDONE;
|
||||
}
|
||||
else if ((*arg)[1] == '{')
|
||||
{
|
||||
++*arg;
|
||||
ret = eval_dict(arg, rettv, evalarg, TRUE);
|
||||
}
|
||||
else
|
||||
ret = NOTDONE;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Go over all entries in "d2" and add them to "d1".
|
||||
* When "action" is "error" then a duplicate key is an error.
|
||||
|
691
src/eval.c
691
src/eval.c
@ -1299,8 +1299,8 @@ typedef enum {
|
||||
*/
|
||||
static int
|
||||
get_lval_dict_item(
|
||||
char_u *name,
|
||||
lval_T *lp,
|
||||
char_u *name,
|
||||
char_u *key,
|
||||
int len,
|
||||
char_u **key_end,
|
||||
@ -1513,24 +1513,107 @@ get_lval_list(
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a Class or Object lval variable that can be assigned a value to:
|
||||
* "name", "name.key", "name.key[expr]" etc.
|
||||
* Get a class or object lval method in class "cl". The 'key' argument points
|
||||
* to the method name and 'key_end' points to the character after 'key'.
|
||||
* 'v_type' is VAR_CLASS or VAR_OBJECT.
|
||||
*
|
||||
* 'cl_exec' is the class that is executing, or NULL. 'v_type' is VAR_CLASS or
|
||||
* VAR_OBJECT. 'key' points to the member variable name and 'key_end' points
|
||||
* to the character after 'key'. If 'quiet' is TRUE, then error messages
|
||||
* are not displayed for invalid indexes.
|
||||
* The method index, method function pointer and method type are returned in
|
||||
* "lp".
|
||||
*/
|
||||
static void
|
||||
get_lval_oc_method(
|
||||
lval_T *lp,
|
||||
class_T *cl,
|
||||
char_u *key,
|
||||
char_u *key_end,
|
||||
vartype_T v_type)
|
||||
{
|
||||
// Look for a method with this name.
|
||||
// round 1: class functions (skipped for an object)
|
||||
// round 2: object methods
|
||||
for (int round = v_type == VAR_OBJECT ? 2 : 1; round <= 2; ++round)
|
||||
{
|
||||
int m_idx;
|
||||
ufunc_T *fp;
|
||||
|
||||
fp = method_lookup(cl, round == 1 ? VAR_CLASS : VAR_OBJECT,
|
||||
key, key_end - key, &m_idx);
|
||||
lp->ll_oi = m_idx;
|
||||
if (fp != NULL)
|
||||
{
|
||||
lp->ll_ufunc = fp;
|
||||
lp->ll_valtype = fp->uf_func_type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a class or object lval variable in class "cl". The "key" argument
|
||||
* points to the variable name and "key_end" points to the character after
|
||||
* "key". "v_type" is VAR_CLASS or VAR_OBJECT. "cl_exec" is the class that is
|
||||
* executing, or NULL.
|
||||
*
|
||||
* The variable index, typval and type are returned in "lp". Returns FAIL if
|
||||
* the variable is not writable. Otherwise returns OK.
|
||||
*/
|
||||
static int
|
||||
get_lval_oc_variable(
|
||||
lval_T *lp,
|
||||
class_T *cl,
|
||||
char_u *key,
|
||||
char_u *key_end,
|
||||
vartype_T v_type,
|
||||
class_T *cl_exec,
|
||||
int flags)
|
||||
{
|
||||
int m_idx;
|
||||
ocmember_T *om;
|
||||
|
||||
om = member_lookup(cl, v_type, key, key_end - key, &m_idx);
|
||||
lp->ll_oi = m_idx;
|
||||
if (om == NULL)
|
||||
return OK;
|
||||
|
||||
// Check variable is accessible
|
||||
if (get_lval_check_access(cl_exec, cl, om, key_end, flags) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
// When lhs is used to modify the variable, check it is not a read-only
|
||||
// variable.
|
||||
if ((flags & GLV_READ_ONLY) == 0 && (*key_end != '.' && *key_end != '[')
|
||||
&& oc_var_check_ro(cl, om))
|
||||
return FAIL;
|
||||
|
||||
lp->ll_valtype = om->ocm_type;
|
||||
|
||||
if (v_type == VAR_OBJECT)
|
||||
lp->ll_tv = ((typval_T *)(lp->ll_tv->vval.v_object + 1)) + m_idx;
|
||||
else
|
||||
lp->ll_tv = &cl->class_members_tv[m_idx];
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a Class or Object lval variable or method that can be assigned a value
|
||||
* to: "name", "name.key", "name.key[expr]" etc.
|
||||
*
|
||||
* The 'key' argument points to the member name and 'key_end' points to the
|
||||
* character after 'key'. 'v_type' is VAR_CLASS or VAR_OBJECT. 'cl_exec' is
|
||||
* the class that is executing, or NULL. If 'quiet' is TRUE, then error
|
||||
* messages are not displayed for invalid indexes.
|
||||
*
|
||||
* The Class or Object is returned in 'lp'. Returns OK on success and FAIL on
|
||||
* failure.
|
||||
*/
|
||||
static int
|
||||
get_lval_class_or_obj(
|
||||
class_T *cl_exec,
|
||||
vartype_T v_type,
|
||||
lval_T *lp,
|
||||
char_u *key,
|
||||
char_u *key_end,
|
||||
vartype_T v_type,
|
||||
class_T *cl_exec,
|
||||
int flags,
|
||||
int quiet)
|
||||
{
|
||||
@ -1556,69 +1639,27 @@ get_lval_class_or_obj(
|
||||
}
|
||||
lp->ll_class = cl;
|
||||
|
||||
// TODO: what if class is NULL?
|
||||
if (cl != NULL)
|
||||
if (cl == NULL)
|
||||
// TODO: what if class is NULL?
|
||||
return OK;
|
||||
|
||||
lp->ll_valtype = NULL;
|
||||
|
||||
if (flags & GLV_PREFER_FUNC)
|
||||
get_lval_oc_method(lp, cl, key, key_end, v_type);
|
||||
|
||||
// Look for object/class member variable
|
||||
if (lp->ll_valtype == NULL)
|
||||
{
|
||||
lp->ll_valtype = NULL;
|
||||
|
||||
if (flags & GLV_PREFER_FUNC)
|
||||
{
|
||||
// First look for a function with this name.
|
||||
// round 1: class functions (skipped for an object)
|
||||
// round 2: object methods
|
||||
for (int round = v_type == VAR_OBJECT ? 2 : 1;
|
||||
round <= 2; ++round)
|
||||
{
|
||||
int m_idx;
|
||||
ufunc_T *fp;
|
||||
|
||||
fp = method_lookup(cl,
|
||||
round == 1 ? VAR_CLASS : VAR_OBJECT,
|
||||
key, key_end - key, &m_idx);
|
||||
lp->ll_oi = m_idx;
|
||||
if (fp != NULL)
|
||||
{
|
||||
lp->ll_ufunc = fp;
|
||||
lp->ll_valtype = fp->uf_func_type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lp->ll_valtype == NULL)
|
||||
{
|
||||
int m_idx;
|
||||
ocmember_T *om
|
||||
= member_lookup(cl, v_type, key, key_end - key, &m_idx);
|
||||
lp->ll_oi = m_idx;
|
||||
if (om != NULL)
|
||||
{
|
||||
if (get_lval_check_access(cl_exec, cl, om,
|
||||
key_end, flags) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
// When lhs is used to modify the variable, check it is
|
||||
// not a read-only variable.
|
||||
if ((flags & GLV_READ_ONLY) == 0
|
||||
&& (*key_end != '.' && *key_end != '[')
|
||||
&& oc_var_check_ro(cl, om))
|
||||
return FAIL;
|
||||
|
||||
lp->ll_valtype = om->ocm_type;
|
||||
|
||||
if (v_type == VAR_OBJECT)
|
||||
lp->ll_tv = ((typval_T *)(
|
||||
lp->ll_tv->vval.v_object + 1)) + m_idx;
|
||||
else
|
||||
lp->ll_tv = &cl->class_members_tv[m_idx];
|
||||
}
|
||||
}
|
||||
|
||||
if (lp->ll_valtype == NULL)
|
||||
{
|
||||
member_not_found_msg(cl, v_type, key, key_end - key);
|
||||
if (get_lval_oc_variable(lp, cl, key, key_end, v_type, cl_exec, flags)
|
||||
== FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
if (lp->ll_valtype == NULL)
|
||||
{
|
||||
member_not_found_msg(cl, v_type, key, key_end - key);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
return OK;
|
||||
@ -1861,7 +1902,7 @@ get_lval_subscript(
|
||||
{
|
||||
glv_status_T glv_status;
|
||||
|
||||
glv_status = get_lval_dict_item(name, lp, key, len, &p, &var1,
|
||||
glv_status = get_lval_dict_item(lp, name, key, len, &p, &var1,
|
||||
flags, unlet, rettv);
|
||||
if (glv_status == GLV_FAIL)
|
||||
goto done;
|
||||
@ -1882,8 +1923,8 @@ get_lval_subscript(
|
||||
}
|
||||
else // v_type == VAR_CLASS || v_type == VAR_OBJECT
|
||||
{
|
||||
if (get_lval_class_or_obj(cl_exec, v_type, lp, key, p, flags,
|
||||
quiet) == FAIL)
|
||||
if (get_lval_class_or_obj(lp, key, p, v_type, cl_exec, flags,
|
||||
quiet) == FAIL)
|
||||
goto done;
|
||||
}
|
||||
|
||||
@ -4014,6 +4055,120 @@ eval5(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Concatenate strings "tv1" and "tv2" and store the result in "tv1".
|
||||
*/
|
||||
static int
|
||||
eval_concat_str(typval_T *tv1, typval_T *tv2)
|
||||
{
|
||||
char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN];
|
||||
char_u *s1 = tv_get_string_buf(tv1, buf1);
|
||||
char_u *s2 = NULL;
|
||||
char_u *p;
|
||||
int vim9script = in_vim9script();
|
||||
|
||||
if (vim9script && (tv2->v_type == VAR_VOID
|
||||
|| tv2->v_type == VAR_CHANNEL
|
||||
|| tv2->v_type == VAR_JOB))
|
||||
semsg(_(e_using_invalid_value_as_string_str),
|
||||
vartype_name(tv2->v_type));
|
||||
else if (vim9script && tv2->v_type == VAR_FLOAT)
|
||||
{
|
||||
vim_snprintf((char *)buf2, NUMBUFLEN, "%g",
|
||||
tv2->vval.v_float);
|
||||
s2 = buf2;
|
||||
}
|
||||
else
|
||||
s2 = tv_get_string_buf_chk(tv2, buf2);
|
||||
if (s2 == NULL) // type error ?
|
||||
{
|
||||
clear_tv(tv1);
|
||||
clear_tv(tv2);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
p = concat_str(s1, s2);
|
||||
clear_tv(tv1);
|
||||
tv1->v_type = VAR_STRING;
|
||||
tv1->vval.v_string = p;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add or subtract numbers "tv1" and "tv2" and store the result in "tv1".
|
||||
* The numbers can be whole numbers or floats.
|
||||
*/
|
||||
static int
|
||||
eval_addsub_num(typval_T *tv1, typval_T *tv2, int op)
|
||||
{
|
||||
int error = FALSE;
|
||||
varnumber_T n1, n2;
|
||||
float_T f1 = 0, f2 = 0;
|
||||
|
||||
if (tv1->v_type == VAR_FLOAT)
|
||||
{
|
||||
f1 = tv1->vval.v_float;
|
||||
n1 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
n1 = tv_get_number_chk(tv1, &error);
|
||||
if (error)
|
||||
{
|
||||
// This can only happen for "list + non-list" or
|
||||
// "blob + non-blob". For "non-list + ..." or
|
||||
// "something - ...", we returned before evaluating the
|
||||
// 2nd operand.
|
||||
clear_tv(tv1);
|
||||
clear_tv(tv2);
|
||||
return FAIL;
|
||||
}
|
||||
if (tv2->v_type == VAR_FLOAT)
|
||||
f1 = n1;
|
||||
}
|
||||
if (tv2->v_type == VAR_FLOAT)
|
||||
{
|
||||
f2 = tv2->vval.v_float;
|
||||
n2 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
n2 = tv_get_number_chk(tv2, &error);
|
||||
if (error)
|
||||
{
|
||||
clear_tv(tv1);
|
||||
clear_tv(tv2);
|
||||
return FAIL;
|
||||
}
|
||||
if (tv1->v_type == VAR_FLOAT)
|
||||
f2 = n2;
|
||||
}
|
||||
clear_tv(tv1);
|
||||
|
||||
// If there is a float on either side the result is a float.
|
||||
if (tv1->v_type == VAR_FLOAT || tv2->v_type == VAR_FLOAT)
|
||||
{
|
||||
if (op == '+')
|
||||
f1 = f1 + f2;
|
||||
else
|
||||
f1 = f1 - f2;
|
||||
tv1->v_type = VAR_FLOAT;
|
||||
tv1->vval.v_float = f1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (op == '+')
|
||||
n1 = n1 + n2;
|
||||
else
|
||||
n1 = n1 - n2;
|
||||
tv1->v_type = VAR_NUMBER;
|
||||
tv1->vval.v_number = n1;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle fifth level expression:
|
||||
* + number addition, concatenation of list or blob
|
||||
@ -4121,33 +4276,8 @@ eval6(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
|
||||
*/
|
||||
if (op == '.')
|
||||
{
|
||||
char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN];
|
||||
char_u *s1 = tv_get_string_buf(rettv, buf1);
|
||||
char_u *s2 = NULL;
|
||||
|
||||
if (vim9script && (var2.v_type == VAR_VOID
|
||||
|| var2.v_type == VAR_CHANNEL
|
||||
|| var2.v_type == VAR_JOB))
|
||||
semsg(_(e_using_invalid_value_as_string_str),
|
||||
vartype_name(var2.v_type));
|
||||
else if (vim9script && var2.v_type == VAR_FLOAT)
|
||||
{
|
||||
vim_snprintf((char *)buf2, NUMBUFLEN, "%g",
|
||||
var2.vval.v_float);
|
||||
s2 = buf2;
|
||||
}
|
||||
else
|
||||
s2 = tv_get_string_buf_chk(&var2, buf2);
|
||||
if (s2 == NULL) // type error ?
|
||||
{
|
||||
clear_tv(rettv);
|
||||
clear_tv(&var2);
|
||||
if (eval_concat_str(rettv, &var2) == FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
p = concat_str(s1, s2);
|
||||
clear_tv(rettv);
|
||||
rettv->v_type = VAR_STRING;
|
||||
rettv->vval.v_string = p;
|
||||
}
|
||||
else if (op == '+' && rettv->v_type == VAR_BLOB
|
||||
&& var2.v_type == VAR_BLOB)
|
||||
@ -4160,69 +4290,8 @@ eval6(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
|
||||
}
|
||||
else
|
||||
{
|
||||
int error = FALSE;
|
||||
varnumber_T n1, n2;
|
||||
float_T f1 = 0, f2 = 0;
|
||||
|
||||
if (rettv->v_type == VAR_FLOAT)
|
||||
{
|
||||
f1 = rettv->vval.v_float;
|
||||
n1 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
n1 = tv_get_number_chk(rettv, &error);
|
||||
if (error)
|
||||
{
|
||||
// This can only happen for "list + non-list" or
|
||||
// "blob + non-blob". For "non-list + ..." or
|
||||
// "something - ...", we returned before evaluating the
|
||||
// 2nd operand.
|
||||
clear_tv(rettv);
|
||||
clear_tv(&var2);
|
||||
return FAIL;
|
||||
}
|
||||
if (var2.v_type == VAR_FLOAT)
|
||||
f1 = n1;
|
||||
}
|
||||
if (var2.v_type == VAR_FLOAT)
|
||||
{
|
||||
f2 = var2.vval.v_float;
|
||||
n2 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
n2 = tv_get_number_chk(&var2, &error);
|
||||
if (error)
|
||||
{
|
||||
clear_tv(rettv);
|
||||
clear_tv(&var2);
|
||||
return FAIL;
|
||||
}
|
||||
if (rettv->v_type == VAR_FLOAT)
|
||||
f2 = n2;
|
||||
}
|
||||
clear_tv(rettv);
|
||||
|
||||
// If there is a float on either side the result is a float.
|
||||
if (rettv->v_type == VAR_FLOAT || var2.v_type == VAR_FLOAT)
|
||||
{
|
||||
if (op == '+')
|
||||
f1 = f1 + f2;
|
||||
else
|
||||
f1 = f1 - f2;
|
||||
rettv->v_type = VAR_FLOAT;
|
||||
rettv->vval.v_float = f1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (op == '+')
|
||||
n1 = n1 + n2;
|
||||
else
|
||||
n1 = n1 - n2;
|
||||
rettv->v_type = VAR_NUMBER;
|
||||
rettv->vval.v_number = n1;
|
||||
}
|
||||
if (eval_addsub_num(rettv, &var2, op) == FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
clear_tv(&var2);
|
||||
}
|
||||
@ -4623,6 +4692,158 @@ handle_predefined(char_u *s, int len, typval_T *rettv)
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle register contents: @r.
|
||||
*/
|
||||
static void
|
||||
eval9_reg_contents(
|
||||
char_u **arg,
|
||||
typval_T *rettv,
|
||||
int evaluate)
|
||||
{
|
||||
int vim9script = in_vim9script();
|
||||
|
||||
++*arg; // skip '@'
|
||||
if (evaluate)
|
||||
{
|
||||
if (vim9script && IS_WHITE_OR_NUL(**arg))
|
||||
semsg(_(e_syntax_error_at_str), *arg);
|
||||
else if (vim9script && !valid_yank_reg(**arg, FALSE))
|
||||
emsg_invreg(**arg);
|
||||
else
|
||||
{
|
||||
rettv->v_type = VAR_STRING;
|
||||
rettv->vval.v_string = get_reg_contents(**arg,
|
||||
GREG_EXPR_SRC);
|
||||
}
|
||||
}
|
||||
if (**arg != NUL)
|
||||
++*arg;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle a nested expression: (expression) or lambda: (arg) => expr
|
||||
*/
|
||||
static int
|
||||
eval9_nested_expr(
|
||||
char_u **arg,
|
||||
typval_T *rettv,
|
||||
evalarg_T *evalarg,
|
||||
int evaluate)
|
||||
{
|
||||
int ret = NOTDONE;
|
||||
int vim9script = in_vim9script();
|
||||
|
||||
if (vim9script)
|
||||
{
|
||||
ret = get_lambda_tv(arg, rettv, TRUE, evalarg);
|
||||
if (ret == OK && evaluate)
|
||||
{
|
||||
ufunc_T *ufunc = rettv->vval.v_partial->pt_func;
|
||||
|
||||
// Compile it here to get the return type. The return
|
||||
// type is optional, when it's missing use t_unknown.
|
||||
// This is recognized in compile_return().
|
||||
if (ufunc->uf_ret_type->tt_type == VAR_VOID)
|
||||
ufunc->uf_ret_type = &t_unknown;
|
||||
if (compile_def_function(ufunc, FALSE,
|
||||
get_compile_type(ufunc), NULL) == FAIL)
|
||||
{
|
||||
clear_tv(rettv);
|
||||
ret = FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ret == NOTDONE)
|
||||
{
|
||||
*arg = skipwhite_and_linebreak(*arg + 1, evalarg);
|
||||
ret = eval1(arg, rettv, evalarg); // recursive!
|
||||
|
||||
*arg = skipwhite_and_linebreak(*arg, evalarg);
|
||||
if (**arg == ')')
|
||||
++*arg;
|
||||
else if (ret == OK)
|
||||
{
|
||||
emsg(_(e_missing_closing_paren));
|
||||
clear_tv(rettv);
|
||||
ret = FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle be a variable or function name.
|
||||
* Can also be a curly-braces kind of name: {expr}.
|
||||
*/
|
||||
static int
|
||||
eval9_var_func_name(
|
||||
char_u **arg,
|
||||
typval_T *rettv,
|
||||
evalarg_T *evalarg,
|
||||
int evaluate,
|
||||
char_u **name_start)
|
||||
{
|
||||
char_u *s;
|
||||
int len;
|
||||
char_u *alias;
|
||||
int ret = OK;
|
||||
int vim9script = in_vim9script();
|
||||
|
||||
s = *arg;
|
||||
len = get_name_len(arg, &alias, evaluate, TRUE);
|
||||
if (alias != NULL)
|
||||
s = alias;
|
||||
|
||||
if (len <= 0)
|
||||
ret = FAIL;
|
||||
else
|
||||
{
|
||||
int flags = evalarg == NULL ? 0 : evalarg->eval_flags;
|
||||
|
||||
if (evaluate && vim9script && len == 1 && *s == '_')
|
||||
{
|
||||
emsg(_(e_cannot_use_underscore_here));
|
||||
ret = FAIL;
|
||||
}
|
||||
else if (evaluate && vim9script && len > 2
|
||||
&& s[0] == 's' && s[1] == ':')
|
||||
{
|
||||
semsg(_(e_cannot_use_s_colon_in_vim9_script_str), s);
|
||||
ret = FAIL;
|
||||
}
|
||||
else if ((vim9script ? **arg : *skipwhite(*arg)) == '(')
|
||||
{
|
||||
// "name(..." recursive!
|
||||
*arg = skipwhite(*arg);
|
||||
ret = eval_func(arg, evalarg, s, len, rettv, flags, NULL);
|
||||
}
|
||||
else if (evaluate)
|
||||
{
|
||||
// get the value of "true", "false", etc. or a variable
|
||||
ret = FAIL;
|
||||
if (vim9script)
|
||||
ret = handle_predefined(s, len, rettv);
|
||||
if (ret == FAIL)
|
||||
{
|
||||
*name_start = s;
|
||||
ret = eval_variable(s, len, 0, rettv, NULL,
|
||||
EVAL_VAR_VERBOSE + EVAL_VAR_IMPORT);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// skip the name
|
||||
check_vars(s, len);
|
||||
ret = OK;
|
||||
}
|
||||
}
|
||||
vim_free(alias);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle sixth level expression:
|
||||
* number number constant
|
||||
@ -4662,12 +4883,9 @@ eval9(
|
||||
{
|
||||
int evaluate = evalarg != NULL
|
||||
&& (evalarg->eval_flags & EVAL_EVALUATE);
|
||||
int len;
|
||||
char_u *s;
|
||||
char_u *name_start = NULL;
|
||||
char_u *start_leader, *end_leader;
|
||||
int ret = OK;
|
||||
char_u *alias;
|
||||
static int recurse = 0;
|
||||
int vim9script = in_vim9script();
|
||||
|
||||
@ -4750,19 +4968,9 @@ eval9(
|
||||
break;
|
||||
|
||||
/*
|
||||
* Dictionary: #{key: val, key: val}
|
||||
* Literal Dictionary: #{key: val, key: val}
|
||||
*/
|
||||
case '#': if (vim9script)
|
||||
{
|
||||
ret = vim9_bad_comment(*arg) ? FAIL : NOTDONE;
|
||||
}
|
||||
else if ((*arg)[1] == '{')
|
||||
{
|
||||
++*arg;
|
||||
ret = eval_dict(arg, rettv, evalarg, TRUE);
|
||||
}
|
||||
else
|
||||
ret = NOTDONE;
|
||||
case '#': ret = eval_lit_dict(arg, rettv, evalarg);
|
||||
break;
|
||||
|
||||
/*
|
||||
@ -4796,64 +5004,14 @@ eval9(
|
||||
/*
|
||||
* Register contents: @r.
|
||||
*/
|
||||
case '@': ++*arg;
|
||||
if (evaluate)
|
||||
{
|
||||
if (vim9script && IS_WHITE_OR_NUL(**arg))
|
||||
semsg(_(e_syntax_error_at_str), *arg);
|
||||
else if (vim9script && !valid_yank_reg(**arg, FALSE))
|
||||
emsg_invreg(**arg);
|
||||
else
|
||||
{
|
||||
rettv->v_type = VAR_STRING;
|
||||
rettv->vval.v_string = get_reg_contents(**arg,
|
||||
GREG_EXPR_SRC);
|
||||
}
|
||||
}
|
||||
if (**arg != NUL)
|
||||
++*arg;
|
||||
case '@': eval9_reg_contents(arg, rettv, evaluate);
|
||||
break;
|
||||
|
||||
/*
|
||||
* nested expression: (expression).
|
||||
* or lambda: (arg) => expr
|
||||
*/
|
||||
case '(': ret = NOTDONE;
|
||||
if (vim9script)
|
||||
{
|
||||
ret = get_lambda_tv(arg, rettv, TRUE, evalarg);
|
||||
if (ret == OK && evaluate)
|
||||
{
|
||||
ufunc_T *ufunc = rettv->vval.v_partial->pt_func;
|
||||
|
||||
// Compile it here to get the return type. The return
|
||||
// type is optional, when it's missing use t_unknown.
|
||||
// This is recognized in compile_return().
|
||||
if (ufunc->uf_ret_type->tt_type == VAR_VOID)
|
||||
ufunc->uf_ret_type = &t_unknown;
|
||||
if (compile_def_function(ufunc, FALSE,
|
||||
get_compile_type(ufunc), NULL) == FAIL)
|
||||
{
|
||||
clear_tv(rettv);
|
||||
ret = FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ret == NOTDONE)
|
||||
{
|
||||
*arg = skipwhite_and_linebreak(*arg + 1, evalarg);
|
||||
ret = eval1(arg, rettv, evalarg); // recursive!
|
||||
|
||||
*arg = skipwhite_and_linebreak(*arg, evalarg);
|
||||
if (**arg == ')')
|
||||
++*arg;
|
||||
else if (ret == OK)
|
||||
{
|
||||
emsg(_(e_missing_closing_paren));
|
||||
clear_tv(rettv);
|
||||
ret = FAIL;
|
||||
}
|
||||
}
|
||||
case '(': ret = eval9_nested_expr(arg, rettv, evalarg, evaluate);
|
||||
break;
|
||||
|
||||
default: ret = NOTDONE;
|
||||
@ -4866,55 +5024,7 @@ eval9(
|
||||
* Must be a variable or function name.
|
||||
* Can also be a curly-braces kind of name: {expr}.
|
||||
*/
|
||||
s = *arg;
|
||||
len = get_name_len(arg, &alias, evaluate, TRUE);
|
||||
if (alias != NULL)
|
||||
s = alias;
|
||||
|
||||
if (len <= 0)
|
||||
ret = FAIL;
|
||||
else
|
||||
{
|
||||
int flags = evalarg == NULL ? 0 : evalarg->eval_flags;
|
||||
|
||||
if (evaluate && vim9script && len == 1 && *s == '_')
|
||||
{
|
||||
emsg(_(e_cannot_use_underscore_here));
|
||||
ret = FAIL;
|
||||
}
|
||||
else if (evaluate && vim9script && len > 2
|
||||
&& s[0] == 's' && s[1] == ':')
|
||||
{
|
||||
semsg(_(e_cannot_use_s_colon_in_vim9_script_str), s);
|
||||
ret = FAIL;
|
||||
}
|
||||
else if ((vim9script ? **arg : *skipwhite(*arg)) == '(')
|
||||
{
|
||||
// "name(..." recursive!
|
||||
*arg = skipwhite(*arg);
|
||||
ret = eval_func(arg, evalarg, s, len, rettv, flags, NULL);
|
||||
}
|
||||
else if (evaluate)
|
||||
{
|
||||
// get the value of "true", "false", etc. or a variable
|
||||
ret = FAIL;
|
||||
if (vim9script)
|
||||
ret = handle_predefined(s, len, rettv);
|
||||
if (ret == FAIL)
|
||||
{
|
||||
name_start = s;
|
||||
ret = eval_variable(s, len, 0, rettv, NULL,
|
||||
EVAL_VAR_VERBOSE + EVAL_VAR_IMPORT);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// skip the name
|
||||
check_vars(s, len);
|
||||
ret = OK;
|
||||
}
|
||||
}
|
||||
vim_free(alias);
|
||||
ret = eval9_var_func_name(arg, rettv, evalarg, evaluate, &name_start);
|
||||
}
|
||||
|
||||
// Handle following '[', '(' and '.' for expr[expr], expr.name,
|
||||
@ -5761,32 +5871,27 @@ func_tv2string(typval_T *tv, char_u **tofree, int echo_style)
|
||||
|
||||
if (echo_style)
|
||||
{
|
||||
*tofree = NULL;
|
||||
|
||||
if (tv->vval.v_string == NULL)
|
||||
{
|
||||
r = (char_u *)"function()";
|
||||
*tofree = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
r = make_ufunc_name_readable(tv->vval.v_string, buf,
|
||||
MAX_FUNC_NAME_LEN);
|
||||
if (r == buf)
|
||||
{
|
||||
r = vim_strsave(buf);
|
||||
*tofree = r;
|
||||
}
|
||||
else
|
||||
*tofree = NULL;
|
||||
r = *tofree = vim_strsave(buf);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tv->vval.v_string == NULL)
|
||||
*tofree = string_quote(NULL, TRUE);
|
||||
else
|
||||
*tofree = string_quote(make_ufunc_name_readable(tv->vval.v_string,
|
||||
buf, MAX_FUNC_NAME_LEN), TRUE);
|
||||
r = *tofree;
|
||||
char_u *s = NULL;
|
||||
|
||||
if (tv->vval.v_string != NULL)
|
||||
s = make_ufunc_name_readable(tv->vval.v_string, buf,
|
||||
MAX_FUNC_NAME_LEN);
|
||||
|
||||
r = *tofree = string_quote(s, TRUE);
|
||||
}
|
||||
|
||||
return r;
|
||||
|
@ -37,6 +37,7 @@ varnumber_T dict_get_bool(dict_T *d, char *key, int def);
|
||||
char_u *dict2string(typval_T *tv, int copyID, int restore_copyID);
|
||||
char_u *get_literal_key(char_u **arg);
|
||||
int eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal);
|
||||
int eval_lit_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
|
||||
void dict_extend(dict_T *d1, dict_T *d2, char_u *action, char *func_name);
|
||||
dictitem_T *dict_lookup(hashitem_T *hi);
|
||||
int dict_equal(dict_T *d1, dict_T *d2, int ic, int recursive);
|
||||
|
@ -704,6 +704,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
450,
|
||||
/**/
|
||||
449,
|
||||
/**/
|
||||
|
Loading…
x
Reference in New Issue
Block a user