mirror of
https://github.com/vim/vim.git
synced 2025-09-24 03:44:06 -04:00
patch 8.2.0708: Vim9: constant expressions are not simplified
Problem: Vim9: constant expressions are not simplified. Solution: Simplify string concatenation.
This commit is contained in:
@@ -996,9 +996,7 @@ def Test_disassemble_echomsg()
|
|||||||
'\d PUSHS "message".*' ..
|
'\d PUSHS "message".*' ..
|
||||||
'\d ECHOMSG 2.*' ..
|
'\d ECHOMSG 2.*' ..
|
||||||
"echoerr 'went' .. 'wrong'.*" ..
|
"echoerr 'went' .. 'wrong'.*" ..
|
||||||
'\d PUSHS "went".*' ..
|
'\d PUSHS "wentwrong".*' ..
|
||||||
'\d PUSHS "wrong".*' ..
|
|
||||||
'\d CONCAT.*' ..
|
|
||||||
'\d ECHOERR 1.*' ..
|
'\d ECHOERR 1.*' ..
|
||||||
'\d PUSHNR 0.*' ..
|
'\d PUSHNR 0.*' ..
|
||||||
'\d RETURN',
|
'\d RETURN',
|
||||||
@@ -1037,4 +1035,16 @@ def Test_display_func()
|
|||||||
res3)
|
res3)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
def s:ConcatStrings(): string
|
||||||
|
return 'one' .. 'two' .. 'three'
|
||||||
|
enddef
|
||||||
|
|
||||||
|
def Test_simplify_const_expr()
|
||||||
|
let res = execute('disass s:ConcatStrings')
|
||||||
|
assert_match('\<SNR>\d*_ConcatStrings.*' ..
|
||||||
|
'\d PUSHS "onetwothree".*' ..
|
||||||
|
'\d RETURN',
|
||||||
|
res)
|
||||||
|
enddef
|
||||||
|
|
||||||
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
|
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
|
||||||
|
@@ -485,8 +485,10 @@ def Test_expr5()
|
|||||||
assert_equal(-6, g:alsoint - g:anint)
|
assert_equal(-6, g:alsoint - g:anint)
|
||||||
|
|
||||||
assert_equal('hello', 'hel' .. 'lo')
|
assert_equal('hello', 'hel' .. 'lo')
|
||||||
assert_equal('hello 123', 'hello ' ..
|
" TODO: a line break here doesn't work
|
||||||
123)
|
" assert_equal('hello 123', 'hello ' ..
|
||||||
|
" 123)
|
||||||
|
assert_equal('hello 123', 'hello ' .. 123)
|
||||||
assert_equal('123 hello', 123 .. ' hello')
|
assert_equal('123 hello', 123 .. ' hello')
|
||||||
assert_equal('123456', 123 .. 456)
|
assert_equal('123456', 123 .. 456)
|
||||||
|
|
||||||
|
@@ -746,6 +746,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 */
|
||||||
|
/**/
|
||||||
|
708,
|
||||||
/**/
|
/**/
|
||||||
707,
|
707,
|
||||||
/**/
|
/**/
|
||||||
|
@@ -1040,6 +1040,43 @@ generate_PUSHFUNC(cctx_T *cctx, char_u *name, type_T *type)
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generate a PUSH instruction for "tv".
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
generate_tv_PUSH(cctx_T *cctx, typval_T *tv)
|
||||||
|
{
|
||||||
|
switch (tv->v_type)
|
||||||
|
{
|
||||||
|
case VAR_BOOL:
|
||||||
|
generate_PUSHBOOL(cctx, tv->vval.v_number);
|
||||||
|
break;
|
||||||
|
case VAR_SPECIAL:
|
||||||
|
generate_PUSHSPEC(cctx, tv->vval.v_number);
|
||||||
|
break;
|
||||||
|
case VAR_NUMBER:
|
||||||
|
generate_PUSHNR(cctx, tv->vval.v_number);
|
||||||
|
break;
|
||||||
|
#ifdef FEAT_FLOAT
|
||||||
|
case VAR_FLOAT:
|
||||||
|
generate_PUSHF(cctx, tv->vval.v_float);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case VAR_BLOB:
|
||||||
|
generate_PUSHBLOB(cctx, tv->vval.v_blob);
|
||||||
|
tv->vval.v_blob = NULL;
|
||||||
|
break;
|
||||||
|
case VAR_STRING:
|
||||||
|
generate_PUSHS(cctx, tv->vval.v_string);
|
||||||
|
tv->vval.v_string = NULL;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
iemsg("constant type not supported");
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generate an ISN_STORE instruction.
|
* Generate an ISN_STORE instruction.
|
||||||
*/
|
*/
|
||||||
@@ -3180,6 +3217,452 @@ get_vim_constant(char_u **arg, typval_T *rettv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Evaluate an expression that is a constant:
|
||||||
|
* has(arg)
|
||||||
|
*
|
||||||
|
* Also handle:
|
||||||
|
* ! in front logical NOT
|
||||||
|
*
|
||||||
|
* Return FAIL if the expression is not a constant.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
evaluate_const_expr7(char_u **arg, cctx_T *cctx UNUSED, typval_T *tv)
|
||||||
|
{
|
||||||
|
typval_T argvars[2];
|
||||||
|
char_u *start_leader, *end_leader;
|
||||||
|
int has_call = FALSE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Skip '!' characters. They are handled later.
|
||||||
|
* TODO: '-' and '+' characters
|
||||||
|
*/
|
||||||
|
start_leader = *arg;
|
||||||
|
while (**arg == '!')
|
||||||
|
*arg = skipwhite(*arg + 1);
|
||||||
|
end_leader = *arg;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Recognize only a few types of constants for now.
|
||||||
|
*/
|
||||||
|
if (STRNCMP("true", *arg, 4) == 0 && !ASCII_ISALNUM((*arg)[4]))
|
||||||
|
{
|
||||||
|
tv->v_type = VAR_BOOL;
|
||||||
|
tv->vval.v_number = VVAL_TRUE;
|
||||||
|
*arg += 4;
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
if (STRNCMP("false", *arg, 5) == 0 && !ASCII_ISALNUM((*arg)[5]))
|
||||||
|
{
|
||||||
|
tv->v_type = VAR_BOOL;
|
||||||
|
tv->vval.v_number = VVAL_FALSE;
|
||||||
|
*arg += 5;
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (STRNCMP("has(", *arg, 4) == 0)
|
||||||
|
{
|
||||||
|
has_call = TRUE;
|
||||||
|
*arg = skipwhite(*arg + 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (**arg == '"')
|
||||||
|
{
|
||||||
|
if (get_string_tv(arg, tv, TRUE) == FAIL)
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
else if (**arg == '\'')
|
||||||
|
{
|
||||||
|
if (get_lit_string_tv(arg, tv, TRUE) == FAIL)
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
if (has_call)
|
||||||
|
{
|
||||||
|
*arg = skipwhite(*arg);
|
||||||
|
if (**arg != ')')
|
||||||
|
return FAIL;
|
||||||
|
*arg = *arg + 1;
|
||||||
|
|
||||||
|
argvars[0] = *tv;
|
||||||
|
argvars[1].v_type = VAR_UNKNOWN;
|
||||||
|
tv->v_type = VAR_NUMBER;
|
||||||
|
tv->vval.v_number = 0;
|
||||||
|
f_has(argvars, tv);
|
||||||
|
clear_tv(&argvars[0]);
|
||||||
|
|
||||||
|
while (start_leader < end_leader)
|
||||||
|
{
|
||||||
|
if (*start_leader == '!')
|
||||||
|
tv->vval.v_number = !tv->vval.v_number;
|
||||||
|
++start_leader;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (end_leader > start_leader)
|
||||||
|
{
|
||||||
|
clear_tv(tv);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* * number multiplication
|
||||||
|
* / number division
|
||||||
|
* % number modulo
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
evaluate_const_expr6(char_u **arg, cctx_T *cctx, typval_T *tv)
|
||||||
|
{
|
||||||
|
char_u *op;
|
||||||
|
|
||||||
|
// get the first variable
|
||||||
|
if (evaluate_const_expr7(arg, cctx, tv) == FAIL)
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Repeat computing, until no "*", "/" or "%" is following.
|
||||||
|
*/
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
op = skipwhite(*arg);
|
||||||
|
if (*op != '*' && *op != '/' && *op != '%')
|
||||||
|
break;
|
||||||
|
// TODO: not implemented yet.
|
||||||
|
clear_tv(tv);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* + number addition
|
||||||
|
* - number subtraction
|
||||||
|
* .. string concatenation
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
evaluate_const_expr5(char_u **arg, cctx_T *cctx, typval_T *tv)
|
||||||
|
{
|
||||||
|
char_u *op;
|
||||||
|
int oplen;
|
||||||
|
|
||||||
|
// get the first variable
|
||||||
|
if (evaluate_const_expr6(arg, cctx, tv) == FAIL)
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Repeat computing, until no "+", "-" or ".." is following.
|
||||||
|
*/
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
op = skipwhite(*arg);
|
||||||
|
if (*op != '+' && *op != '-' && !(*op == '.' && (*(*arg + 1) == '.')))
|
||||||
|
break;
|
||||||
|
oplen = (*op == '.' ? 2 : 1);
|
||||||
|
|
||||||
|
if (!IS_WHITE_OR_NUL(**arg) || !IS_WHITE_OR_NUL(op[oplen]))
|
||||||
|
{
|
||||||
|
clear_tv(tv);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*op == '.' && tv->v_type == VAR_STRING)
|
||||||
|
{
|
||||||
|
typval_T tv2;
|
||||||
|
size_t len1;
|
||||||
|
char_u *s1, *s2;
|
||||||
|
|
||||||
|
tv2.v_type = VAR_UNKNOWN;
|
||||||
|
*arg = skipwhite(op + oplen);
|
||||||
|
|
||||||
|
// TODO: what if we fail???
|
||||||
|
if (may_get_next_line(op + oplen, arg, cctx) == FAIL)
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
// get the second variable
|
||||||
|
if (evaluate_const_expr6(arg, cctx, &tv2) == FAIL)
|
||||||
|
{
|
||||||
|
clear_tv(tv);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
if (tv2.v_type != VAR_STRING)
|
||||||
|
{
|
||||||
|
clear_tv(tv);
|
||||||
|
clear_tv(&tv2);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
s1 = tv->vval.v_string;
|
||||||
|
len1 = STRLEN(s1);
|
||||||
|
s2 = tv2.vval.v_string;
|
||||||
|
tv->vval.v_string = alloc((int)(len1 + STRLEN(s2) + 1));
|
||||||
|
if (tv->vval.v_string == NULL)
|
||||||
|
{
|
||||||
|
vim_free(s1);
|
||||||
|
vim_free(s2);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
mch_memmove(tv->vval.v_string, s1, len1);
|
||||||
|
STRCPY(tv->vval.v_string + len1, s2);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Not implemented yet.
|
||||||
|
clear_tv(tv);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static exptype_T
|
||||||
|
get_compare_type(char_u *p, int *len, int *type_is)
|
||||||
|
{
|
||||||
|
exptype_T type = EXPR_UNKNOWN;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
switch (p[0])
|
||||||
|
{
|
||||||
|
case '=': if (p[1] == '=')
|
||||||
|
type = EXPR_EQUAL;
|
||||||
|
else if (p[1] == '~')
|
||||||
|
type = EXPR_MATCH;
|
||||||
|
break;
|
||||||
|
case '!': if (p[1] == '=')
|
||||||
|
type = EXPR_NEQUAL;
|
||||||
|
else if (p[1] == '~')
|
||||||
|
type = EXPR_NOMATCH;
|
||||||
|
break;
|
||||||
|
case '>': if (p[1] != '=')
|
||||||
|
{
|
||||||
|
type = EXPR_GREATER;
|
||||||
|
*len = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
type = EXPR_GEQUAL;
|
||||||
|
break;
|
||||||
|
case '<': if (p[1] != '=')
|
||||||
|
{
|
||||||
|
type = EXPR_SMALLER;
|
||||||
|
*len = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
type = EXPR_SEQUAL;
|
||||||
|
break;
|
||||||
|
case 'i': if (p[1] == 's')
|
||||||
|
{
|
||||||
|
// "is" and "isnot"; but not a prefix of a name
|
||||||
|
if (p[2] == 'n' && p[3] == 'o' && p[4] == 't')
|
||||||
|
*len = 5;
|
||||||
|
i = p[*len];
|
||||||
|
if (!isalnum(i) && i != '_')
|
||||||
|
{
|
||||||
|
type = *len == 2 ? EXPR_IS : EXPR_ISNOT;
|
||||||
|
*type_is = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only comparing strings is supported right now.
|
||||||
|
* expr5a == expr5b
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
evaluate_const_expr4(char_u **arg, cctx_T *cctx UNUSED, typval_T *tv)
|
||||||
|
{
|
||||||
|
exptype_T type = EXPR_UNKNOWN;
|
||||||
|
char_u *p;
|
||||||
|
int len = 2;
|
||||||
|
int type_is = FALSE;
|
||||||
|
|
||||||
|
// get the first variable
|
||||||
|
if (evaluate_const_expr5(arg, cctx, tv) == FAIL)
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
p = skipwhite(*arg);
|
||||||
|
type = get_compare_type(p, &len, &type_is);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If there is a comparative operator, use it.
|
||||||
|
*/
|
||||||
|
if (type != EXPR_UNKNOWN)
|
||||||
|
{
|
||||||
|
typval_T tv2;
|
||||||
|
char_u *s1, *s2;
|
||||||
|
char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN];
|
||||||
|
int n;
|
||||||
|
|
||||||
|
// TODO: Only string == string is supported now
|
||||||
|
if (tv->v_type != VAR_STRING)
|
||||||
|
return FAIL;
|
||||||
|
if (type != EXPR_EQUAL)
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
// get the second variable
|
||||||
|
init_tv(&tv2);
|
||||||
|
*arg = skipwhite(p + len);
|
||||||
|
if (evaluate_const_expr5(arg, cctx, &tv2) == FAIL
|
||||||
|
|| tv2.v_type != VAR_STRING)
|
||||||
|
{
|
||||||
|
clear_tv(&tv2);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
s1 = tv_get_string_buf(tv, buf1);
|
||||||
|
s2 = tv_get_string_buf(&tv2, buf2);
|
||||||
|
n = STRCMP(s1, s2);
|
||||||
|
clear_tv(tv);
|
||||||
|
clear_tv(&tv2);
|
||||||
|
tv->v_type = VAR_BOOL;
|
||||||
|
tv->vval.v_number = n == 0 ? VVAL_TRUE : VVAL_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int evaluate_const_expr3(char_u **arg, cctx_T *cctx, typval_T *tv);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compile constant || or &&.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
evaluate_const_and_or(char_u **arg, cctx_T *cctx, char *op, typval_T *tv)
|
||||||
|
{
|
||||||
|
char_u *p = skipwhite(*arg);
|
||||||
|
int opchar = *op;
|
||||||
|
|
||||||
|
if (p[0] == opchar && p[1] == opchar)
|
||||||
|
{
|
||||||
|
int val = tv2bool(tv);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Repeat until there is no following "||" or "&&"
|
||||||
|
*/
|
||||||
|
while (p[0] == opchar && p[1] == opchar)
|
||||||
|
{
|
||||||
|
typval_T tv2;
|
||||||
|
|
||||||
|
if (!VIM_ISWHITE(**arg) || !VIM_ISWHITE(p[2]))
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
// eval the next expression
|
||||||
|
*arg = skipwhite(p + 2);
|
||||||
|
tv2.v_type = VAR_UNKNOWN;
|
||||||
|
tv2.v_lock = 0;
|
||||||
|
if ((opchar == '|' ? evaluate_const_expr3(arg, cctx, &tv2)
|
||||||
|
: evaluate_const_expr4(arg, cctx, &tv2)) == FAIL)
|
||||||
|
{
|
||||||
|
clear_tv(&tv2);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
if ((opchar == '&') == val)
|
||||||
|
{
|
||||||
|
// false || tv2 or true && tv2: use tv2
|
||||||
|
clear_tv(tv);
|
||||||
|
*tv = tv2;
|
||||||
|
val = tv2bool(tv);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
clear_tv(&tv2);
|
||||||
|
p = skipwhite(*arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Evaluate an expression that is a constant: expr4 && expr4 && expr4
|
||||||
|
* Return FAIL if the expression is not a constant.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
evaluate_const_expr3(char_u **arg, cctx_T *cctx, typval_T *tv)
|
||||||
|
{
|
||||||
|
// evaluate the first expression
|
||||||
|
if (evaluate_const_expr4(arg, cctx, tv) == FAIL)
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
// || and && work almost the same
|
||||||
|
return evaluate_const_and_or(arg, cctx, "&&", tv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Evaluate an expression that is a constant: expr3 || expr3 || expr3
|
||||||
|
* Return FAIL if the expression is not a constant.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
evaluate_const_expr2(char_u **arg, cctx_T *cctx, typval_T *tv)
|
||||||
|
{
|
||||||
|
// evaluate the first expression
|
||||||
|
if (evaluate_const_expr3(arg, cctx, tv) == FAIL)
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
// || and && work almost the same
|
||||||
|
return evaluate_const_and_or(arg, cctx, "||", tv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Evaluate an expression that is a constant: expr2 ? expr1 : expr1
|
||||||
|
* E.g. for "has('feature')".
|
||||||
|
* This does not produce error messages. "tv" should be cleared afterwards.
|
||||||
|
* Return FAIL if the expression is not a constant.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
evaluate_const_expr1(char_u **arg, cctx_T *cctx, typval_T *tv)
|
||||||
|
{
|
||||||
|
char_u *p;
|
||||||
|
|
||||||
|
// evaluate the first expression
|
||||||
|
if (evaluate_const_expr2(arg, cctx, tv) == FAIL)
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
p = skipwhite(*arg);
|
||||||
|
if (*p == '?')
|
||||||
|
{
|
||||||
|
int val = tv2bool(tv);
|
||||||
|
typval_T tv2;
|
||||||
|
|
||||||
|
// require space before and after the ?
|
||||||
|
if (!VIM_ISWHITE(**arg) || !VIM_ISWHITE(p[1]))
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
// evaluate the second expression; any type is accepted
|
||||||
|
clear_tv(tv);
|
||||||
|
*arg = skipwhite(p + 1);
|
||||||
|
if (evaluate_const_expr1(arg, cctx, tv) == FAIL)
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
// Check for the ":".
|
||||||
|
p = skipwhite(*arg);
|
||||||
|
if (*p != ':' || !VIM_ISWHITE(**arg) || !VIM_ISWHITE(p[1]))
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
// evaluate the third expression
|
||||||
|
*arg = skipwhite(p + 1);
|
||||||
|
tv2.v_type = VAR_UNKNOWN;
|
||||||
|
if (evaluate_const_expr1(arg, cctx, &tv2) == FAIL)
|
||||||
|
{
|
||||||
|
clear_tv(&tv2);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
if (val)
|
||||||
|
{
|
||||||
|
// use the expr after "?"
|
||||||
|
clear_tv(&tv2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// use the expr after ":"
|
||||||
|
clear_tv(tv);
|
||||||
|
*tv = tv2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compile code to apply '-', '+' and '!'.
|
* Compile code to apply '-', '+' and '!'.
|
||||||
*/
|
*/
|
||||||
@@ -3525,35 +4008,9 @@ compile_expr7(char_u **arg, cctx_T *cctx)
|
|||||||
start_leader = end_leader; // don't apply again below
|
start_leader = end_leader; // don't apply again below
|
||||||
|
|
||||||
// push constant
|
// push constant
|
||||||
switch (rettv.v_type)
|
if (generate_tv_PUSH(cctx, &rettv) == FAIL)
|
||||||
{
|
|
||||||
case VAR_BOOL:
|
|
||||||
generate_PUSHBOOL(cctx, rettv.vval.v_number);
|
|
||||||
break;
|
|
||||||
case VAR_SPECIAL:
|
|
||||||
generate_PUSHSPEC(cctx, rettv.vval.v_number);
|
|
||||||
break;
|
|
||||||
case VAR_NUMBER:
|
|
||||||
generate_PUSHNR(cctx, rettv.vval.v_number);
|
|
||||||
break;
|
|
||||||
#ifdef FEAT_FLOAT
|
|
||||||
case VAR_FLOAT:
|
|
||||||
generate_PUSHF(cctx, rettv.vval.v_float);
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
case VAR_BLOB:
|
|
||||||
generate_PUSHBLOB(cctx, rettv.vval.v_blob);
|
|
||||||
rettv.vval.v_blob = NULL;
|
|
||||||
break;
|
|
||||||
case VAR_STRING:
|
|
||||||
generate_PUSHS(cctx, rettv.vval.v_string);
|
|
||||||
rettv.vval.v_string = NULL;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
iemsg("constant type missing");
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else if (ret == NOTDONE)
|
else if (ret == NOTDONE)
|
||||||
{
|
{
|
||||||
char_u *p;
|
char_u *p;
|
||||||
@@ -3682,57 +4139,6 @@ compile_expr5(char_u **arg, cctx_T *cctx)
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static exptype_T
|
|
||||||
get_compare_type(char_u *p, int *len, int *type_is)
|
|
||||||
{
|
|
||||||
exptype_T type = EXPR_UNKNOWN;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
switch (p[0])
|
|
||||||
{
|
|
||||||
case '=': if (p[1] == '=')
|
|
||||||
type = EXPR_EQUAL;
|
|
||||||
else if (p[1] == '~')
|
|
||||||
type = EXPR_MATCH;
|
|
||||||
break;
|
|
||||||
case '!': if (p[1] == '=')
|
|
||||||
type = EXPR_NEQUAL;
|
|
||||||
else if (p[1] == '~')
|
|
||||||
type = EXPR_NOMATCH;
|
|
||||||
break;
|
|
||||||
case '>': if (p[1] != '=')
|
|
||||||
{
|
|
||||||
type = EXPR_GREATER;
|
|
||||||
*len = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
type = EXPR_GEQUAL;
|
|
||||||
break;
|
|
||||||
case '<': if (p[1] != '=')
|
|
||||||
{
|
|
||||||
type = EXPR_SMALLER;
|
|
||||||
*len = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
type = EXPR_SEQUAL;
|
|
||||||
break;
|
|
||||||
case 'i': if (p[1] == 's')
|
|
||||||
{
|
|
||||||
// "is" and "isnot"; but not a prefix of a name
|
|
||||||
if (p[2] == 'n' && p[3] == 'o' && p[4] == 't')
|
|
||||||
*len = 5;
|
|
||||||
i = p[*len];
|
|
||||||
if (!isalnum(i) && i != '_')
|
|
||||||
{
|
|
||||||
type = *len == 2 ? EXPR_IS : EXPR_ISNOT;
|
|
||||||
*type_is = TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* expr5a == expr5b
|
* expr5a == expr5b
|
||||||
* expr5a =~ expr5b
|
* expr5a =~ expr5b
|
||||||
@@ -3936,12 +4342,19 @@ compile_expr2(char_u **arg, cctx_T *cctx)
|
|||||||
compile_expr1(char_u **arg, cctx_T *cctx)
|
compile_expr1(char_u **arg, cctx_T *cctx)
|
||||||
{
|
{
|
||||||
char_u *p;
|
char_u *p;
|
||||||
|
typval_T tv;
|
||||||
|
|
||||||
// TODO: Try parsing as a constant. If that works just one PUSH
|
// Evaluate the first expression.
|
||||||
|
// First try parsing as a constant. If that works just one PUSH
|
||||||
// instruction needs to be generated.
|
// instruction needs to be generated.
|
||||||
|
tv.v_type = VAR_UNKNOWN;
|
||||||
// evaluate the first expression
|
p = *arg;
|
||||||
if (compile_expr2(arg, cctx) == FAIL)
|
if (evaluate_const_expr2(&p, cctx, &tv) == OK)
|
||||||
|
{
|
||||||
|
*arg = p;
|
||||||
|
generate_tv_PUSH(cctx, &tv);
|
||||||
|
}
|
||||||
|
else if (compile_expr2(arg, cctx) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
p = skipwhite(*arg);
|
p = skipwhite(*arg);
|
||||||
@@ -4113,7 +4526,7 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx)
|
|||||||
|| generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL) == FAIL)
|
|| generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL) == FAIL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
// TODO: warning for trailing?
|
// TODO: warning for trailing text?
|
||||||
return (char_u *)"";
|
return (char_u *)"";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4925,284 +5338,6 @@ drop_scope(cctx_T *cctx)
|
|||||||
vim_free(scope);
|
vim_free(scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Evaluate an expression that is a constant:
|
|
||||||
* has(arg)
|
|
||||||
*
|
|
||||||
* Also handle:
|
|
||||||
* ! in front logical NOT
|
|
||||||
*
|
|
||||||
* Return FAIL if the expression is not a constant.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
evaluate_const_expr7(char_u **arg, cctx_T *cctx UNUSED, typval_T *tv)
|
|
||||||
{
|
|
||||||
typval_T argvars[2];
|
|
||||||
char_u *start_leader, *end_leader;
|
|
||||||
int has_call = FALSE;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Skip '!' characters. They are handled later.
|
|
||||||
*/
|
|
||||||
start_leader = *arg;
|
|
||||||
while (**arg == '!')
|
|
||||||
*arg = skipwhite(*arg + 1);
|
|
||||||
end_leader = *arg;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Recognize only a few types of constants for now.
|
|
||||||
*/
|
|
||||||
if (STRNCMP("true", *arg, 4) == 0 && !ASCII_ISALNUM((*arg)[4]))
|
|
||||||
{
|
|
||||||
tv->v_type = VAR_SPECIAL;
|
|
||||||
tv->vval.v_number = VVAL_TRUE;
|
|
||||||
*arg += 4;
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
if (STRNCMP("false", *arg, 5) == 0 && !ASCII_ISALNUM((*arg)[5]))
|
|
||||||
{
|
|
||||||
tv->v_type = VAR_SPECIAL;
|
|
||||||
tv->vval.v_number = VVAL_FALSE;
|
|
||||||
*arg += 5;
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (STRNCMP("has(", *arg, 4) == 0)
|
|
||||||
{
|
|
||||||
has_call = TRUE;
|
|
||||||
*arg = skipwhite(*arg + 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (**arg == '"')
|
|
||||||
{
|
|
||||||
if (get_string_tv(arg, tv, TRUE) == FAIL)
|
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
else if (**arg == '\'')
|
|
||||||
{
|
|
||||||
if (get_lit_string_tv(arg, tv, TRUE) == FAIL)
|
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return FAIL;
|
|
||||||
|
|
||||||
if (has_call)
|
|
||||||
{
|
|
||||||
*arg = skipwhite(*arg);
|
|
||||||
if (**arg != ')')
|
|
||||||
return FAIL;
|
|
||||||
*arg = *arg + 1;
|
|
||||||
|
|
||||||
argvars[0] = *tv;
|
|
||||||
argvars[1].v_type = VAR_UNKNOWN;
|
|
||||||
tv->v_type = VAR_NUMBER;
|
|
||||||
tv->vval.v_number = 0;
|
|
||||||
f_has(argvars, tv);
|
|
||||||
clear_tv(&argvars[0]);
|
|
||||||
|
|
||||||
while (start_leader < end_leader)
|
|
||||||
{
|
|
||||||
if (*start_leader == '!')
|
|
||||||
tv->vval.v_number = !tv->vval.v_number;
|
|
||||||
++start_leader;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
evaluate_const_expr4(char_u **arg, cctx_T *cctx UNUSED, typval_T *tv)
|
|
||||||
{
|
|
||||||
exptype_T type = EXPR_UNKNOWN;
|
|
||||||
char_u *p;
|
|
||||||
int len = 2;
|
|
||||||
int type_is = FALSE;
|
|
||||||
|
|
||||||
// get the first variable
|
|
||||||
if (evaluate_const_expr7(arg, cctx, tv) == FAIL)
|
|
||||||
return FAIL;
|
|
||||||
|
|
||||||
p = skipwhite(*arg);
|
|
||||||
type = get_compare_type(p, &len, &type_is);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If there is a comparative operator, use it.
|
|
||||||
*/
|
|
||||||
if (type != EXPR_UNKNOWN)
|
|
||||||
{
|
|
||||||
typval_T tv2;
|
|
||||||
char_u *s1, *s2;
|
|
||||||
char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN];
|
|
||||||
int n;
|
|
||||||
|
|
||||||
// TODO: Only string == string is supported now
|
|
||||||
if (tv->v_type != VAR_STRING)
|
|
||||||
return FAIL;
|
|
||||||
if (type != EXPR_EQUAL)
|
|
||||||
return FAIL;
|
|
||||||
|
|
||||||
// get the second variable
|
|
||||||
init_tv(&tv2);
|
|
||||||
*arg = skipwhite(p + len);
|
|
||||||
if (evaluate_const_expr7(arg, cctx, &tv2) == FAIL
|
|
||||||
|| tv2.v_type != VAR_STRING)
|
|
||||||
{
|
|
||||||
clear_tv(&tv2);
|
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
s1 = tv_get_string_buf(tv, buf1);
|
|
||||||
s2 = tv_get_string_buf(&tv2, buf2);
|
|
||||||
n = STRCMP(s1, s2);
|
|
||||||
clear_tv(tv);
|
|
||||||
clear_tv(&tv2);
|
|
||||||
tv->v_type = VAR_BOOL;
|
|
||||||
tv->vval.v_number = n == 0 ? VVAL_TRUE : VVAL_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int evaluate_const_expr3(char_u **arg, cctx_T *cctx, typval_T *tv);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Compile constant || or &&.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
evaluate_const_and_or(char_u **arg, cctx_T *cctx, char *op, typval_T *tv)
|
|
||||||
{
|
|
||||||
char_u *p = skipwhite(*arg);
|
|
||||||
int opchar = *op;
|
|
||||||
|
|
||||||
if (p[0] == opchar && p[1] == opchar)
|
|
||||||
{
|
|
||||||
int val = tv2bool(tv);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Repeat until there is no following "||" or "&&"
|
|
||||||
*/
|
|
||||||
while (p[0] == opchar && p[1] == opchar)
|
|
||||||
{
|
|
||||||
typval_T tv2;
|
|
||||||
|
|
||||||
if (!VIM_ISWHITE(**arg) || !VIM_ISWHITE(p[2]))
|
|
||||||
return FAIL;
|
|
||||||
|
|
||||||
// eval the next expression
|
|
||||||
*arg = skipwhite(p + 2);
|
|
||||||
tv2.v_type = VAR_UNKNOWN;
|
|
||||||
tv2.v_lock = 0;
|
|
||||||
if ((opchar == '|' ? evaluate_const_expr3(arg, cctx, &tv2)
|
|
||||||
: evaluate_const_expr4(arg, cctx, &tv2)) == FAIL)
|
|
||||||
{
|
|
||||||
clear_tv(&tv2);
|
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
if ((opchar == '&') == val)
|
|
||||||
{
|
|
||||||
// false || tv2 or true && tv2: use tv2
|
|
||||||
clear_tv(tv);
|
|
||||||
*tv = tv2;
|
|
||||||
val = tv2bool(tv);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
clear_tv(&tv2);
|
|
||||||
p = skipwhite(*arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Evaluate an expression that is a constant: expr4 && expr4 && expr4
|
|
||||||
* Return FAIL if the expression is not a constant.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
evaluate_const_expr3(char_u **arg, cctx_T *cctx, typval_T *tv)
|
|
||||||
{
|
|
||||||
// evaluate the first expression
|
|
||||||
if (evaluate_const_expr4(arg, cctx, tv) == FAIL)
|
|
||||||
return FAIL;
|
|
||||||
|
|
||||||
// || and && work almost the same
|
|
||||||
return evaluate_const_and_or(arg, cctx, "&&", tv);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Evaluate an expression that is a constant: expr3 || expr3 || expr3
|
|
||||||
* Return FAIL if the expression is not a constant.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
evaluate_const_expr2(char_u **arg, cctx_T *cctx, typval_T *tv)
|
|
||||||
{
|
|
||||||
// evaluate the first expression
|
|
||||||
if (evaluate_const_expr3(arg, cctx, tv) == FAIL)
|
|
||||||
return FAIL;
|
|
||||||
|
|
||||||
// || and && work almost the same
|
|
||||||
return evaluate_const_and_or(arg, cctx, "||", tv);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Evaluate an expression that is a constant: expr2 ? expr1 : expr1
|
|
||||||
* E.g. for "has('feature')".
|
|
||||||
* This does not produce error messages. "tv" should be cleared afterwards.
|
|
||||||
* Return FAIL if the expression is not a constant.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
evaluate_const_expr1(char_u **arg, cctx_T *cctx, typval_T *tv)
|
|
||||||
{
|
|
||||||
char_u *p;
|
|
||||||
|
|
||||||
// evaluate the first expression
|
|
||||||
if (evaluate_const_expr2(arg, cctx, tv) == FAIL)
|
|
||||||
return FAIL;
|
|
||||||
|
|
||||||
p = skipwhite(*arg);
|
|
||||||
if (*p == '?')
|
|
||||||
{
|
|
||||||
int val = tv2bool(tv);
|
|
||||||
typval_T tv2;
|
|
||||||
|
|
||||||
// require space before and after the ?
|
|
||||||
if (!VIM_ISWHITE(**arg) || !VIM_ISWHITE(p[1]))
|
|
||||||
return FAIL;
|
|
||||||
|
|
||||||
// evaluate the second expression; any type is accepted
|
|
||||||
clear_tv(tv);
|
|
||||||
*arg = skipwhite(p + 1);
|
|
||||||
if (evaluate_const_expr1(arg, cctx, tv) == FAIL)
|
|
||||||
return FAIL;
|
|
||||||
|
|
||||||
// Check for the ":".
|
|
||||||
p = skipwhite(*arg);
|
|
||||||
if (*p != ':' || !VIM_ISWHITE(**arg) || !VIM_ISWHITE(p[1]))
|
|
||||||
return FAIL;
|
|
||||||
|
|
||||||
// evaluate the third expression
|
|
||||||
*arg = skipwhite(p + 1);
|
|
||||||
tv2.v_type = VAR_UNKNOWN;
|
|
||||||
if (evaluate_const_expr1(arg, cctx, &tv2) == FAIL)
|
|
||||||
{
|
|
||||||
clear_tv(&tv2);
|
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
if (val)
|
|
||||||
{
|
|
||||||
// use the expr after "?"
|
|
||||||
clear_tv(&tv2);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// use the expr after ":"
|
|
||||||
clear_tv(tv);
|
|
||||||
*tv = tv2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* compile "if expr"
|
* compile "if expr"
|
||||||
*
|
*
|
||||||
|
Reference in New Issue
Block a user