mirror of
https://github.com/vim/vim.git
synced 2025-09-24 03:44:06 -04:00
patch 8.2.0719: Vim9: more expressions can be evaluated at compile time
Problem: Vim9: more expressions can be evaluated at compile time Solution: Recognize has('name').
This commit is contained in:
@@ -814,46 +814,45 @@ def Test_disassemble_invert_bool()
|
||||
enddef
|
||||
|
||||
def Test_disassemble_compare()
|
||||
" TODO: COMPAREFUNC
|
||||
let cases = [
|
||||
['true == false', 'COMPAREBOOL =='],
|
||||
['true != false', 'COMPAREBOOL !='],
|
||||
['v:none == v:null', 'COMPARESPECIAL =='],
|
||||
['v:none != v:null', 'COMPARESPECIAL !='],
|
||||
['true == isFalse', 'COMPAREBOOL =='],
|
||||
['true != isFalse', 'COMPAREBOOL !='],
|
||||
['v:none == isNull', 'COMPARESPECIAL =='],
|
||||
['v:none != isNull', 'COMPARESPECIAL !='],
|
||||
|
||||
['111 == 222', 'COMPARENR =='],
|
||||
['111 != 222', 'COMPARENR !='],
|
||||
['111 > 222', 'COMPARENR >'],
|
||||
['111 < 222', 'COMPARENR <'],
|
||||
['111 >= 222', 'COMPARENR >='],
|
||||
['111 <= 222', 'COMPARENR <='],
|
||||
['111 =~ 222', 'COMPARENR =\~'],
|
||||
['111 !~ 222', 'COMPARENR !\~'],
|
||||
['111 == aNumber', 'COMPARENR =='],
|
||||
['111 != aNumber', 'COMPARENR !='],
|
||||
['111 > aNumber', 'COMPARENR >'],
|
||||
['111 < aNumber', 'COMPARENR <'],
|
||||
['111 >= aNumber', 'COMPARENR >='],
|
||||
['111 <= aNumber', 'COMPARENR <='],
|
||||
['111 =~ aNumber', 'COMPARENR =\~'],
|
||||
['111 !~ aNumber', 'COMPARENR !\~'],
|
||||
|
||||
['"xx" != "yy"', 'COMPARESTRING !='],
|
||||
['"xx" > "yy"', 'COMPARESTRING >'],
|
||||
['"xx" < "yy"', 'COMPARESTRING <'],
|
||||
['"xx" >= "yy"', 'COMPARESTRING >='],
|
||||
['"xx" <= "yy"', 'COMPARESTRING <='],
|
||||
['"xx" =~ "yy"', 'COMPARESTRING =\~'],
|
||||
['"xx" !~ "yy"', 'COMPARESTRING !\~'],
|
||||
['"xx" is "yy"', 'COMPARESTRING is'],
|
||||
['"xx" isnot "yy"', 'COMPARESTRING isnot'],
|
||||
['"xx" != aString', 'COMPARESTRING !='],
|
||||
['"xx" > aString', 'COMPARESTRING >'],
|
||||
['"xx" < aString', 'COMPARESTRING <'],
|
||||
['"xx" >= aString', 'COMPARESTRING >='],
|
||||
['"xx" <= aString', 'COMPARESTRING <='],
|
||||
['"xx" =~ aString', 'COMPARESTRING =\~'],
|
||||
['"xx" !~ aString', 'COMPARESTRING !\~'],
|
||||
['"xx" is aString', 'COMPARESTRING is'],
|
||||
['"xx" isnot aString', 'COMPARESTRING isnot'],
|
||||
|
||||
['0z11 == 0z22', 'COMPAREBLOB =='],
|
||||
['0z11 != 0z22', 'COMPAREBLOB !='],
|
||||
['0z11 is 0z22', 'COMPAREBLOB is'],
|
||||
['0z11 isnot 0z22', 'COMPAREBLOB isnot'],
|
||||
['0z11 == aBlob', 'COMPAREBLOB =='],
|
||||
['0z11 != aBlob', 'COMPAREBLOB !='],
|
||||
['0z11 is aBlob', 'COMPAREBLOB is'],
|
||||
['0z11 isnot aBlob', 'COMPAREBLOB isnot'],
|
||||
|
||||
['[1,2] == [3,4]', 'COMPARELIST =='],
|
||||
['[1,2] != [3,4]', 'COMPARELIST !='],
|
||||
['[1,2] is [3,4]', 'COMPARELIST is'],
|
||||
['[1,2] isnot [3,4]', 'COMPARELIST isnot'],
|
||||
['[1, 2] == aList', 'COMPARELIST =='],
|
||||
['[1, 2] != aList', 'COMPARELIST !='],
|
||||
['[1, 2] is aList', 'COMPARELIST is'],
|
||||
['[1, 2] isnot aList', 'COMPARELIST isnot'],
|
||||
|
||||
['#{a:1} == #{x:2}', 'COMPAREDICT =='],
|
||||
['#{a:1} != #{x:2}', 'COMPAREDICT !='],
|
||||
['#{a:1} is #{x:2}', 'COMPAREDICT is'],
|
||||
['#{a:1} isnot #{x:2}', 'COMPAREDICT isnot'],
|
||||
['#{a: 1} == aDict', 'COMPAREDICT =='],
|
||||
['#{a: 1} != aDict', 'COMPAREDICT !='],
|
||||
['#{a: 1} is aDict', 'COMPAREDICT is'],
|
||||
['#{a: 1} isnot aDict', 'COMPAREDICT isnot'],
|
||||
|
||||
['{->33} == {->44}', 'COMPAREFUNC =='],
|
||||
['{->33} != {->44}', 'COMPAREFUNC !='],
|
||||
@@ -871,22 +870,33 @@ def Test_disassemble_compare()
|
||||
['77 is g:xx', 'COMPAREANY is'],
|
||||
['77 isnot g:xx', 'COMPAREANY isnot'],
|
||||
]
|
||||
let floatDecl = ''
|
||||
if has('float')
|
||||
cases->extend([
|
||||
['1.1 == 2.2', 'COMPAREFLOAT =='],
|
||||
['1.1 != 2.2', 'COMPAREFLOAT !='],
|
||||
['1.1 > 2.2', 'COMPAREFLOAT >'],
|
||||
['1.1 < 2.2', 'COMPAREFLOAT <'],
|
||||
['1.1 >= 2.2', 'COMPAREFLOAT >='],
|
||||
['1.1 <= 2.2', 'COMPAREFLOAT <='],
|
||||
['1.1 =~ 2.2', 'COMPAREFLOAT =\~'],
|
||||
['1.1 !~ 2.2', 'COMPAREFLOAT !\~'],
|
||||
['1.1 == aFloat', 'COMPAREFLOAT =='],
|
||||
['1.1 != aFloat', 'COMPAREFLOAT !='],
|
||||
['1.1 > aFloat', 'COMPAREFLOAT >'],
|
||||
['1.1 < aFloat', 'COMPAREFLOAT <'],
|
||||
['1.1 >= aFloat', 'COMPAREFLOAT >='],
|
||||
['1.1 <= aFloat', 'COMPAREFLOAT <='],
|
||||
['1.1 =~ aFloat', 'COMPAREFLOAT =\~'],
|
||||
['1.1 !~ aFloat', 'COMPAREFLOAT !\~'],
|
||||
])
|
||||
floatDecl = 'let aFloat = 2.2'
|
||||
endif
|
||||
|
||||
let nr = 1
|
||||
for case in cases
|
||||
" declare local variables to get a non-constant with the right type
|
||||
writefile(['def TestCase' .. nr .. '()',
|
||||
' let isFalse = false',
|
||||
' let isNull = v:null',
|
||||
' let aNumber = 222',
|
||||
' let aString = "yy"',
|
||||
' let aBlob = 0z22',
|
||||
' let aList = [3, 4]',
|
||||
' let aDict = #{x: 2}',
|
||||
floatDecl,
|
||||
' if ' .. case[0],
|
||||
' echo 42'
|
||||
' endif',
|
||||
@@ -896,7 +906,7 @@ def Test_disassemble_compare()
|
||||
assert_match('TestCase' .. nr .. '.*' ..
|
||||
'if ' .. substitute(case[0], '[[~]', '\\\0', 'g') .. '.*' ..
|
||||
'\d \(PUSH\|FUNCREF\).*' ..
|
||||
'\d \(PUSH\|FUNCREF\|LOADG\).*' ..
|
||||
'\d \(PUSH\|FUNCREF\|LOAD\).*' ..
|
||||
'\d ' .. case[1] .. '.*' ..
|
||||
'\d JUMP_IF_FALSE -> \d\+.*',
|
||||
instr)
|
||||
|
@@ -429,7 +429,7 @@ func Test_expr4_fails()
|
||||
|
||||
call CheckDefFailure(["let x = 1 == '2'"], 'Cannot compare number with string')
|
||||
call CheckDefFailure(["let x = '1' == 2"], 'Cannot compare string with number')
|
||||
call CheckDefFailure(["let x = 1 == RetVoid()"], 'Cannot use void value')
|
||||
call CheckDefFailure(["let x = 1 == RetVoid()"], 'Cannot compare number with void')
|
||||
call CheckDefFailure(["let x = RetVoid() == 1"], 'Cannot compare void with number')
|
||||
|
||||
call CheckDefFailure(["let x = true > false"], 'Cannot compare bool with bool')
|
||||
|
@@ -746,6 +746,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
719,
|
||||
/**/
|
||||
718,
|
||||
/**/
|
||||
|
@@ -136,9 +136,7 @@ struct cctx_S {
|
||||
static char e_var_notfound[] = N_("E1001: variable not found: %s");
|
||||
static char e_syntax_at[] = N_("E1002: Syntax error at %s");
|
||||
|
||||
static int compile_expr1(char_u **arg, cctx_T *cctx);
|
||||
static int compile_expr2(char_u **arg, cctx_T *cctx);
|
||||
static int compile_expr3(char_u **arg, cctx_T *cctx);
|
||||
static int compile_expr0(char_u **arg, cctx_T *cctx);
|
||||
static void delete_def_function_contents(dfunc_T *dfunc);
|
||||
static void arg_type_mismatch(type_T *expected, type_T *actual, int argidx);
|
||||
static int check_type(type_T *expected, type_T *actual, int give_msg);
|
||||
@@ -744,24 +742,14 @@ generate_two_op(cctx_T *cctx, char_u *op)
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate an ISN_COMPARE* instruction with a boolean result.
|
||||
* Get the instruction to use for comparing "type1" with "type2"
|
||||
* Return ISN_DROP when failed.
|
||||
*/
|
||||
static int
|
||||
generate_COMPARE(cctx_T *cctx, exptype_T exptype, int ic)
|
||||
static isntype_T
|
||||
get_compare_isn(exptype_T exptype, vartype_T type1, vartype_T type2)
|
||||
{
|
||||
isntype_T isntype = ISN_DROP;
|
||||
isn_T *isn;
|
||||
garray_T *stack = &cctx->ctx_type_stack;
|
||||
vartype_T type1;
|
||||
vartype_T type2;
|
||||
|
||||
RETURN_OK_IF_SKIP(cctx);
|
||||
|
||||
// Get the known type of the two items on the stack. If they are matching
|
||||
// use a type-specific instruction. Otherwise fall back to runtime type
|
||||
// checking.
|
||||
type1 = ((type_T **)stack->ga_data)[stack->ga_len - 2]->tt_type;
|
||||
type2 = ((type_T **)stack->ga_data)[stack->ga_len - 1]->tt_type;
|
||||
if (type1 == VAR_UNKNOWN)
|
||||
type1 = VAR_ANY;
|
||||
if (type2 == VAR_UNKNOWN)
|
||||
@@ -796,7 +784,7 @@ generate_COMPARE(cctx_T *cctx, exptype_T exptype, int ic)
|
||||
{
|
||||
semsg(_("E1037: Cannot use \"%s\" with %s"),
|
||||
exptype == EXPR_IS ? "is" : "isnot" , vartype_name(type1));
|
||||
return FAIL;
|
||||
return ISN_DROP;
|
||||
}
|
||||
if (isntype == ISN_DROP
|
||||
|| ((exptype != EXPR_EQUAL && exptype != EXPR_NEQUAL
|
||||
@@ -809,8 +797,33 @@ generate_COMPARE(cctx_T *cctx, exptype_T exptype, int ic)
|
||||
{
|
||||
semsg(_("E1072: Cannot compare %s with %s"),
|
||||
vartype_name(type1), vartype_name(type2));
|
||||
return FAIL;
|
||||
return ISN_DROP;
|
||||
}
|
||||
return isntype;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate an ISN_COMPARE* instruction with a boolean result.
|
||||
*/
|
||||
static int
|
||||
generate_COMPARE(cctx_T *cctx, exptype_T exptype, int ic)
|
||||
{
|
||||
isntype_T isntype;
|
||||
isn_T *isn;
|
||||
garray_T *stack = &cctx->ctx_type_stack;
|
||||
vartype_T type1;
|
||||
vartype_T type2;
|
||||
|
||||
RETURN_OK_IF_SKIP(cctx);
|
||||
|
||||
// Get the known type of the two items on the stack. If they are matching
|
||||
// use a type-specific instruction. Otherwise fall back to runtime type
|
||||
// checking.
|
||||
type1 = ((type_T **)stack->ga_data)[stack->ga_len - 2]->tt_type;
|
||||
type2 = ((type_T **)stack->ga_data)[stack->ga_len - 1]->tt_type;
|
||||
isntype = get_compare_isn(exptype, type1, type2);
|
||||
if (isntype == ISN_DROP)
|
||||
return FAIL;
|
||||
|
||||
if ((isn = generate_instr(cctx, isntype)) == NULL)
|
||||
return FAIL;
|
||||
@@ -2340,6 +2353,91 @@ may_get_next_line(char_u *whitep, char_u **arg, cctx_T *cctx)
|
||||
return OK;
|
||||
}
|
||||
|
||||
// Structure passed between the compile_expr* functions to keep track of
|
||||
// constants that have been parsed but for which no code was produced yet. If
|
||||
// possible expressions on these constants are applied at compile time. If
|
||||
// that is not possible, the code to push the constants needs to be generated
|
||||
// before other instructions.
|
||||
typedef struct {
|
||||
typval_T pp_tv[10]; // stack of ppconst constants
|
||||
int pp_used; // active entries in pp_tv[]
|
||||
} ppconst_T;
|
||||
|
||||
/*
|
||||
* Generate a PUSH instruction for "tv".
|
||||
* "tv" will be consumed or cleared.
|
||||
* Nothing happens if "tv" is NULL or of type VAR_UNKNOWN;
|
||||
*/
|
||||
static int
|
||||
generate_tv_PUSH(cctx_T *cctx, typval_T *tv)
|
||||
{
|
||||
if (tv != NULL)
|
||||
{
|
||||
switch (tv->v_type)
|
||||
{
|
||||
case VAR_UNKNOWN:
|
||||
break;
|
||||
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");
|
||||
clear_tv(tv);
|
||||
return FAIL;
|
||||
}
|
||||
tv->v_type = VAR_UNKNOWN;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate code for any ppconst entries.
|
||||
*/
|
||||
static int
|
||||
generate_ppconst(cctx_T *cctx, ppconst_T *ppconst)
|
||||
{
|
||||
int i;
|
||||
int ret = OK;
|
||||
|
||||
for (i = 0; i < ppconst->pp_used; ++i)
|
||||
if (generate_tv_PUSH(cctx, &ppconst->pp_tv[i]) == FAIL)
|
||||
ret = FAIL;
|
||||
ppconst->pp_used = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear ppconst constants. Used when failing.
|
||||
*/
|
||||
static void
|
||||
clear_ppconst(ppconst_T *ppconst)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ppconst->pp_used; ++i)
|
||||
clear_tv(&ppconst->pp_tv[i]);
|
||||
ppconst->pp_used = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate an instruction to load script-local variable "name", without the
|
||||
* leading "s:".
|
||||
@@ -2526,25 +2624,18 @@ compile_load(char_u **arg, char_u *end_arg, cctx_T *cctx, int error)
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((len == 4 && STRNCMP("true", *arg, 4) == 0)
|
||||
|| (len == 5 && STRNCMP("false", *arg, 5) == 0))
|
||||
res = generate_PUSHBOOL(cctx, **arg == 't'
|
||||
? VVAL_TRUE : VVAL_FALSE);
|
||||
else
|
||||
{
|
||||
// "var" can be script-local even without using "s:" if it
|
||||
// already exists.
|
||||
if (SCRIPT_ITEM(current_sctx.sc_sid)->sn_version
|
||||
== SCRIPT_VERSION_VIM9
|
||||
|| lookup_script(*arg, len) == OK)
|
||||
res = compile_load_scriptvar(cctx, name, *arg, &end,
|
||||
FALSE);
|
||||
// "var" can be script-local even without using "s:" if it
|
||||
// already exists.
|
||||
if (SCRIPT_ITEM(current_sctx.sc_sid)->sn_version
|
||||
== SCRIPT_VERSION_VIM9
|
||||
|| lookup_script(*arg, len) == OK)
|
||||
res = compile_load_scriptvar(cctx, name, *arg, &end,
|
||||
FALSE);
|
||||
|
||||
// When the name starts with an uppercase letter or "x:" it
|
||||
// can be a user defined function.
|
||||
if (res == FAIL && (ASCII_ISUPPER(*name) || name[1] == ':'))
|
||||
res = generate_funcref(cctx, name);
|
||||
}
|
||||
// When the name starts with an uppercase letter or "x:" it
|
||||
// can be a user defined function.
|
||||
if (res == FAIL && (ASCII_ISUPPER(*name) || name[1] == ':'))
|
||||
res = generate_funcref(cctx, name);
|
||||
}
|
||||
}
|
||||
if (gen_load)
|
||||
@@ -2588,7 +2679,7 @@ compile_arguments(char_u **arg, cctx_T *cctx, int *argcount)
|
||||
return OK;
|
||||
}
|
||||
|
||||
if (compile_expr1(&p, cctx) == FAIL)
|
||||
if (compile_expr0(&p, cctx) == FAIL)
|
||||
return FAIL;
|
||||
++*argcount;
|
||||
|
||||
@@ -2621,7 +2712,12 @@ failret:
|
||||
* BCALL / DCALL / UCALL
|
||||
*/
|
||||
static int
|
||||
compile_call(char_u **arg, size_t varlen, cctx_T *cctx, int argcount_init)
|
||||
compile_call(
|
||||
char_u **arg,
|
||||
size_t varlen,
|
||||
cctx_T *cctx,
|
||||
ppconst_T *ppconst,
|
||||
int argcount_init)
|
||||
{
|
||||
char_u *name = *arg;
|
||||
char_u *p;
|
||||
@@ -2633,6 +2729,36 @@ compile_call(char_u **arg, size_t varlen, cctx_T *cctx, int argcount_init)
|
||||
ufunc_T *ufunc;
|
||||
int res = FAIL;
|
||||
|
||||
// we can evaluate "has('name')" at compile time
|
||||
if (varlen == 3 && STRNCMP(*arg, "has", 3) == 0)
|
||||
{
|
||||
char_u *s = skipwhite(*arg + varlen + 1);
|
||||
typval_T argvars[2];
|
||||
|
||||
argvars[0].v_type = VAR_UNKNOWN;
|
||||
if (*s == '"')
|
||||
(void)get_string_tv(&s, &argvars[0], TRUE);
|
||||
else if (*s == '\'')
|
||||
(void)get_lit_string_tv(&s, &argvars[0], TRUE);
|
||||
s = skipwhite(s);
|
||||
if (*s == ')' && argvars[0].v_type == VAR_STRING)
|
||||
{
|
||||
typval_T *tv = &ppconst->pp_tv[ppconst->pp_used];
|
||||
|
||||
*arg = s + 1;
|
||||
argvars[1].v_type = VAR_UNKNOWN;
|
||||
tv->v_type = VAR_NUMBER;
|
||||
tv->vval.v_number = 0;
|
||||
f_has(argvars, tv);
|
||||
clear_tv(&argvars[0]);
|
||||
++ppconst->pp_used;
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (generate_ppconst(cctx, ppconst) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
if (varlen >= sizeof(namebuf))
|
||||
{
|
||||
semsg(_("E1011: name too long: %s"), name);
|
||||
@@ -2791,7 +2917,7 @@ compile_list(char_u **arg, cctx_T *cctx)
|
||||
p += STRLEN(p);
|
||||
break;
|
||||
}
|
||||
if (compile_expr1(&p, cctx) == FAIL)
|
||||
if (compile_expr0(&p, cctx) == FAIL)
|
||||
break;
|
||||
++count;
|
||||
if (*p == ',')
|
||||
@@ -2929,7 +3055,7 @@ compile_dict(char_u **arg, cctx_T *cctx, int literal)
|
||||
{
|
||||
isn_T *isn;
|
||||
|
||||
if (compile_expr1(arg, cctx) == FAIL)
|
||||
if (compile_expr0(arg, cctx) == FAIL)
|
||||
return FAIL;
|
||||
// TODO: check type is string
|
||||
isn = ((isn_T *)instr->ga_data) + instr->ga_len - 1;
|
||||
@@ -2974,7 +3100,7 @@ compile_dict(char_u **arg, cctx_T *cctx, int literal)
|
||||
*arg = skipwhite(*arg);
|
||||
}
|
||||
|
||||
if (compile_expr1(arg, cctx) == FAIL)
|
||||
if (compile_expr0(arg, cctx) == FAIL)
|
||||
return FAIL;
|
||||
++count;
|
||||
|
||||
@@ -3626,90 +3752,7 @@ evaluate_const_expr1(char_u **arg, cctx_T *cctx, typval_T *tv)
|
||||
return OK;
|
||||
}
|
||||
|
||||
// Structure passed between the compile_expr* functions to keep track of
|
||||
// constants that have been parsed but for which no code was produced yet. If
|
||||
// possible expressions on these constants are applied at compile time. If
|
||||
// that is not possible, the code to push the constants needs to be generated
|
||||
// before other instructions.
|
||||
typedef struct {
|
||||
typval_T pp_tv[10]; // stack of ppconst constants
|
||||
int pp_used; // active entries in pp_tv[]
|
||||
} ppconst_T;
|
||||
|
||||
/*
|
||||
* Generate a PUSH instruction for "tv".
|
||||
* "tv" will be consumed or cleared.
|
||||
* Nothing happens if "tv" is NULL or of type VAR_UNKNOWN;
|
||||
*/
|
||||
static int
|
||||
generate_tv_PUSH(cctx_T *cctx, typval_T *tv)
|
||||
{
|
||||
if (tv != NULL)
|
||||
{
|
||||
switch (tv->v_type)
|
||||
{
|
||||
case VAR_UNKNOWN:
|
||||
break;
|
||||
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");
|
||||
clear_tv(tv);
|
||||
return FAIL;
|
||||
}
|
||||
tv->v_type = VAR_UNKNOWN;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate code for any ppconst entries.
|
||||
*/
|
||||
static int
|
||||
generate_ppconst(cctx_T *cctx, ppconst_T *ppconst)
|
||||
{
|
||||
int i;
|
||||
int ret = OK;
|
||||
|
||||
for (i = 0; i < ppconst->pp_used; ++i)
|
||||
if (generate_tv_PUSH(cctx, &ppconst->pp_tv[i]) == FAIL)
|
||||
ret = FAIL;
|
||||
ppconst->pp_used = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear ppconst constants. Used when failing.
|
||||
*/
|
||||
static void
|
||||
clear_ppconst(ppconst_T *ppconst)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ppconst->pp_used; ++i)
|
||||
clear_tv(&ppconst->pp_tv[i]);
|
||||
ppconst->pp_used = 0;
|
||||
}
|
||||
static int compile_expr3(char_u **arg, cctx_T *cctx, ppconst_T *ppconst);
|
||||
|
||||
/*
|
||||
* Compile code to apply '-', '+' and '!'.
|
||||
@@ -3825,7 +3868,7 @@ compile_subscript(
|
||||
return FAIL;
|
||||
}
|
||||
// TODO: base value may not be the first argument
|
||||
if (compile_call(arg, p - *arg, cctx, 1) == FAIL)
|
||||
if (compile_call(arg, p - *arg, cctx, ppconst, 1) == FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
@@ -3841,7 +3884,7 @@ compile_subscript(
|
||||
// TODO: more arguments
|
||||
// TODO: dict member dict['name']
|
||||
*arg = skipwhite(*arg + 1);
|
||||
if (compile_expr1(arg, cctx) == FAIL)
|
||||
if (compile_expr0(arg, cctx) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
if (**arg != ']')
|
||||
@@ -3990,6 +4033,34 @@ compile_expr7(
|
||||
ret = NOTDONE;
|
||||
break;
|
||||
|
||||
/*
|
||||
* "true" constant
|
||||
*/
|
||||
case 't': if (STRNCMP(*arg, "true", 4) == 0
|
||||
&& !eval_isnamec((*arg)[4]))
|
||||
{
|
||||
*arg += 4;
|
||||
rettv->v_type = VAR_BOOL;
|
||||
rettv->vval.v_number = VVAL_TRUE;
|
||||
}
|
||||
else
|
||||
ret = NOTDONE;
|
||||
break;
|
||||
|
||||
/*
|
||||
* "false" constant
|
||||
*/
|
||||
case 'f': if (STRNCMP(*arg, "false", 5) == 0
|
||||
&& !eval_isnamec((*arg)[5]))
|
||||
{
|
||||
*arg += 5;
|
||||
rettv->v_type = VAR_BOOL;
|
||||
rettv->vval.v_number = VVAL_FALSE;
|
||||
}
|
||||
else
|
||||
ret = NOTDONE;
|
||||
break;
|
||||
|
||||
/*
|
||||
* List: [expr, expr]
|
||||
*/
|
||||
@@ -4047,7 +4118,7 @@ compile_expr7(
|
||||
* nested expression: (expression).
|
||||
*/
|
||||
case '(': *arg = skipwhite(*arg + 1);
|
||||
ret = compile_expr1(arg, cctx); // recursive!
|
||||
ret = compile_expr0(arg, cctx); // recursive!
|
||||
*arg = skipwhite(*arg);
|
||||
if (**arg == ')')
|
||||
++*arg;
|
||||
@@ -4074,18 +4145,18 @@ compile_expr7(
|
||||
}
|
||||
start_leader = end_leader; // don't apply again below
|
||||
|
||||
// A constant expression can possibly be handled compile time, return
|
||||
// the value instead of generating code.
|
||||
++ppconst->pp_used;
|
||||
if (cctx->ctx_skip == TRUE)
|
||||
clear_tv(rettv);
|
||||
else
|
||||
// A constant expression can possibly be handled compile time,
|
||||
// return the value instead of generating code.
|
||||
++ppconst->pp_used;
|
||||
}
|
||||
else if (ret == NOTDONE)
|
||||
{
|
||||
char_u *p;
|
||||
int r;
|
||||
|
||||
if (generate_ppconst(cctx, ppconst) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
if (!eval_isnamec1(**arg))
|
||||
{
|
||||
semsg(_("E1015: Name expected: %s"), *arg);
|
||||
@@ -4095,9 +4166,15 @@ compile_expr7(
|
||||
// "name" or "name()"
|
||||
p = to_name_end(*arg, TRUE);
|
||||
if (*p == '(')
|
||||
r = compile_call(arg, p - *arg, cctx, 0);
|
||||
{
|
||||
r = compile_call(arg, p - *arg, cctx, ppconst, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (generate_ppconst(cctx, ppconst) == FAIL)
|
||||
return FAIL;
|
||||
r = compile_load(arg, p, cctx, TRUE);
|
||||
}
|
||||
if (r == FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
@@ -4105,8 +4182,17 @@ compile_expr7(
|
||||
// Handle following "[]", ".member", etc.
|
||||
// Then deal with prefixed '-', '+' and '!', if not done already.
|
||||
if (compile_subscript(arg, cctx, &start_leader, end_leader,
|
||||
ppconst) == FAIL
|
||||
|| compile_leader(cctx, start_leader, end_leader) == FAIL)
|
||||
ppconst) == FAIL)
|
||||
return FAIL;
|
||||
if (ppconst->pp_used > 0)
|
||||
{
|
||||
// apply the '!', '-' and '+' before the constant
|
||||
rettv = &ppconst->pp_tv[ppconst->pp_used - 1];
|
||||
if (apply_leader(rettv, start_leader, end_leader) == FAIL)
|
||||
return FAIL;
|
||||
return OK;
|
||||
}
|
||||
if (compile_leader(cctx, start_leader, end_leader) == FAIL)
|
||||
return FAIL;
|
||||
return OK;
|
||||
}
|
||||
@@ -4117,10 +4203,7 @@ compile_expr7(
|
||||
* % number modulo
|
||||
*/
|
||||
static int
|
||||
compile_expr6(
|
||||
char_u **arg,
|
||||
cctx_T *cctx,
|
||||
ppconst_T *ppconst)
|
||||
compile_expr6(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
|
||||
{
|
||||
char_u *op;
|
||||
int ppconst_used = ppconst->pp_used;
|
||||
@@ -4191,21 +4274,15 @@ compile_expr6(
|
||||
* .. string concatenation
|
||||
*/
|
||||
static int
|
||||
compile_expr5(char_u **arg, cctx_T *cctx)
|
||||
compile_expr5(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
|
||||
{
|
||||
char_u *op;
|
||||
int oplen;
|
||||
ppconst_T ppconst;
|
||||
int ppconst_used = 0;
|
||||
|
||||
CLEAR_FIELD(ppconst);
|
||||
int ppconst_used = ppconst->pp_used;
|
||||
|
||||
// get the first variable
|
||||
if (compile_expr6(arg, cctx, &ppconst) == FAIL)
|
||||
{
|
||||
clear_ppconst(&ppconst);
|
||||
if (compile_expr6(arg, cctx, ppconst) == FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Repeat computing, until no "+", "-" or ".." is following.
|
||||
@@ -4221,7 +4298,6 @@ compile_expr5(char_u **arg, cctx_T *cctx)
|
||||
{
|
||||
char_u buf[3];
|
||||
|
||||
clear_ppconst(&ppconst);
|
||||
vim_strncpy(buf, op, oplen);
|
||||
semsg(_(e_white_both), buf);
|
||||
return FAIL;
|
||||
@@ -4229,27 +4305,21 @@ compile_expr5(char_u **arg, cctx_T *cctx)
|
||||
|
||||
*arg = skipwhite(op + oplen);
|
||||
if (may_get_next_line(op + oplen, arg, cctx) == FAIL)
|
||||
{
|
||||
clear_ppconst(&ppconst);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
// get the second expression
|
||||
if (compile_expr6(arg, cctx, &ppconst) == FAIL)
|
||||
{
|
||||
clear_ppconst(&ppconst);
|
||||
if (compile_expr6(arg, cctx, ppconst) == FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if (ppconst.pp_used == ppconst_used + 2
|
||||
if (ppconst->pp_used == ppconst_used + 2
|
||||
&& (*op == '.'
|
||||
? (ppconst.pp_tv[ppconst_used].v_type == VAR_STRING
|
||||
&& ppconst.pp_tv[ppconst_used + 1].v_type == VAR_STRING)
|
||||
: (ppconst.pp_tv[ppconst_used].v_type == VAR_NUMBER
|
||||
&& ppconst.pp_tv[ppconst_used + 1].v_type == VAR_NUMBER)))
|
||||
? (ppconst->pp_tv[ppconst_used].v_type == VAR_STRING
|
||||
&& ppconst->pp_tv[ppconst_used + 1].v_type == VAR_STRING)
|
||||
: (ppconst->pp_tv[ppconst_used].v_type == VAR_NUMBER
|
||||
&& ppconst->pp_tv[ppconst_used + 1].v_type == VAR_NUMBER)))
|
||||
{
|
||||
typval_T *tv1 = &ppconst.pp_tv[ppconst_used];
|
||||
typval_T *tv2 = &ppconst.pp_tv[ppconst_used + 1];
|
||||
typval_T *tv1 = &ppconst->pp_tv[ppconst_used];
|
||||
typval_T *tv2 = &ppconst->pp_tv[ppconst_used + 1];
|
||||
|
||||
// concat/subtract/add constant numbers
|
||||
if (*op == '+')
|
||||
@@ -4266,7 +4336,7 @@ compile_expr5(char_u **arg, cctx_T *cctx)
|
||||
tv1->vval.v_string = alloc((int)(len1 + STRLEN(s2) + 1));
|
||||
if (tv1->vval.v_string == NULL)
|
||||
{
|
||||
clear_ppconst(&ppconst);
|
||||
clear_ppconst(ppconst);
|
||||
return FAIL;
|
||||
}
|
||||
mch_memmove(tv1->vval.v_string, s1, len1);
|
||||
@@ -4274,19 +4344,16 @@ compile_expr5(char_u **arg, cctx_T *cctx)
|
||||
vim_free(s1);
|
||||
vim_free(s2);
|
||||
}
|
||||
--ppconst.pp_used;
|
||||
--ppconst->pp_used;
|
||||
}
|
||||
else
|
||||
{
|
||||
generate_ppconst(cctx, &ppconst);
|
||||
generate_ppconst(cctx, ppconst);
|
||||
if (*op == '.')
|
||||
{
|
||||
if (may_generate_2STRING(-2, cctx) == FAIL
|
||||
|| may_generate_2STRING(-1, cctx) == FAIL)
|
||||
{
|
||||
clear_ppconst(&ppconst);
|
||||
return FAIL;
|
||||
}
|
||||
generate_instr_drop(cctx, ISN_CONCAT, 1);
|
||||
}
|
||||
else
|
||||
@@ -4294,9 +4361,6 @@ compile_expr5(char_u **arg, cctx_T *cctx)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: move to caller
|
||||
generate_ppconst(cctx, &ppconst);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
@@ -4318,15 +4382,16 @@ compile_expr5(char_u **arg, cctx_T *cctx)
|
||||
* COMPARE one of the compare instructions
|
||||
*/
|
||||
static int
|
||||
compile_expr4(char_u **arg, cctx_T *cctx)
|
||||
compile_expr4(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
|
||||
{
|
||||
exptype_T type = EXPR_UNKNOWN;
|
||||
char_u *p;
|
||||
int len = 2;
|
||||
int type_is = FALSE;
|
||||
int ppconst_used = ppconst->pp_used;
|
||||
|
||||
// get the first variable
|
||||
if (compile_expr5(arg, cctx) == FAIL)
|
||||
if (compile_expr5(arg, cctx, ppconst) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
p = skipwhite(*arg);
|
||||
@@ -4369,10 +4434,34 @@ compile_expr4(char_u **arg, cctx_T *cctx)
|
||||
if (may_get_next_line(p + len, arg, cctx) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
if (compile_expr5(arg, cctx) == FAIL)
|
||||
if (compile_expr5(arg, cctx, ppconst) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
generate_COMPARE(cctx, type, ic);
|
||||
if (ppconst->pp_used == ppconst_used + 2)
|
||||
{
|
||||
typval_T * tv1 = &ppconst->pp_tv[ppconst->pp_used - 2];
|
||||
typval_T *tv2 = &ppconst->pp_tv[ppconst->pp_used - 1];
|
||||
int ret;
|
||||
|
||||
// Both sides are a constant, compute the result now.
|
||||
// First check for a valid combination of types, this is more
|
||||
// strict than typval_compare().
|
||||
if (get_compare_isn(type, tv1->v_type, tv2->v_type) == ISN_DROP)
|
||||
ret = FAIL;
|
||||
else
|
||||
{
|
||||
ret = typval_compare(tv1, tv2, type, ic);
|
||||
tv1->v_type = VAR_BOOL;
|
||||
tv1->vval.v_number = tv1->vval.v_number
|
||||
? VVAL_TRUE : VVAL_FALSE;
|
||||
clear_tv(tv2);
|
||||
--ppconst->pp_used;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
generate_ppconst(cctx, ppconst);
|
||||
return generate_COMPARE(cctx, type, ic);
|
||||
}
|
||||
|
||||
return OK;
|
||||
@@ -4382,7 +4471,12 @@ compile_expr4(char_u **arg, cctx_T *cctx)
|
||||
* Compile || or &&.
|
||||
*/
|
||||
static int
|
||||
compile_and_or(char_u **arg, cctx_T *cctx, char *op)
|
||||
compile_and_or(
|
||||
char_u **arg,
|
||||
cctx_T *cctx,
|
||||
char *op,
|
||||
ppconst_T *ppconst,
|
||||
int ppconst_used UNUSED)
|
||||
{
|
||||
char_u *p = skipwhite(*arg);
|
||||
int opchar = *op;
|
||||
@@ -4404,6 +4498,9 @@ compile_and_or(char_u **arg, cctx_T *cctx, char *op)
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
// TODO: use ppconst if the value is a constant
|
||||
generate_ppconst(cctx, ppconst);
|
||||
|
||||
if (ga_grow(&end_ga, 1) == FAIL)
|
||||
{
|
||||
ga_clear(&end_ga);
|
||||
@@ -4419,14 +4516,15 @@ compile_and_or(char_u **arg, cctx_T *cctx, char *op)
|
||||
if (may_get_next_line(p + 2, arg, cctx) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
if ((opchar == '|' ? compile_expr3(arg, cctx)
|
||||
: compile_expr4(arg, cctx)) == FAIL)
|
||||
if ((opchar == '|' ? compile_expr3(arg, cctx, ppconst)
|
||||
: compile_expr4(arg, cctx, ppconst)) == FAIL)
|
||||
{
|
||||
ga_clear(&end_ga);
|
||||
return FAIL;
|
||||
}
|
||||
p = skipwhite(*arg);
|
||||
}
|
||||
generate_ppconst(cctx, ppconst);
|
||||
|
||||
// Fill in the end label in all jumps.
|
||||
while (end_ga.ga_len > 0)
|
||||
@@ -4456,14 +4554,16 @@ compile_and_or(char_u **arg, cctx_T *cctx, char *op)
|
||||
* end:
|
||||
*/
|
||||
static int
|
||||
compile_expr3(char_u **arg, cctx_T *cctx)
|
||||
compile_expr3(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
|
||||
{
|
||||
int ppconst_used = ppconst->pp_used;
|
||||
|
||||
// get the first variable
|
||||
if (compile_expr4(arg, cctx) == FAIL)
|
||||
if (compile_expr4(arg, cctx, ppconst) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
// || and && work almost the same
|
||||
return compile_and_or(arg, cctx, "&&");
|
||||
return compile_and_or(arg, cctx, "&&", ppconst, ppconst_used);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -4478,14 +4578,16 @@ compile_expr3(char_u **arg, cctx_T *cctx)
|
||||
* end:
|
||||
*/
|
||||
static int
|
||||
compile_expr2(char_u **arg, cctx_T *cctx)
|
||||
compile_expr2(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
|
||||
{
|
||||
int ppconst_used = ppconst->pp_used;
|
||||
|
||||
// eval the first expression
|
||||
if (compile_expr3(arg, cctx) == FAIL)
|
||||
if (compile_expr3(arg, cctx, ppconst) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
// || and && work almost the same
|
||||
return compile_and_or(arg, cctx, "||");
|
||||
return compile_and_or(arg, cctx, "||", ppconst, ppconst_used);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -4500,12 +4602,13 @@ compile_expr2(char_u **arg, cctx_T *cctx)
|
||||
* end:
|
||||
*/
|
||||
static int
|
||||
compile_expr1(char_u **arg, cctx_T *cctx)
|
||||
compile_expr1(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
|
||||
{
|
||||
char_u *p;
|
||||
int ppconst_used = ppconst->pp_used;
|
||||
|
||||
// Evaluate the first expression.
|
||||
if (compile_expr2(arg, cctx) == FAIL)
|
||||
if (compile_expr2(arg, cctx, ppconst) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
p = skipwhite(*arg);
|
||||
@@ -4518,6 +4621,9 @@ compile_expr1(char_u **arg, cctx_T *cctx)
|
||||
isn_T *isn;
|
||||
type_T *type1;
|
||||
type_T *type2;
|
||||
int has_const_expr = FALSE;
|
||||
int const_value = FALSE;
|
||||
int save_skip = cctx->ctx_skip;
|
||||
|
||||
if (!IS_WHITE_OR_NUL(**arg) || !IS_WHITE_OR_NUL(p[1]))
|
||||
{
|
||||
@@ -4525,26 +4631,44 @@ compile_expr1(char_u **arg, cctx_T *cctx)
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
generate_JUMP(cctx, JUMP_IF_FALSE, 0);
|
||||
if (ppconst->pp_used == ppconst_used + 1)
|
||||
{
|
||||
// the condition is a constant, we know whether the ? or the :
|
||||
// expression is to be evaluated.
|
||||
has_const_expr = TRUE;
|
||||
const_value = tv2bool(&ppconst->pp_tv[ppconst_used]);
|
||||
clear_tv(&ppconst->pp_tv[ppconst_used]);
|
||||
--ppconst->pp_used;
|
||||
cctx->ctx_skip = save_skip == TRUE || !const_value;
|
||||
}
|
||||
else
|
||||
{
|
||||
generate_ppconst(cctx, ppconst);
|
||||
generate_JUMP(cctx, JUMP_IF_FALSE, 0);
|
||||
}
|
||||
|
||||
// evaluate the second expression; any type is accepted
|
||||
*arg = skipwhite(p + 1);
|
||||
if (may_get_next_line(p + 1, arg, cctx) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
if (compile_expr1(arg, cctx) == FAIL)
|
||||
if (compile_expr1(arg, cctx, ppconst) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
// remember the type and drop it
|
||||
--stack->ga_len;
|
||||
type1 = ((type_T **)stack->ga_data)[stack->ga_len];
|
||||
if (!has_const_expr)
|
||||
{
|
||||
generate_ppconst(cctx, ppconst);
|
||||
|
||||
end_idx = instr->ga_len;
|
||||
generate_JUMP(cctx, JUMP_ALWAYS, 0);
|
||||
// remember the type and drop it
|
||||
--stack->ga_len;
|
||||
type1 = ((type_T **)stack->ga_data)[stack->ga_len];
|
||||
|
||||
// jump here from JUMP_IF_FALSE
|
||||
isn = ((isn_T *)instr->ga_data) + alt_idx;
|
||||
isn->isn_arg.jump.jump_where = instr->ga_len;
|
||||
end_idx = instr->ga_len;
|
||||
generate_JUMP(cctx, JUMP_ALWAYS, 0);
|
||||
|
||||
// jump here from JUMP_IF_FALSE
|
||||
isn = ((isn_T *)instr->ga_data) + alt_idx;
|
||||
isn->isn_arg.jump.jump_where = instr->ga_len;
|
||||
}
|
||||
|
||||
// Check for the ":".
|
||||
p = skipwhite(*arg);
|
||||
@@ -4560,24 +4684,51 @@ compile_expr1(char_u **arg, cctx_T *cctx)
|
||||
}
|
||||
|
||||
// evaluate the third expression
|
||||
if (has_const_expr)
|
||||
cctx->ctx_skip = save_skip == TRUE || const_value;
|
||||
*arg = skipwhite(p + 1);
|
||||
if (may_get_next_line(p + 1, arg, cctx) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
if (compile_expr1(arg, cctx) == FAIL)
|
||||
if (compile_expr1(arg, cctx, ppconst) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
// If the types differ, the result has a more generic type.
|
||||
type2 = ((type_T **)stack->ga_data)[stack->ga_len - 1];
|
||||
common_type(type1, type2, &type2, cctx->ctx_type_list);
|
||||
if (!has_const_expr)
|
||||
{
|
||||
generate_ppconst(cctx, ppconst);
|
||||
|
||||
// jump here from JUMP_ALWAYS
|
||||
isn = ((isn_T *)instr->ga_data) + end_idx;
|
||||
isn->isn_arg.jump.jump_where = instr->ga_len;
|
||||
// If the types differ, the result has a more generic type.
|
||||
type2 = ((type_T **)stack->ga_data)[stack->ga_len - 1];
|
||||
common_type(type1, type2, &type2, cctx->ctx_type_list);
|
||||
|
||||
// jump here from JUMP_ALWAYS
|
||||
isn = ((isn_T *)instr->ga_data) + end_idx;
|
||||
isn->isn_arg.jump.jump_where = instr->ga_len;
|
||||
}
|
||||
|
||||
cctx->ctx_skip = save_skip;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Toplevel expression.
|
||||
*/
|
||||
static int
|
||||
compile_expr0(char_u **arg, cctx_T *cctx)
|
||||
{
|
||||
ppconst_T ppconst;
|
||||
|
||||
CLEAR_FIELD(ppconst);
|
||||
if (compile_expr1(arg, cctx, &ppconst) == FAIL)
|
||||
{
|
||||
clear_ppconst(&ppconst);
|
||||
return FAIL;
|
||||
}
|
||||
if (generate_ppconst(cctx, &ppconst) == FAIL)
|
||||
return FAIL;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* compile "return [expr]"
|
||||
*/
|
||||
@@ -4591,7 +4742,7 @@ compile_return(char_u *arg, int set_return_type, cctx_T *cctx)
|
||||
if (*p != NUL && *p != '|' && *p != '\n')
|
||||
{
|
||||
// compile return argument into instructions
|
||||
if (compile_expr1(&p, cctx) == FAIL)
|
||||
if (compile_expr0(&p, cctx) == FAIL)
|
||||
return NULL;
|
||||
|
||||
stack_type = ((type_T **)stack->ga_data)[stack->ga_len - 1];
|
||||
@@ -5075,7 +5226,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
|
||||
--cctx->ctx_locals.ga_len;
|
||||
instr_count = instr->ga_len;
|
||||
p = skipwhite(p + oplen);
|
||||
r = compile_expr1(&p, cctx);
|
||||
r = compile_expr0(&p, cctx);
|
||||
if (new_local)
|
||||
++cctx->ctx_locals.ga_len;
|
||||
if (r == FAIL)
|
||||
@@ -5526,20 +5677,28 @@ compile_if(char_u *arg, cctx_T *cctx)
|
||||
{
|
||||
char_u *p = arg;
|
||||
garray_T *instr = &cctx->ctx_instr;
|
||||
int instr_count = instr->ga_len;
|
||||
scope_T *scope;
|
||||
typval_T tv;
|
||||
ppconst_T ppconst;
|
||||
|
||||
// compile "expr"; if we know it evaluates to FALSE skip the block
|
||||
tv.v_type = VAR_UNKNOWN;
|
||||
if (evaluate_const_expr1(&p, cctx, &tv) == OK)
|
||||
cctx->ctx_skip = tv2bool(&tv) ? FALSE : TRUE;
|
||||
else
|
||||
cctx->ctx_skip = MAYBE;
|
||||
clear_tv(&tv);
|
||||
if (cctx->ctx_skip == MAYBE)
|
||||
CLEAR_FIELD(ppconst);
|
||||
if (compile_expr1(&p, cctx, &ppconst) == FAIL)
|
||||
{
|
||||
p = arg;
|
||||
if (compile_expr1(&p, cctx) == FAIL)
|
||||
clear_ppconst(&ppconst);
|
||||
return NULL;
|
||||
}
|
||||
if (instr->ga_len == instr_count && ppconst.pp_used == 1)
|
||||
{
|
||||
// The expression results in a constant.
|
||||
// TODO: how about nesting?
|
||||
cctx->ctx_skip = tv2bool(&ppconst.pp_tv[0]) ? FALSE : TRUE;
|
||||
clear_ppconst(&ppconst);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not a constant, generate instructions for the expression.
|
||||
cctx->ctx_skip = MAYBE;
|
||||
if (generate_ppconst(cctx, &ppconst) == FAIL)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -5595,7 +5754,7 @@ compile_elseif(char_u *arg, cctx_T *cctx)
|
||||
if (cctx->ctx_skip == MAYBE)
|
||||
{
|
||||
p = arg;
|
||||
if (compile_expr1(&p, cctx) == FAIL)
|
||||
if (compile_expr0(&p, cctx) == FAIL)
|
||||
return NULL;
|
||||
|
||||
// "where" is set when ":elseif", "else" or ":endif" is found
|
||||
@@ -5754,7 +5913,7 @@ compile_for(char_u *arg, cctx_T *cctx)
|
||||
|
||||
// compile "expr", it remains on the stack until "endfor"
|
||||
arg = p;
|
||||
if (compile_expr1(&arg, cctx) == FAIL)
|
||||
if (compile_expr0(&arg, cctx) == FAIL)
|
||||
{
|
||||
drop_scope(cctx);
|
||||
return NULL;
|
||||
@@ -5844,7 +6003,7 @@ compile_while(char_u *arg, cctx_T *cctx)
|
||||
scope->se_u.se_while.ws_top_label = instr->ga_len;
|
||||
|
||||
// compile "expr"
|
||||
if (compile_expr1(&p, cctx) == FAIL)
|
||||
if (compile_expr0(&p, cctx) == FAIL)
|
||||
return NULL;
|
||||
|
||||
// "while_end" is set when ":endwhile" is found
|
||||
@@ -6219,7 +6378,7 @@ compile_throw(char_u *arg, cctx_T *cctx UNUSED)
|
||||
{
|
||||
char_u *p = skipwhite(arg);
|
||||
|
||||
if (compile_expr1(&p, cctx) == FAIL)
|
||||
if (compile_expr0(&p, cctx) == FAIL)
|
||||
return NULL;
|
||||
if (may_generate_2STRING(-1, cctx) == FAIL)
|
||||
return NULL;
|
||||
@@ -6243,7 +6402,7 @@ compile_mult_expr(char_u *arg, int cmdidx, cctx_T *cctx)
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (compile_expr1(&p, cctx) == FAIL)
|
||||
if (compile_expr0(&p, cctx) == FAIL)
|
||||
return NULL;
|
||||
++count;
|
||||
p = skipwhite(p);
|
||||
@@ -6305,7 +6464,7 @@ compile_exec(char_u *line, exarg_T *eap, cctx_T *cctx)
|
||||
++count;
|
||||
}
|
||||
p += 2;
|
||||
if (compile_expr1(&p, cctx) == FAIL)
|
||||
if (compile_expr0(&p, cctx) == FAIL)
|
||||
return NULL;
|
||||
may_generate_2STRING(-1, cctx);
|
||||
++count;
|
||||
@@ -6423,7 +6582,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
|
||||
|
||||
ufunc->uf_def_arg_idx[i] = instr->ga_len;
|
||||
arg = ((char_u **)(ufunc->uf_def_args.ga_data))[i];
|
||||
if (compile_expr1(&arg, &cctx) == FAIL)
|
||||
if (compile_expr0(&arg, &cctx) == FAIL)
|
||||
goto erret;
|
||||
|
||||
// If no type specified use the type of the default value.
|
||||
@@ -6609,7 +6768,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
|
||||
if (ea.cmdidx == CMD_eval)
|
||||
{
|
||||
p = ea.cmd;
|
||||
if (compile_expr1(&p, &cctx) == FAIL)
|
||||
if (compile_expr0(&p, &cctx) == FAIL)
|
||||
goto erret;
|
||||
|
||||
// drop the return value
|
||||
|
Reference in New Issue
Block a user