mirror of
https://github.com/vim/vim.git
synced 2025-09-25 03:54:15 -04:00
patch 8.2.0717: Vim9: postponed constant expressions does not scale
Problem: Vim9: postponed constant expressions does not scale. Solution: Add a structure to pass around postponed constants.
This commit is contained in:
@@ -1039,12 +1039,22 @@ def s:ConcatStrings(): string
|
|||||||
return 'one' .. 'two' .. 'three'
|
return 'one' .. 'two' .. 'three'
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
def s:ComputeConst(): number
|
||||||
|
return 2 + 3 * 4 / 6 + 7
|
||||||
|
enddef
|
||||||
|
|
||||||
def Test_simplify_const_expr()
|
def Test_simplify_const_expr()
|
||||||
let res = execute('disass s:ConcatStrings')
|
let res = execute('disass s:ConcatStrings')
|
||||||
assert_match('\<SNR>\d*_ConcatStrings.*' ..
|
assert_match('\<SNR>\d*_ConcatStrings.*' ..
|
||||||
'\d PUSHS "onetwothree".*' ..
|
'\d PUSHS "onetwothree".*' ..
|
||||||
'\d RETURN',
|
'\d RETURN',
|
||||||
res)
|
res)
|
||||||
|
|
||||||
|
res = execute('disass s:ComputeConst')
|
||||||
|
assert_match('\<SNR>\d*_ComputeConst.*' ..
|
||||||
|
'\d PUSHNR 11.*' ..
|
||||||
|
'\d RETURN',
|
||||||
|
res)
|
||||||
enddef
|
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
|
||||||
|
@@ -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 */
|
||||||
|
/**/
|
||||||
|
717,
|
||||||
/**/
|
/**/
|
||||||
716,
|
716,
|
||||||
/**/
|
/**/
|
||||||
|
@@ -1040,51 +1040,6 @@ generate_PUSHFUNC(cctx_T *cctx, char_u *name, type_T *type)
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Generate a PUSH instruction for "tv".
|
|
||||||
* "tv" will be consumed or cleared. "tv" may be NULL;
|
|
||||||
*/
|
|
||||||
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 an ISN_STORE instruction.
|
* Generate an ISN_STORE instruction.
|
||||||
*/
|
*/
|
||||||
@@ -3671,6 +3626,91 @@ 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
|
||||||
|
// 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 '!'.
|
||||||
*/
|
*/
|
||||||
@@ -3728,9 +3768,7 @@ compile_subscript(
|
|||||||
cctx_T *cctx,
|
cctx_T *cctx,
|
||||||
char_u **start_leader,
|
char_u **start_leader,
|
||||||
char_u *end_leader,
|
char_u *end_leader,
|
||||||
typval_T *bef1_tv,
|
ppconst_T *ppconst)
|
||||||
typval_T *bef2_tv,
|
|
||||||
typval_T *new_tv)
|
|
||||||
{
|
{
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
@@ -3740,9 +3778,7 @@ compile_subscript(
|
|||||||
type_T *type;
|
type_T *type;
|
||||||
int argcount = 0;
|
int argcount = 0;
|
||||||
|
|
||||||
if (generate_tv_PUSH(cctx, bef1_tv) == FAIL
|
if (generate_ppconst(cctx, ppconst) == FAIL)
|
||||||
|| generate_tv_PUSH(cctx, bef2_tv) == FAIL
|
|
||||||
|| generate_tv_PUSH(cctx, new_tv) == FAIL)
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
// funcref(arg)
|
// funcref(arg)
|
||||||
@@ -3758,9 +3794,7 @@ compile_subscript(
|
|||||||
{
|
{
|
||||||
char_u *p;
|
char_u *p;
|
||||||
|
|
||||||
if (generate_tv_PUSH(cctx, bef1_tv) == FAIL
|
if (generate_ppconst(cctx, ppconst) == FAIL)
|
||||||
|| generate_tv_PUSH(cctx, bef2_tv) == FAIL
|
|
||||||
|| generate_tv_PUSH(cctx, new_tv) == FAIL)
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
// something->method()
|
// something->method()
|
||||||
@@ -3800,9 +3834,7 @@ compile_subscript(
|
|||||||
garray_T *stack;
|
garray_T *stack;
|
||||||
type_T **typep;
|
type_T **typep;
|
||||||
|
|
||||||
if (generate_tv_PUSH(cctx, bef1_tv) == FAIL
|
if (generate_ppconst(cctx, ppconst) == FAIL)
|
||||||
|| generate_tv_PUSH(cctx, bef2_tv) == FAIL
|
|
||||||
|| generate_tv_PUSH(cctx, new_tv) == FAIL)
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
// list index: list[123]
|
// list index: list[123]
|
||||||
@@ -3835,9 +3867,7 @@ compile_subscript(
|
|||||||
{
|
{
|
||||||
char_u *p;
|
char_u *p;
|
||||||
|
|
||||||
if (generate_tv_PUSH(cctx, bef1_tv) == FAIL
|
if (generate_ppconst(cctx, ppconst) == FAIL)
|
||||||
|| generate_tv_PUSH(cctx, bef2_tv) == FAIL
|
|
||||||
|| generate_tv_PUSH(cctx, new_tv) == FAIL)
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
++*arg;
|
++*arg;
|
||||||
@@ -3871,8 +3901,8 @@ compile_subscript(
|
|||||||
* Compile an expression at "*arg" and add instructions to "cctx->ctx_instr".
|
* Compile an expression at "*arg" and add instructions to "cctx->ctx_instr".
|
||||||
* "arg" is advanced until after the expression, skipping white space.
|
* "arg" is advanced until after the expression, skipping white space.
|
||||||
*
|
*
|
||||||
* If the value is a constant "new_tv" will be set.
|
* If the value is a constant "ppconst->pp_ret" will be set.
|
||||||
* Before instructions are generated, any "bef_tv" will generated.
|
* Before instructions are generated, any values in "ppconst" will generated.
|
||||||
*
|
*
|
||||||
* This is the compiling equivalent of eval1(), eval2(), etc.
|
* This is the compiling equivalent of eval1(), eval2(), etc.
|
||||||
*/
|
*/
|
||||||
@@ -3905,13 +3935,11 @@ compile_subscript(
|
|||||||
compile_expr7(
|
compile_expr7(
|
||||||
char_u **arg,
|
char_u **arg,
|
||||||
cctx_T *cctx,
|
cctx_T *cctx,
|
||||||
typval_T *bef1_tv,
|
ppconst_T *ppconst)
|
||||||
typval_T *bef2_tv,
|
|
||||||
typval_T *new_tv)
|
|
||||||
{
|
{
|
||||||
typval_T rettv;
|
|
||||||
char_u *start_leader, *end_leader;
|
char_u *start_leader, *end_leader;
|
||||||
int ret = OK;
|
int ret = OK;
|
||||||
|
typval_T *rettv = &ppconst->pp_tv[ppconst->pp_used];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Skip '!', '-' and '+' characters. They are handled later.
|
* Skip '!', '-' and '+' characters. They are handled later.
|
||||||
@@ -3921,7 +3949,7 @@ compile_expr7(
|
|||||||
*arg = skipwhite(*arg + 1);
|
*arg = skipwhite(*arg + 1);
|
||||||
end_leader = *arg;
|
end_leader = *arg;
|
||||||
|
|
||||||
rettv.v_type = VAR_UNKNOWN;
|
rettv->v_type = VAR_UNKNOWN;
|
||||||
switch (**arg)
|
switch (**arg)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@@ -3937,28 +3965,28 @@ compile_expr7(
|
|||||||
case '7':
|
case '7':
|
||||||
case '8':
|
case '8':
|
||||||
case '9':
|
case '9':
|
||||||
case '.': if (get_number_tv(arg, &rettv, TRUE, FALSE) == FAIL)
|
case '.': if (get_number_tv(arg, rettv, TRUE, FALSE) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* String constant: "string".
|
* String constant: "string".
|
||||||
*/
|
*/
|
||||||
case '"': if (get_string_tv(arg, &rettv, TRUE) == FAIL)
|
case '"': if (get_string_tv(arg, rettv, TRUE) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Literal string constant: 'str''ing'.
|
* Literal string constant: 'str''ing'.
|
||||||
*/
|
*/
|
||||||
case '\'': if (get_lit_string_tv(arg, &rettv, TRUE) == FAIL)
|
case '\'': if (get_lit_string_tv(arg, rettv, TRUE) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Constant Vim variable.
|
* Constant Vim variable.
|
||||||
*/
|
*/
|
||||||
case 'v': get_vim_constant(arg, &rettv);
|
case 'v': get_vim_constant(arg, rettv);
|
||||||
ret = NOTDONE;
|
ret = NOTDONE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -4036,26 +4064,26 @@ compile_expr7(
|
|||||||
if (ret == FAIL)
|
if (ret == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
if (rettv.v_type != VAR_UNKNOWN)
|
if (rettv->v_type != VAR_UNKNOWN)
|
||||||
{
|
{
|
||||||
// apply the '!', '-' and '+' before the constant
|
// apply the '!', '-' and '+' before the constant
|
||||||
if (apply_leader(&rettv, start_leader, end_leader) == FAIL)
|
if (apply_leader(rettv, start_leader, end_leader) == FAIL)
|
||||||
{
|
{
|
||||||
clear_tv(&rettv);
|
clear_tv(rettv);
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
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.
|
// A constant expression can possibly be handled compile time, return
|
||||||
*new_tv = rettv;
|
// the value instead of generating code.
|
||||||
|
++ppconst->pp_used;
|
||||||
}
|
}
|
||||||
else if (ret == NOTDONE)
|
else if (ret == NOTDONE)
|
||||||
{
|
{
|
||||||
char_u *p;
|
char_u *p;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
if (generate_tv_PUSH(cctx, bef1_tv) == FAIL
|
if (generate_ppconst(cctx, ppconst) == FAIL)
|
||||||
|| generate_tv_PUSH(cctx, bef2_tv) == FAIL)
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
if (!eval_isnamec1(**arg))
|
if (!eval_isnamec1(**arg))
|
||||||
@@ -4077,12 +4105,9 @@ 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,
|
||||||
bef1_tv, bef2_tv, new_tv) == FAIL
|
ppconst) == FAIL
|
||||||
|| compile_leader(cctx, start_leader, end_leader) == FAIL)
|
|| compile_leader(cctx, start_leader, end_leader) == FAIL)
|
||||||
{
|
|
||||||
clear_tv(new_tv);
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4093,15 +4118,15 @@ compile_expr7(
|
|||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
compile_expr6(
|
compile_expr6(
|
||||||
char_u **arg,
|
char_u **arg,
|
||||||
cctx_T *cctx,
|
cctx_T *cctx,
|
||||||
typval_T *bef_tv,
|
ppconst_T *ppconst)
|
||||||
typval_T *new_tv)
|
|
||||||
{
|
{
|
||||||
char_u *op;
|
char_u *op;
|
||||||
|
int ppconst_used = ppconst->pp_used;
|
||||||
|
|
||||||
// get the first expression
|
// get the first expression
|
||||||
if (compile_expr7(arg, cctx, NULL, bef_tv, new_tv) == FAIL)
|
if (compile_expr7(arg, cctx, ppconst) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -4109,8 +4134,6 @@ compile_expr6(
|
|||||||
*/
|
*/
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
typval_T tv2;
|
|
||||||
|
|
||||||
op = skipwhite(*arg);
|
op = skipwhite(*arg);
|
||||||
if (*op != '*' && *op != '/' && *op != '%')
|
if (*op != '*' && *op != '/' && *op != '%')
|
||||||
break;
|
break;
|
||||||
@@ -4128,29 +4151,33 @@ compile_expr6(
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
// get the second expression
|
// get the second expression
|
||||||
tv2.v_type = VAR_UNKNOWN;
|
if (compile_expr7(arg, cctx, ppconst) == FAIL)
|
||||||
if (compile_expr7(arg, cctx, bef_tv, new_tv, &tv2) == FAIL)
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
if (new_tv->v_type == VAR_NUMBER && tv2.v_type == VAR_NUMBER)
|
|
||||||
|
if (ppconst->pp_used == ppconst_used + 2
|
||||||
|
&& 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];
|
||||||
varnumber_T res = 0;
|
varnumber_T res = 0;
|
||||||
|
|
||||||
// both are numbers: compute the result
|
// both are numbers: compute the result
|
||||||
switch (*op)
|
switch (*op)
|
||||||
{
|
{
|
||||||
case '*': res = new_tv->vval.v_number * tv2.vval.v_number;
|
case '*': res = tv1->vval.v_number * tv2->vval.v_number;
|
||||||
break;
|
break;
|
||||||
case '/': res = new_tv->vval.v_number / tv2.vval.v_number;
|
case '/': res = tv1->vval.v_number / tv2->vval.v_number;
|
||||||
break;
|
break;
|
||||||
case '%': res = new_tv->vval.v_number % tv2.vval.v_number;
|
case '%': res = tv1->vval.v_number % tv2->vval.v_number;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
new_tv->vval.v_number = res;
|
tv1->vval.v_number = res;
|
||||||
|
--ppconst->pp_used;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
generate_tv_PUSH(cctx, new_tv);
|
generate_ppconst(cctx, ppconst);
|
||||||
generate_tv_PUSH(cctx, &tv2);
|
|
||||||
generate_two_op(cctx, op);
|
generate_two_op(cctx, op);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4166,22 +4193,25 @@ compile_expr6(
|
|||||||
static int
|
static int
|
||||||
compile_expr5(char_u **arg, cctx_T *cctx)
|
compile_expr5(char_u **arg, cctx_T *cctx)
|
||||||
{
|
{
|
||||||
typval_T tv1;
|
|
||||||
char_u *op;
|
char_u *op;
|
||||||
int oplen;
|
int oplen;
|
||||||
|
ppconst_T ppconst;
|
||||||
|
int ppconst_used = 0;
|
||||||
|
|
||||||
|
CLEAR_FIELD(ppconst);
|
||||||
|
|
||||||
// get the first variable
|
// get the first variable
|
||||||
tv1.v_type = VAR_UNKNOWN;
|
if (compile_expr6(arg, cctx, &ppconst) == FAIL)
|
||||||
if (compile_expr6(arg, cctx, NULL, &tv1) == FAIL)
|
{
|
||||||
|
clear_ppconst(&ppconst);
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Repeat computing, until no "+", "-" or ".." is following.
|
* Repeat computing, until no "+", "-" or ".." is following.
|
||||||
*/
|
*/
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
typval_T tv2;
|
|
||||||
|
|
||||||
op = skipwhite(*arg);
|
op = skipwhite(*arg);
|
||||||
if (*op != '+' && *op != '-' && !(*op == '.' && (*(*arg + 1) == '.')))
|
if (*op != '+' && *op != '-' && !(*op == '.' && (*(*arg + 1) == '.')))
|
||||||
break;
|
break;
|
||||||
@@ -4191,7 +4221,7 @@ compile_expr5(char_u **arg, cctx_T *cctx)
|
|||||||
{
|
{
|
||||||
char_u buf[3];
|
char_u buf[3];
|
||||||
|
|
||||||
clear_tv(&tv1);
|
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;
|
||||||
@@ -4200,58 +4230,63 @@ 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_tv(&tv1);
|
clear_ppconst(&ppconst);
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the second expression
|
// get the second expression
|
||||||
tv2.v_type = VAR_UNKNOWN;
|
if (compile_expr6(arg, cctx, &ppconst) == FAIL)
|
||||||
if (compile_expr6(arg, cctx, &tv1, &tv2) == FAIL)
|
|
||||||
{
|
{
|
||||||
clear_tv(&tv1);
|
clear_ppconst(&ppconst);
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*op == '+' && tv1.v_type == VAR_NUMBER && tv2.v_type == VAR_NUMBER)
|
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)))
|
||||||
{
|
{
|
||||||
// add constant numbers
|
typval_T *tv1 = &ppconst.pp_tv[ppconst_used];
|
||||||
tv1.vval.v_number = tv1.vval.v_number + tv2.vval.v_number;
|
typval_T *tv2 = &ppconst.pp_tv[ppconst_used + 1];
|
||||||
}
|
|
||||||
else if (*op == '-' && tv1.v_type == VAR_NUMBER
|
|
||||||
&& tv2.v_type == VAR_NUMBER)
|
|
||||||
{
|
|
||||||
// subtract constant numbers
|
|
||||||
tv1.vval.v_number = tv1.vval.v_number - tv2.vval.v_number;
|
|
||||||
}
|
|
||||||
else if (*op == '.' && tv1.v_type == VAR_STRING
|
|
||||||
&& tv2.v_type == VAR_STRING)
|
|
||||||
{
|
|
||||||
// concatenate constant strings
|
|
||||||
char_u *s1 = tv1.vval.v_string;
|
|
||||||
char_u *s2 = tv2.vval.v_string;
|
|
||||||
size_t len1 = STRLEN(s1);
|
|
||||||
|
|
||||||
tv1.vval.v_string = alloc((int)(len1 + STRLEN(s2) + 1));
|
// concat/subtract/add constant numbers
|
||||||
if (tv1.vval.v_string == NULL)
|
if (*op == '+')
|
||||||
|
tv1->vval.v_number = tv1->vval.v_number + tv2->vval.v_number;
|
||||||
|
else if (*op == '-')
|
||||||
|
tv1->vval.v_number = tv1->vval.v_number - tv2->vval.v_number;
|
||||||
|
else
|
||||||
{
|
{
|
||||||
|
// concatenate constant strings
|
||||||
|
char_u *s1 = tv1->vval.v_string;
|
||||||
|
char_u *s2 = tv2->vval.v_string;
|
||||||
|
size_t len1 = STRLEN(s1);
|
||||||
|
|
||||||
|
tv1->vval.v_string = alloc((int)(len1 + STRLEN(s2) + 1));
|
||||||
|
if (tv1->vval.v_string == NULL)
|
||||||
|
{
|
||||||
|
clear_ppconst(&ppconst);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
mch_memmove(tv1->vval.v_string, s1, len1);
|
||||||
|
STRCPY(tv1->vval.v_string + len1, s2);
|
||||||
vim_free(s1);
|
vim_free(s1);
|
||||||
vim_free(s2);
|
vim_free(s2);
|
||||||
return FAIL;
|
|
||||||
}
|
}
|
||||||
mch_memmove(tv1.vval.v_string, s1, len1);
|
--ppconst.pp_used;
|
||||||
STRCPY(tv1.vval.v_string + len1, s2);
|
|
||||||
vim_free(s1);
|
|
||||||
vim_free(s2);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
generate_tv_PUSH(cctx, &tv1);
|
generate_ppconst(cctx, &ppconst);
|
||||||
generate_tv_PUSH(cctx, &tv2);
|
|
||||||
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
|
||||||
@@ -4260,7 +4295,7 @@ compile_expr5(char_u **arg, cctx_T *cctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: move to caller
|
// TODO: move to caller
|
||||||
generate_tv_PUSH(cctx, &tv1);
|
generate_ppconst(cctx, &ppconst);
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user