mirror of
https://github.com/vim/vim.git
synced 2025-09-25 03:54:15 -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
|
enddef
|
||||||
|
|
||||||
def Test_disassemble_compare()
|
def Test_disassemble_compare()
|
||||||
" TODO: COMPAREFUNC
|
|
||||||
let cases = [
|
let cases = [
|
||||||
['true == false', 'COMPAREBOOL =='],
|
['true == isFalse', 'COMPAREBOOL =='],
|
||||||
['true != false', 'COMPAREBOOL !='],
|
['true != isFalse', 'COMPAREBOOL !='],
|
||||||
['v:none == v:null', 'COMPARESPECIAL =='],
|
['v:none == isNull', 'COMPARESPECIAL =='],
|
||||||
['v:none != v:null', 'COMPARESPECIAL !='],
|
['v:none != isNull', 'COMPARESPECIAL !='],
|
||||||
|
|
||||||
['111 == 222', 'COMPARENR =='],
|
['111 == aNumber', 'COMPARENR =='],
|
||||||
['111 != 222', 'COMPARENR !='],
|
['111 != aNumber', 'COMPARENR !='],
|
||||||
['111 > 222', 'COMPARENR >'],
|
['111 > aNumber', 'COMPARENR >'],
|
||||||
['111 < 222', 'COMPARENR <'],
|
['111 < aNumber', 'COMPARENR <'],
|
||||||
['111 >= 222', 'COMPARENR >='],
|
['111 >= aNumber', 'COMPARENR >='],
|
||||||
['111 <= 222', 'COMPARENR <='],
|
['111 <= aNumber', 'COMPARENR <='],
|
||||||
['111 =~ 222', 'COMPARENR =\~'],
|
['111 =~ aNumber', 'COMPARENR =\~'],
|
||||||
['111 !~ 222', 'COMPARENR !\~'],
|
['111 !~ aNumber', 'COMPARENR !\~'],
|
||||||
|
|
||||||
['"xx" != "yy"', 'COMPARESTRING !='],
|
['"xx" != aString', 'COMPARESTRING !='],
|
||||||
['"xx" > "yy"', 'COMPARESTRING >'],
|
['"xx" > aString', 'COMPARESTRING >'],
|
||||||
['"xx" < "yy"', 'COMPARESTRING <'],
|
['"xx" < aString', 'COMPARESTRING <'],
|
||||||
['"xx" >= "yy"', 'COMPARESTRING >='],
|
['"xx" >= aString', 'COMPARESTRING >='],
|
||||||
['"xx" <= "yy"', 'COMPARESTRING <='],
|
['"xx" <= aString', 'COMPARESTRING <='],
|
||||||
['"xx" =~ "yy"', 'COMPARESTRING =\~'],
|
['"xx" =~ aString', 'COMPARESTRING =\~'],
|
||||||
['"xx" !~ "yy"', 'COMPARESTRING !\~'],
|
['"xx" !~ aString', 'COMPARESTRING !\~'],
|
||||||
['"xx" is "yy"', 'COMPARESTRING is'],
|
['"xx" is aString', 'COMPARESTRING is'],
|
||||||
['"xx" isnot "yy"', 'COMPARESTRING isnot'],
|
['"xx" isnot aString', 'COMPARESTRING isnot'],
|
||||||
|
|
||||||
['0z11 == 0z22', 'COMPAREBLOB =='],
|
['0z11 == aBlob', 'COMPAREBLOB =='],
|
||||||
['0z11 != 0z22', 'COMPAREBLOB !='],
|
['0z11 != aBlob', 'COMPAREBLOB !='],
|
||||||
['0z11 is 0z22', 'COMPAREBLOB is'],
|
['0z11 is aBlob', 'COMPAREBLOB is'],
|
||||||
['0z11 isnot 0z22', 'COMPAREBLOB isnot'],
|
['0z11 isnot aBlob', 'COMPAREBLOB isnot'],
|
||||||
|
|
||||||
['[1,2] == [3,4]', 'COMPARELIST =='],
|
['[1, 2] == aList', 'COMPARELIST =='],
|
||||||
['[1,2] != [3,4]', 'COMPARELIST !='],
|
['[1, 2] != aList', 'COMPARELIST !='],
|
||||||
['[1,2] is [3,4]', 'COMPARELIST is'],
|
['[1, 2] is aList', 'COMPARELIST is'],
|
||||||
['[1,2] isnot [3,4]', 'COMPARELIST isnot'],
|
['[1, 2] isnot aList', 'COMPARELIST isnot'],
|
||||||
|
|
||||||
['#{a:1} == #{x:2}', 'COMPAREDICT =='],
|
['#{a: 1} == aDict', 'COMPAREDICT =='],
|
||||||
['#{a:1} != #{x:2}', 'COMPAREDICT !='],
|
['#{a: 1} != aDict', 'COMPAREDICT !='],
|
||||||
['#{a:1} is #{x:2}', 'COMPAREDICT is'],
|
['#{a: 1} is aDict', 'COMPAREDICT is'],
|
||||||
['#{a:1} isnot #{x:2}', 'COMPAREDICT isnot'],
|
['#{a: 1} isnot aDict', 'COMPAREDICT isnot'],
|
||||||
|
|
||||||
['{->33} == {->44}', 'COMPAREFUNC =='],
|
['{->33} == {->44}', 'COMPAREFUNC =='],
|
||||||
['{->33} != {->44}', 'COMPAREFUNC !='],
|
['{->33} != {->44}', 'COMPAREFUNC !='],
|
||||||
@@ -871,22 +870,33 @@ def Test_disassemble_compare()
|
|||||||
['77 is g:xx', 'COMPAREANY is'],
|
['77 is g:xx', 'COMPAREANY is'],
|
||||||
['77 isnot g:xx', 'COMPAREANY isnot'],
|
['77 isnot g:xx', 'COMPAREANY isnot'],
|
||||||
]
|
]
|
||||||
|
let floatDecl = ''
|
||||||
if has('float')
|
if has('float')
|
||||||
cases->extend([
|
cases->extend([
|
||||||
['1.1 == 2.2', 'COMPAREFLOAT =='],
|
['1.1 == aFloat', 'COMPAREFLOAT =='],
|
||||||
['1.1 != 2.2', 'COMPAREFLOAT !='],
|
['1.1 != aFloat', 'COMPAREFLOAT !='],
|
||||||
['1.1 > 2.2', 'COMPAREFLOAT >'],
|
['1.1 > aFloat', 'COMPAREFLOAT >'],
|
||||||
['1.1 < 2.2', 'COMPAREFLOAT <'],
|
['1.1 < aFloat', 'COMPAREFLOAT <'],
|
||||||
['1.1 >= 2.2', 'COMPAREFLOAT >='],
|
['1.1 >= aFloat', 'COMPAREFLOAT >='],
|
||||||
['1.1 <= 2.2', 'COMPAREFLOAT <='],
|
['1.1 <= aFloat', 'COMPAREFLOAT <='],
|
||||||
['1.1 =~ 2.2', 'COMPAREFLOAT =\~'],
|
['1.1 =~ aFloat', 'COMPAREFLOAT =\~'],
|
||||||
['1.1 !~ 2.2', 'COMPAREFLOAT !\~'],
|
['1.1 !~ aFloat', 'COMPAREFLOAT !\~'],
|
||||||
])
|
])
|
||||||
|
floatDecl = 'let aFloat = 2.2'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let nr = 1
|
let nr = 1
|
||||||
for case in cases
|
for case in cases
|
||||||
|
" declare local variables to get a non-constant with the right type
|
||||||
writefile(['def TestCase' .. nr .. '()',
|
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],
|
' if ' .. case[0],
|
||||||
' echo 42'
|
' echo 42'
|
||||||
' endif',
|
' endif',
|
||||||
@@ -896,7 +906,7 @@ def Test_disassemble_compare()
|
|||||||
assert_match('TestCase' .. nr .. '.*' ..
|
assert_match('TestCase' .. nr .. '.*' ..
|
||||||
'if ' .. substitute(case[0], '[[~]', '\\\0', 'g') .. '.*' ..
|
'if ' .. substitute(case[0], '[[~]', '\\\0', 'g') .. '.*' ..
|
||||||
'\d \(PUSH\|FUNCREF\).*' ..
|
'\d \(PUSH\|FUNCREF\).*' ..
|
||||||
'\d \(PUSH\|FUNCREF\|LOADG\).*' ..
|
'\d \(PUSH\|FUNCREF\|LOAD\).*' ..
|
||||||
'\d ' .. case[1] .. '.*' ..
|
'\d ' .. case[1] .. '.*' ..
|
||||||
'\d JUMP_IF_FALSE -> \d\+.*',
|
'\d JUMP_IF_FALSE -> \d\+.*',
|
||||||
instr)
|
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 number with string')
|
||||||
call CheckDefFailure(["let x = '1' == 2"], 'Cannot compare string with number')
|
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 = RetVoid() == 1"], 'Cannot compare void with number')
|
||||||
|
|
||||||
call CheckDefFailure(["let x = true > false"], 'Cannot compare bool with bool')
|
call CheckDefFailure(["let x = true > false"], 'Cannot compare bool with bool')
|
||||||
|
@@ -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 */
|
||||||
|
/**/
|
||||||
|
719,
|
||||||
/**/
|
/**/
|
||||||
718,
|
718,
|
||||||
/**/
|
/**/
|
||||||
|
@@ -136,9 +136,7 @@ struct cctx_S {
|
|||||||
static char e_var_notfound[] = N_("E1001: variable not found: %s");
|
static char e_var_notfound[] = N_("E1001: variable not found: %s");
|
||||||
static char e_syntax_at[] = N_("E1002: Syntax error at %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_expr0(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 void delete_def_function_contents(dfunc_T *dfunc);
|
static void delete_def_function_contents(dfunc_T *dfunc);
|
||||||
static void arg_type_mismatch(type_T *expected, type_T *actual, int argidx);
|
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);
|
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
|
static isntype_T
|
||||||
generate_COMPARE(cctx_T *cctx, exptype_T exptype, int ic)
|
get_compare_isn(exptype_T exptype, vartype_T type1, vartype_T type2)
|
||||||
{
|
{
|
||||||
isntype_T isntype = ISN_DROP;
|
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)
|
if (type1 == VAR_UNKNOWN)
|
||||||
type1 = VAR_ANY;
|
type1 = VAR_ANY;
|
||||||
if (type2 == VAR_UNKNOWN)
|
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"),
|
semsg(_("E1037: Cannot use \"%s\" with %s"),
|
||||||
exptype == EXPR_IS ? "is" : "isnot" , vartype_name(type1));
|
exptype == EXPR_IS ? "is" : "isnot" , vartype_name(type1));
|
||||||
return FAIL;
|
return ISN_DROP;
|
||||||
}
|
}
|
||||||
if (isntype == ISN_DROP
|
if (isntype == ISN_DROP
|
||||||
|| ((exptype != EXPR_EQUAL && exptype != EXPR_NEQUAL
|
|| ((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"),
|
semsg(_("E1072: Cannot compare %s with %s"),
|
||||||
vartype_name(type1), vartype_name(type2));
|
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)
|
if ((isn = generate_instr(cctx, isntype)) == NULL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
@@ -2340,6 +2353,91 @@ may_get_next_line(char_u *whitep, char_u **arg, cctx_T *cctx)
|
|||||||
return OK;
|
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
|
* Generate an instruction to load script-local variable "name", without the
|
||||||
* leading "s:".
|
* leading "s:".
|
||||||
@@ -2525,12 +2623,6 @@ compile_load(char_u **arg, char_u *end_arg, cctx_T *cctx, int error)
|
|||||||
gen_load = TRUE;
|
gen_load = TRUE;
|
||||||
}
|
}
|
||||||
else
|
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
|
// "var" can be script-local even without using "s:" if it
|
||||||
// already exists.
|
// already exists.
|
||||||
@@ -2546,7 +2638,6 @@ compile_load(char_u **arg, char_u *end_arg, cctx_T *cctx, int error)
|
|||||||
res = generate_funcref(cctx, name);
|
res = generate_funcref(cctx, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (gen_load)
|
if (gen_load)
|
||||||
res = generate_LOAD(cctx, ISN_LOAD, idx, NULL, type);
|
res = generate_LOAD(cctx, ISN_LOAD, idx, NULL, type);
|
||||||
if (gen_load_outer)
|
if (gen_load_outer)
|
||||||
@@ -2588,7 +2679,7 @@ compile_arguments(char_u **arg, cctx_T *cctx, int *argcount)
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compile_expr1(&p, cctx) == FAIL)
|
if (compile_expr0(&p, cctx) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
++*argcount;
|
++*argcount;
|
||||||
|
|
||||||
@@ -2621,7 +2712,12 @@ failret:
|
|||||||
* BCALL / DCALL / UCALL
|
* BCALL / DCALL / UCALL
|
||||||
*/
|
*/
|
||||||
static int
|
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 *name = *arg;
|
||||||
char_u *p;
|
char_u *p;
|
||||||
@@ -2633,6 +2729,36 @@ compile_call(char_u **arg, size_t varlen, cctx_T *cctx, int argcount_init)
|
|||||||
ufunc_T *ufunc;
|
ufunc_T *ufunc;
|
||||||
int res = FAIL;
|
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))
|
if (varlen >= sizeof(namebuf))
|
||||||
{
|
{
|
||||||
semsg(_("E1011: name too long: %s"), name);
|
semsg(_("E1011: name too long: %s"), name);
|
||||||
@@ -2791,7 +2917,7 @@ compile_list(char_u **arg, cctx_T *cctx)
|
|||||||
p += STRLEN(p);
|
p += STRLEN(p);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (compile_expr1(&p, cctx) == FAIL)
|
if (compile_expr0(&p, cctx) == FAIL)
|
||||||
break;
|
break;
|
||||||
++count;
|
++count;
|
||||||
if (*p == ',')
|
if (*p == ',')
|
||||||
@@ -2929,7 +3055,7 @@ compile_dict(char_u **arg, cctx_T *cctx, int literal)
|
|||||||
{
|
{
|
||||||
isn_T *isn;
|
isn_T *isn;
|
||||||
|
|
||||||
if (compile_expr1(arg, cctx) == FAIL)
|
if (compile_expr0(arg, cctx) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
// TODO: check type is string
|
// TODO: check type is string
|
||||||
isn = ((isn_T *)instr->ga_data) + instr->ga_len - 1;
|
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);
|
*arg = skipwhite(*arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compile_expr1(arg, cctx) == FAIL)
|
if (compile_expr0(arg, cctx) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
++count;
|
++count;
|
||||||
|
|
||||||
@@ -3626,90 +3752,7 @@ evaluate_const_expr1(char_u **arg, cctx_T *cctx, typval_T *tv)
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Structure passed between the compile_expr* functions to keep track of
|
static int compile_expr3(char_u **arg, cctx_T *cctx, ppconst_T *ppconst);
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compile code to apply '-', '+' and '!'.
|
* Compile code to apply '-', '+' and '!'.
|
||||||
@@ -3825,7 +3868,7 @@ compile_subscript(
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
// TODO: base value may not be the first argument
|
// 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;
|
return FAIL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3841,7 +3884,7 @@ compile_subscript(
|
|||||||
// TODO: more arguments
|
// TODO: more arguments
|
||||||
// TODO: dict member dict['name']
|
// TODO: dict member dict['name']
|
||||||
*arg = skipwhite(*arg + 1);
|
*arg = skipwhite(*arg + 1);
|
||||||
if (compile_expr1(arg, cctx) == FAIL)
|
if (compile_expr0(arg, cctx) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
if (**arg != ']')
|
if (**arg != ']')
|
||||||
@@ -3990,6 +4033,34 @@ compile_expr7(
|
|||||||
ret = NOTDONE;
|
ret = NOTDONE;
|
||||||
break;
|
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]
|
* List: [expr, expr]
|
||||||
*/
|
*/
|
||||||
@@ -4047,7 +4118,7 @@ compile_expr7(
|
|||||||
* nested expression: (expression).
|
* nested expression: (expression).
|
||||||
*/
|
*/
|
||||||
case '(': *arg = skipwhite(*arg + 1);
|
case '(': *arg = skipwhite(*arg + 1);
|
||||||
ret = compile_expr1(arg, cctx); // recursive!
|
ret = compile_expr0(arg, cctx); // recursive!
|
||||||
*arg = skipwhite(*arg);
|
*arg = skipwhite(*arg);
|
||||||
if (**arg == ')')
|
if (**arg == ')')
|
||||||
++*arg;
|
++*arg;
|
||||||
@@ -4074,8 +4145,11 @@ compile_expr7(
|
|||||||
}
|
}
|
||||||
start_leader = end_leader; // don't apply again below
|
start_leader = end_leader; // don't apply again below
|
||||||
|
|
||||||
// A constant expression can possibly be handled compile time, return
|
if (cctx->ctx_skip == TRUE)
|
||||||
// the value instead of generating code.
|
clear_tv(rettv);
|
||||||
|
else
|
||||||
|
// A constant expression can possibly be handled compile time,
|
||||||
|
// return the value instead of generating code.
|
||||||
++ppconst->pp_used;
|
++ppconst->pp_used;
|
||||||
}
|
}
|
||||||
else if (ret == NOTDONE)
|
else if (ret == NOTDONE)
|
||||||
@@ -4083,9 +4157,6 @@ compile_expr7(
|
|||||||
char_u *p;
|
char_u *p;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
if (generate_ppconst(cctx, ppconst) == FAIL)
|
|
||||||
return FAIL;
|
|
||||||
|
|
||||||
if (!eval_isnamec1(**arg))
|
if (!eval_isnamec1(**arg))
|
||||||
{
|
{
|
||||||
semsg(_("E1015: Name expected: %s"), *arg);
|
semsg(_("E1015: Name expected: %s"), *arg);
|
||||||
@@ -4095,9 +4166,15 @@ compile_expr7(
|
|||||||
// "name" or "name()"
|
// "name" or "name()"
|
||||||
p = to_name_end(*arg, TRUE);
|
p = to_name_end(*arg, TRUE);
|
||||||
if (*p == '(')
|
if (*p == '(')
|
||||||
r = compile_call(arg, p - *arg, cctx, 0);
|
{
|
||||||
|
r = compile_call(arg, p - *arg, cctx, ppconst, 0);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
if (generate_ppconst(cctx, ppconst) == FAIL)
|
||||||
|
return FAIL;
|
||||||
r = compile_load(arg, p, cctx, TRUE);
|
r = compile_load(arg, p, cctx, TRUE);
|
||||||
|
}
|
||||||
if (r == FAIL)
|
if (r == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
@@ -4105,8 +4182,17 @@ compile_expr7(
|
|||||||
// Handle following "[]", ".member", etc.
|
// Handle following "[]", ".member", etc.
|
||||||
// Then deal with prefixed '-', '+' and '!', if not done already.
|
// Then deal with prefixed '-', '+' and '!', if not done already.
|
||||||
if (compile_subscript(arg, cctx, &start_leader, end_leader,
|
if (compile_subscript(arg, cctx, &start_leader, end_leader,
|
||||||
ppconst) == FAIL
|
ppconst) == FAIL)
|
||||||
|| compile_leader(cctx, start_leader, end_leader) == 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 FAIL;
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
@@ -4117,10 +4203,7 @@ compile_expr7(
|
|||||||
* % number modulo
|
* % number modulo
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
compile_expr6(
|
compile_expr6(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
|
||||||
char_u **arg,
|
|
||||||
cctx_T *cctx,
|
|
||||||
ppconst_T *ppconst)
|
|
||||||
{
|
{
|
||||||
char_u *op;
|
char_u *op;
|
||||||
int ppconst_used = ppconst->pp_used;
|
int ppconst_used = ppconst->pp_used;
|
||||||
@@ -4191,21 +4274,15 @@ compile_expr6(
|
|||||||
* .. string concatenation
|
* .. string concatenation
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
compile_expr5(char_u **arg, cctx_T *cctx)
|
compile_expr5(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
|
||||||
{
|
{
|
||||||
char_u *op;
|
char_u *op;
|
||||||
int oplen;
|
int oplen;
|
||||||
ppconst_T ppconst;
|
int ppconst_used = ppconst->pp_used;
|
||||||
int ppconst_used = 0;
|
|
||||||
|
|
||||||
CLEAR_FIELD(ppconst);
|
|
||||||
|
|
||||||
// get the first variable
|
// get the first variable
|
||||||
if (compile_expr6(arg, cctx, &ppconst) == FAIL)
|
if (compile_expr6(arg, cctx, ppconst) == FAIL)
|
||||||
{
|
|
||||||
clear_ppconst(&ppconst);
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Repeat computing, until no "+", "-" or ".." is following.
|
* Repeat computing, until no "+", "-" or ".." is following.
|
||||||
@@ -4221,7 +4298,6 @@ compile_expr5(char_u **arg, cctx_T *cctx)
|
|||||||
{
|
{
|
||||||
char_u buf[3];
|
char_u buf[3];
|
||||||
|
|
||||||
clear_ppconst(&ppconst);
|
|
||||||
vim_strncpy(buf, op, oplen);
|
vim_strncpy(buf, op, oplen);
|
||||||
semsg(_(e_white_both), buf);
|
semsg(_(e_white_both), buf);
|
||||||
return FAIL;
|
return FAIL;
|
||||||
@@ -4229,27 +4305,21 @@ compile_expr5(char_u **arg, cctx_T *cctx)
|
|||||||
|
|
||||||
*arg = skipwhite(op + oplen);
|
*arg = skipwhite(op + oplen);
|
||||||
if (may_get_next_line(op + oplen, arg, cctx) == FAIL)
|
if (may_get_next_line(op + oplen, arg, cctx) == FAIL)
|
||||||
{
|
|
||||||
clear_ppconst(&ppconst);
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
|
||||||
|
|
||||||
// get the second expression
|
// get the second expression
|
||||||
if (compile_expr6(arg, cctx, &ppconst) == FAIL)
|
if (compile_expr6(arg, cctx, ppconst) == FAIL)
|
||||||
{
|
|
||||||
clear_ppconst(&ppconst);
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
|
||||||
|
|
||||||
if (ppconst.pp_used == ppconst_used + 2
|
if (ppconst->pp_used == ppconst_used + 2
|
||||||
&& (*op == '.'
|
&& (*op == '.'
|
||||||
? (ppconst.pp_tv[ppconst_used].v_type == VAR_STRING
|
? (ppconst->pp_tv[ppconst_used].v_type == VAR_STRING
|
||||||
&& ppconst.pp_tv[ppconst_used + 1].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].v_type == VAR_NUMBER
|
||||||
&& ppconst.pp_tv[ppconst_used + 1].v_type == VAR_NUMBER)))
|
&& ppconst->pp_tv[ppconst_used + 1].v_type == VAR_NUMBER)))
|
||||||
{
|
{
|
||||||
typval_T *tv1 = &ppconst.pp_tv[ppconst_used];
|
typval_T *tv1 = &ppconst->pp_tv[ppconst_used];
|
||||||
typval_T *tv2 = &ppconst.pp_tv[ppconst_used + 1];
|
typval_T *tv2 = &ppconst->pp_tv[ppconst_used + 1];
|
||||||
|
|
||||||
// concat/subtract/add constant numbers
|
// concat/subtract/add constant numbers
|
||||||
if (*op == '+')
|
if (*op == '+')
|
||||||
@@ -4266,7 +4336,7 @@ compile_expr5(char_u **arg, cctx_T *cctx)
|
|||||||
tv1->vval.v_string = alloc((int)(len1 + STRLEN(s2) + 1));
|
tv1->vval.v_string = alloc((int)(len1 + STRLEN(s2) + 1));
|
||||||
if (tv1->vval.v_string == NULL)
|
if (tv1->vval.v_string == NULL)
|
||||||
{
|
{
|
||||||
clear_ppconst(&ppconst);
|
clear_ppconst(ppconst);
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
mch_memmove(tv1->vval.v_string, s1, len1);
|
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(s1);
|
||||||
vim_free(s2);
|
vim_free(s2);
|
||||||
}
|
}
|
||||||
--ppconst.pp_used;
|
--ppconst->pp_used;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
generate_ppconst(cctx, &ppconst);
|
generate_ppconst(cctx, ppconst);
|
||||||
if (*op == '.')
|
if (*op == '.')
|
||||||
{
|
{
|
||||||
if (may_generate_2STRING(-2, cctx) == FAIL
|
if (may_generate_2STRING(-2, cctx) == FAIL
|
||||||
|| may_generate_2STRING(-1, cctx) == FAIL)
|
|| may_generate_2STRING(-1, cctx) == FAIL)
|
||||||
{
|
|
||||||
clear_ppconst(&ppconst);
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
|
||||||
generate_instr_drop(cctx, ISN_CONCAT, 1);
|
generate_instr_drop(cctx, ISN_CONCAT, 1);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -4294,9 +4361,6 @@ compile_expr5(char_u **arg, cctx_T *cctx)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: move to caller
|
|
||||||
generate_ppconst(cctx, &ppconst);
|
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4318,15 +4382,16 @@ compile_expr5(char_u **arg, cctx_T *cctx)
|
|||||||
* COMPARE one of the compare instructions
|
* COMPARE one of the compare instructions
|
||||||
*/
|
*/
|
||||||
static int
|
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;
|
exptype_T type = EXPR_UNKNOWN;
|
||||||
char_u *p;
|
char_u *p;
|
||||||
int len = 2;
|
int len = 2;
|
||||||
int type_is = FALSE;
|
int type_is = FALSE;
|
||||||
|
int ppconst_used = ppconst->pp_used;
|
||||||
|
|
||||||
// get the first variable
|
// get the first variable
|
||||||
if (compile_expr5(arg, cctx) == FAIL)
|
if (compile_expr5(arg, cctx, ppconst) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
p = skipwhite(*arg);
|
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)
|
if (may_get_next_line(p + len, arg, cctx) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
if (compile_expr5(arg, cctx) == FAIL)
|
if (compile_expr5(arg, cctx, ppconst) == FAIL)
|
||||||
return 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;
|
return OK;
|
||||||
@@ -4382,7 +4471,12 @@ compile_expr4(char_u **arg, cctx_T *cctx)
|
|||||||
* Compile || or &&.
|
* Compile || or &&.
|
||||||
*/
|
*/
|
||||||
static int
|
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);
|
char_u *p = skipwhite(*arg);
|
||||||
int opchar = *op;
|
int opchar = *op;
|
||||||
@@ -4404,6 +4498,9 @@ compile_and_or(char_u **arg, cctx_T *cctx, char *op)
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: use ppconst if the value is a constant
|
||||||
|
generate_ppconst(cctx, ppconst);
|
||||||
|
|
||||||
if (ga_grow(&end_ga, 1) == FAIL)
|
if (ga_grow(&end_ga, 1) == FAIL)
|
||||||
{
|
{
|
||||||
ga_clear(&end_ga);
|
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)
|
if (may_get_next_line(p + 2, arg, cctx) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
if ((opchar == '|' ? compile_expr3(arg, cctx)
|
if ((opchar == '|' ? compile_expr3(arg, cctx, ppconst)
|
||||||
: compile_expr4(arg, cctx)) == FAIL)
|
: compile_expr4(arg, cctx, ppconst)) == FAIL)
|
||||||
{
|
{
|
||||||
ga_clear(&end_ga);
|
ga_clear(&end_ga);
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
p = skipwhite(*arg);
|
p = skipwhite(*arg);
|
||||||
}
|
}
|
||||||
|
generate_ppconst(cctx, ppconst);
|
||||||
|
|
||||||
// Fill in the end label in all jumps.
|
// Fill in the end label in all jumps.
|
||||||
while (end_ga.ga_len > 0)
|
while (end_ga.ga_len > 0)
|
||||||
@@ -4456,14 +4554,16 @@ compile_and_or(char_u **arg, cctx_T *cctx, char *op)
|
|||||||
* end:
|
* end:
|
||||||
*/
|
*/
|
||||||
static int
|
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
|
// get the first variable
|
||||||
if (compile_expr4(arg, cctx) == FAIL)
|
if (compile_expr4(arg, cctx, ppconst) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
// || and && work almost the same
|
// || 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:
|
* end:
|
||||||
*/
|
*/
|
||||||
static int
|
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
|
// eval the first expression
|
||||||
if (compile_expr3(arg, cctx) == FAIL)
|
if (compile_expr3(arg, cctx, ppconst) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
// || and && work almost the same
|
// || 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:
|
* end:
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
compile_expr1(char_u **arg, cctx_T *cctx)
|
compile_expr1(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
|
||||||
{
|
{
|
||||||
char_u *p;
|
char_u *p;
|
||||||
|
int ppconst_used = ppconst->pp_used;
|
||||||
|
|
||||||
// Evaluate the first expression.
|
// Evaluate the first expression.
|
||||||
if (compile_expr2(arg, cctx) == FAIL)
|
if (compile_expr2(arg, cctx, ppconst) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
p = skipwhite(*arg);
|
p = skipwhite(*arg);
|
||||||
@@ -4518,6 +4621,9 @@ compile_expr1(char_u **arg, cctx_T *cctx)
|
|||||||
isn_T *isn;
|
isn_T *isn;
|
||||||
type_T *type1;
|
type_T *type1;
|
||||||
type_T *type2;
|
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]))
|
if (!IS_WHITE_OR_NUL(**arg) || !IS_WHITE_OR_NUL(p[1]))
|
||||||
{
|
{
|
||||||
@@ -4525,16 +4631,33 @@ compile_expr1(char_u **arg, cctx_T *cctx)
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
generate_JUMP(cctx, JUMP_IF_FALSE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
// evaluate the second expression; any type is accepted
|
// evaluate the second expression; any type is accepted
|
||||||
*arg = skipwhite(p + 1);
|
*arg = skipwhite(p + 1);
|
||||||
if (may_get_next_line(p + 1, arg, cctx) == FAIL)
|
if (may_get_next_line(p + 1, arg, cctx) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
if (compile_expr1(arg, cctx, ppconst) == FAIL)
|
||||||
if (compile_expr1(arg, cctx) == FAIL)
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
|
if (!has_const_expr)
|
||||||
|
{
|
||||||
|
generate_ppconst(cctx, ppconst);
|
||||||
|
|
||||||
// remember the type and drop it
|
// remember the type and drop it
|
||||||
--stack->ga_len;
|
--stack->ga_len;
|
||||||
type1 = ((type_T **)stack->ga_data)[stack->ga_len];
|
type1 = ((type_T **)stack->ga_data)[stack->ga_len];
|
||||||
@@ -4545,6 +4668,7 @@ compile_expr1(char_u **arg, cctx_T *cctx)
|
|||||||
// jump here from JUMP_IF_FALSE
|
// jump here from JUMP_IF_FALSE
|
||||||
isn = ((isn_T *)instr->ga_data) + alt_idx;
|
isn = ((isn_T *)instr->ga_data) + alt_idx;
|
||||||
isn->isn_arg.jump.jump_where = instr->ga_len;
|
isn->isn_arg.jump.jump_where = instr->ga_len;
|
||||||
|
}
|
||||||
|
|
||||||
// Check for the ":".
|
// Check for the ":".
|
||||||
p = skipwhite(*arg);
|
p = skipwhite(*arg);
|
||||||
@@ -4560,13 +4684,18 @@ compile_expr1(char_u **arg, cctx_T *cctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// evaluate the third expression
|
// evaluate the third expression
|
||||||
|
if (has_const_expr)
|
||||||
|
cctx->ctx_skip = save_skip == TRUE || const_value;
|
||||||
*arg = skipwhite(p + 1);
|
*arg = skipwhite(p + 1);
|
||||||
if (may_get_next_line(p + 1, arg, cctx) == FAIL)
|
if (may_get_next_line(p + 1, arg, cctx) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
if (compile_expr1(arg, cctx, ppconst) == FAIL)
|
||||||
if (compile_expr1(arg, cctx) == FAIL)
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
|
if (!has_const_expr)
|
||||||
|
{
|
||||||
|
generate_ppconst(cctx, ppconst);
|
||||||
|
|
||||||
// If the types differ, the result has a more generic type.
|
// If the types differ, the result has a more generic type.
|
||||||
type2 = ((type_T **)stack->ga_data)[stack->ga_len - 1];
|
type2 = ((type_T **)stack->ga_data)[stack->ga_len - 1];
|
||||||
common_type(type1, type2, &type2, cctx->ctx_type_list);
|
common_type(type1, type2, &type2, cctx->ctx_type_list);
|
||||||
@@ -4575,6 +4704,28 @@ compile_expr1(char_u **arg, cctx_T *cctx)
|
|||||||
isn = ((isn_T *)instr->ga_data) + end_idx;
|
isn = ((isn_T *)instr->ga_data) + end_idx;
|
||||||
isn->isn_arg.jump.jump_where = instr->ga_len;
|
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;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4591,7 +4742,7 @@ compile_return(char_u *arg, int set_return_type, cctx_T *cctx)
|
|||||||
if (*p != NUL && *p != '|' && *p != '\n')
|
if (*p != NUL && *p != '|' && *p != '\n')
|
||||||
{
|
{
|
||||||
// compile return argument into instructions
|
// compile return argument into instructions
|
||||||
if (compile_expr1(&p, cctx) == FAIL)
|
if (compile_expr0(&p, cctx) == FAIL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
stack_type = ((type_T **)stack->ga_data)[stack->ga_len - 1];
|
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;
|
--cctx->ctx_locals.ga_len;
|
||||||
instr_count = instr->ga_len;
|
instr_count = instr->ga_len;
|
||||||
p = skipwhite(p + oplen);
|
p = skipwhite(p + oplen);
|
||||||
r = compile_expr1(&p, cctx);
|
r = compile_expr0(&p, cctx);
|
||||||
if (new_local)
|
if (new_local)
|
||||||
++cctx->ctx_locals.ga_len;
|
++cctx->ctx_locals.ga_len;
|
||||||
if (r == FAIL)
|
if (r == FAIL)
|
||||||
@@ -5526,20 +5677,28 @@ compile_if(char_u *arg, cctx_T *cctx)
|
|||||||
{
|
{
|
||||||
char_u *p = arg;
|
char_u *p = arg;
|
||||||
garray_T *instr = &cctx->ctx_instr;
|
garray_T *instr = &cctx->ctx_instr;
|
||||||
|
int instr_count = instr->ga_len;
|
||||||
scope_T *scope;
|
scope_T *scope;
|
||||||
typval_T tv;
|
ppconst_T ppconst;
|
||||||
|
|
||||||
// compile "expr"; if we know it evaluates to FALSE skip the block
|
CLEAR_FIELD(ppconst);
|
||||||
tv.v_type = VAR_UNKNOWN;
|
if (compile_expr1(&p, cctx, &ppconst) == FAIL)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
p = arg;
|
clear_ppconst(&ppconst);
|
||||||
if (compile_expr1(&p, cctx) == FAIL)
|
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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5595,7 +5754,7 @@ compile_elseif(char_u *arg, cctx_T *cctx)
|
|||||||
if (cctx->ctx_skip == MAYBE)
|
if (cctx->ctx_skip == MAYBE)
|
||||||
{
|
{
|
||||||
p = arg;
|
p = arg;
|
||||||
if (compile_expr1(&p, cctx) == FAIL)
|
if (compile_expr0(&p, cctx) == FAIL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
// "where" is set when ":elseif", "else" or ":endif" is found
|
// "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"
|
// compile "expr", it remains on the stack until "endfor"
|
||||||
arg = p;
|
arg = p;
|
||||||
if (compile_expr1(&arg, cctx) == FAIL)
|
if (compile_expr0(&arg, cctx) == FAIL)
|
||||||
{
|
{
|
||||||
drop_scope(cctx);
|
drop_scope(cctx);
|
||||||
return NULL;
|
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;
|
scope->se_u.se_while.ws_top_label = instr->ga_len;
|
||||||
|
|
||||||
// compile "expr"
|
// compile "expr"
|
||||||
if (compile_expr1(&p, cctx) == FAIL)
|
if (compile_expr0(&p, cctx) == FAIL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
// "while_end" is set when ":endwhile" is found
|
// "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);
|
char_u *p = skipwhite(arg);
|
||||||
|
|
||||||
if (compile_expr1(&p, cctx) == FAIL)
|
if (compile_expr0(&p, cctx) == FAIL)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (may_generate_2STRING(-1, cctx) == FAIL)
|
if (may_generate_2STRING(-1, cctx) == FAIL)
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -6243,7 +6402,7 @@ compile_mult_expr(char_u *arg, int cmdidx, cctx_T *cctx)
|
|||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
if (compile_expr1(&p, cctx) == FAIL)
|
if (compile_expr0(&p, cctx) == FAIL)
|
||||||
return NULL;
|
return NULL;
|
||||||
++count;
|
++count;
|
||||||
p = skipwhite(p);
|
p = skipwhite(p);
|
||||||
@@ -6305,7 +6464,7 @@ compile_exec(char_u *line, exarg_T *eap, cctx_T *cctx)
|
|||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
p += 2;
|
p += 2;
|
||||||
if (compile_expr1(&p, cctx) == FAIL)
|
if (compile_expr0(&p, cctx) == FAIL)
|
||||||
return NULL;
|
return NULL;
|
||||||
may_generate_2STRING(-1, cctx);
|
may_generate_2STRING(-1, cctx);
|
||||||
++count;
|
++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;
|
ufunc->uf_def_arg_idx[i] = instr->ga_len;
|
||||||
arg = ((char_u **)(ufunc->uf_def_args.ga_data))[i];
|
arg = ((char_u **)(ufunc->uf_def_args.ga_data))[i];
|
||||||
if (compile_expr1(&arg, &cctx) == FAIL)
|
if (compile_expr0(&arg, &cctx) == FAIL)
|
||||||
goto erret;
|
goto erret;
|
||||||
|
|
||||||
// If no type specified use the type of the default value.
|
// 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)
|
if (ea.cmdidx == CMD_eval)
|
||||||
{
|
{
|
||||||
p = ea.cmd;
|
p = ea.cmd;
|
||||||
if (compile_expr1(&p, &cctx) == FAIL)
|
if (compile_expr0(&p, &cctx) == FAIL)
|
||||||
goto erret;
|
goto erret;
|
||||||
|
|
||||||
// drop the return value
|
// drop the return value
|
||||||
|
Reference in New Issue
Block a user