mirror of
https://github.com/vim/vim.git
synced 2025-09-24 03:44:06 -04:00
patch 8.2.1794: no falsy Coalescing operator
Problem: No falsy Coalescing operator. Solution: Add the "??" operator. Fix mistake with function argument count.
This commit is contained in:
@@ -133,7 +133,27 @@ non-zero number it means TRUE: >
|
|||||||
:" executed
|
:" executed
|
||||||
To test for a non-empty string, use empty(): >
|
To test for a non-empty string, use empty(): >
|
||||||
:if !empty("foo")
|
:if !empty("foo")
|
||||||
<
|
|
||||||
|
< *falsy* *truthy*
|
||||||
|
An expression can be used as a condition, ignoring the type and only using
|
||||||
|
whether the value is "sort of true" or "sort of false". Falsy is:
|
||||||
|
the number zero
|
||||||
|
empty string, blob, list or dictionary
|
||||||
|
Other values are truthy. Examples:
|
||||||
|
0 falsy
|
||||||
|
1 truthy
|
||||||
|
-1 truthy
|
||||||
|
0.0 falsy
|
||||||
|
0.1 truthy
|
||||||
|
'' falsy
|
||||||
|
'x' truthy
|
||||||
|
[] falsy
|
||||||
|
[0] truthy
|
||||||
|
{} falsy
|
||||||
|
#{x: 1} truthy
|
||||||
|
0z falsy
|
||||||
|
0z00 truthy
|
||||||
|
|
||||||
*non-zero-arg*
|
*non-zero-arg*
|
||||||
Function arguments often behave slightly different from |TRUE|: If the
|
Function arguments often behave slightly different from |TRUE|: If the
|
||||||
argument is present and it evaluates to a non-zero Number, |v:true| or a
|
argument is present and it evaluates to a non-zero Number, |v:true| or a
|
||||||
@@ -877,10 +897,13 @@ Example: >
|
|||||||
All expressions within one level are parsed from left to right.
|
All expressions within one level are parsed from left to right.
|
||||||
|
|
||||||
|
|
||||||
expr1 *expr1* *trinary* *E109*
|
expr1 *expr1* *trinary* *falsy-operator* *E109*
|
||||||
-----
|
-----
|
||||||
|
|
||||||
expr2 ? expr1 : expr1
|
The trinary operator: expr2 ? expr1 : expr1
|
||||||
|
The falsy operator: expr2 ?? expr1
|
||||||
|
|
||||||
|
Trinary operator ~
|
||||||
|
|
||||||
The expression before the '?' is evaluated to a number. If it evaluates to
|
The expression before the '?' is evaluated to a number. If it evaluates to
|
||||||
|TRUE|, the result is the value of the expression between the '?' and ':',
|
|TRUE|, the result is the value of the expression between the '?' and ':',
|
||||||
@@ -903,6 +926,23 @@ To keep this readable, using |line-continuation| is suggested: >
|
|||||||
You should always put a space before the ':', otherwise it can be mistaken for
|
You should always put a space before the ':', otherwise it can be mistaken for
|
||||||
use in a variable such as "a:1".
|
use in a variable such as "a:1".
|
||||||
|
|
||||||
|
Falsy operator ~
|
||||||
|
|
||||||
|
This is also known as the "null coalescing operator", but that's too
|
||||||
|
complicated, thus we just call it the falsy operator.
|
||||||
|
|
||||||
|
The expression before the '??' is evaluated. If it evaluates to
|
||||||
|
|truthy|, this is used as the result. Otherwise the expression after the '??'
|
||||||
|
is evaluated and used as the result. This is most useful to have a default
|
||||||
|
value for an expression that may result in zero or empty: >
|
||||||
|
echo theList ?? 'list is empty'
|
||||||
|
echo GetName() ?? 'unknown'
|
||||||
|
|
||||||
|
These are similar, but not equal: >
|
||||||
|
expr2 ?? expr1
|
||||||
|
expr2 ? expr2 : expr1
|
||||||
|
In the second line "expr2" is evaluated twice.
|
||||||
|
|
||||||
|
|
||||||
expr2 and expr3 *expr2* *expr3*
|
expr2 and expr3 *expr2* *expr3*
|
||||||
---------------
|
---------------
|
||||||
|
98
src/eval.c
98
src/eval.c
@@ -2110,6 +2110,7 @@ eval0(
|
|||||||
/*
|
/*
|
||||||
* Handle top level expression:
|
* Handle top level expression:
|
||||||
* expr2 ? expr1 : expr1
|
* expr2 ? expr1 : expr1
|
||||||
|
* expr2 ?? expr1
|
||||||
*
|
*
|
||||||
* "arg" must point to the first non-white of the expression.
|
* "arg" must point to the first non-white of the expression.
|
||||||
* "arg" is advanced to just after the recognized expression.
|
* "arg" is advanced to just after the recognized expression.
|
||||||
@@ -2135,6 +2136,7 @@ eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
|
|||||||
p = eval_next_non_blank(*arg, evalarg, &getnext);
|
p = eval_next_non_blank(*arg, evalarg, &getnext);
|
||||||
if (*p == '?')
|
if (*p == '?')
|
||||||
{
|
{
|
||||||
|
int op_falsy = p[1] == '?';
|
||||||
int result;
|
int result;
|
||||||
typval_T var2;
|
typval_T var2;
|
||||||
evalarg_T *evalarg_used = evalarg;
|
evalarg_T *evalarg_used = evalarg;
|
||||||
@@ -2168,11 +2170,12 @@ eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
|
|||||||
{
|
{
|
||||||
int error = FALSE;
|
int error = FALSE;
|
||||||
|
|
||||||
if (in_vim9script())
|
if (in_vim9script() || op_falsy)
|
||||||
result = tv2bool(rettv);
|
result = tv2bool(rettv);
|
||||||
else if (tv_get_number_chk(rettv, &error) != 0)
|
else if (tv_get_number_chk(rettv, &error) != 0)
|
||||||
result = TRUE;
|
result = TRUE;
|
||||||
clear_tv(rettv);
|
if (error || !op_falsy || !result)
|
||||||
|
clear_tv(rettv);
|
||||||
if (error)
|
if (error)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
@@ -2180,6 +2183,8 @@ eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
|
|||||||
/*
|
/*
|
||||||
* Get the second variable. Recursive!
|
* Get the second variable. Recursive!
|
||||||
*/
|
*/
|
||||||
|
if (op_falsy)
|
||||||
|
++*arg;
|
||||||
if (evaluate && in_vim9script() && !IS_WHITE_OR_NUL((*arg)[1]))
|
if (evaluate && in_vim9script() && !IS_WHITE_OR_NUL((*arg)[1]))
|
||||||
{
|
{
|
||||||
error_white_both(p, 1);
|
error_white_both(p, 1);
|
||||||
@@ -2187,62 +2192,67 @@ eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
*arg = skipwhite_and_linebreak(*arg + 1, evalarg_used);
|
*arg = skipwhite_and_linebreak(*arg + 1, evalarg_used);
|
||||||
evalarg_used->eval_flags = result ? orig_flags
|
evalarg_used->eval_flags = (op_falsy ? !result : result)
|
||||||
: orig_flags & ~EVAL_EVALUATE;
|
? orig_flags : orig_flags & ~EVAL_EVALUATE;
|
||||||
if (eval1(arg, rettv, evalarg_used) == FAIL)
|
if (eval1(arg, &var2, evalarg_used) == FAIL)
|
||||||
{
|
{
|
||||||
evalarg_used->eval_flags = orig_flags;
|
evalarg_used->eval_flags = orig_flags;
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
if (!op_falsy || !result)
|
||||||
|
*rettv = var2;
|
||||||
|
|
||||||
/*
|
if (!op_falsy)
|
||||||
* Check for the ":".
|
|
||||||
*/
|
|
||||||
p = eval_next_non_blank(*arg, evalarg_used, &getnext);
|
|
||||||
if (*p != ':')
|
|
||||||
{
|
{
|
||||||
emsg(_(e_missing_colon));
|
/*
|
||||||
if (evaluate && result)
|
* Check for the ":".
|
||||||
clear_tv(rettv);
|
*/
|
||||||
evalarg_used->eval_flags = orig_flags;
|
p = eval_next_non_blank(*arg, evalarg_used, &getnext);
|
||||||
return FAIL;
|
if (*p != ':')
|
||||||
}
|
{
|
||||||
if (getnext)
|
emsg(_(e_missing_colon));
|
||||||
*arg = eval_next_line(evalarg_used);
|
if (evaluate && result)
|
||||||
else
|
clear_tv(rettv);
|
||||||
{
|
evalarg_used->eval_flags = orig_flags;
|
||||||
if (evaluate && in_vim9script() && !VIM_ISWHITE(p[-1]))
|
return FAIL;
|
||||||
|
}
|
||||||
|
if (getnext)
|
||||||
|
*arg = eval_next_line(evalarg_used);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (evaluate && in_vim9script() && !VIM_ISWHITE(p[-1]))
|
||||||
|
{
|
||||||
|
error_white_both(p, 1);
|
||||||
|
clear_tv(rettv);
|
||||||
|
evalarg_used->eval_flags = orig_flags;
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
*arg = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the third variable. Recursive!
|
||||||
|
*/
|
||||||
|
if (evaluate && in_vim9script() && !IS_WHITE_OR_NUL((*arg)[1]))
|
||||||
{
|
{
|
||||||
error_white_both(p, 1);
|
error_white_both(p, 1);
|
||||||
clear_tv(rettv);
|
clear_tv(rettv);
|
||||||
evalarg_used->eval_flags = orig_flags;
|
evalarg_used->eval_flags = orig_flags;
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
*arg = p;
|
*arg = skipwhite_and_linebreak(*arg + 1, evalarg_used);
|
||||||
}
|
evalarg_used->eval_flags = !result ? orig_flags
|
||||||
|
|
||||||
/*
|
|
||||||
* Get the third variable. Recursive!
|
|
||||||
*/
|
|
||||||
if (evaluate && in_vim9script() && !IS_WHITE_OR_NUL((*arg)[1]))
|
|
||||||
{
|
|
||||||
error_white_both(p, 1);
|
|
||||||
clear_tv(rettv);
|
|
||||||
evalarg_used->eval_flags = orig_flags;
|
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
*arg = skipwhite_and_linebreak(*arg + 1, evalarg_used);
|
|
||||||
evalarg_used->eval_flags = !result ? orig_flags
|
|
||||||
: orig_flags & ~EVAL_EVALUATE;
|
: orig_flags & ~EVAL_EVALUATE;
|
||||||
if (eval1(arg, &var2, evalarg_used) == FAIL)
|
if (eval1(arg, &var2, evalarg_used) == FAIL)
|
||||||
{
|
{
|
||||||
if (evaluate && result)
|
if (evaluate && result)
|
||||||
clear_tv(rettv);
|
clear_tv(rettv);
|
||||||
evalarg_used->eval_flags = orig_flags;
|
evalarg_used->eval_flags = orig_flags;
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
}
|
||||||
|
if (evaluate && !result)
|
||||||
|
*rettv = var2;
|
||||||
}
|
}
|
||||||
if (evaluate && !result)
|
|
||||||
*rettv = var2;
|
|
||||||
|
|
||||||
if (evalarg == NULL)
|
if (evalarg == NULL)
|
||||||
clear_evalarg(&local_evalarg, NULL);
|
clear_evalarg(&local_evalarg, NULL);
|
||||||
|
@@ -42,6 +42,28 @@ func Test_version()
|
|||||||
call assert_false(has('patch-9.9.1'))
|
call assert_false(has('patch-9.9.1'))
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func Test_op_falsy()
|
||||||
|
call assert_equal(v:true, v:true ?? 456)
|
||||||
|
call assert_equal(123, 123 ?? 456)
|
||||||
|
call assert_equal('yes', 'yes' ?? 456)
|
||||||
|
call assert_equal(0z00, 0z00 ?? 456)
|
||||||
|
call assert_equal([1], [1] ?? 456)
|
||||||
|
call assert_equal(#{one: 1}, #{one: 1} ?? 456)
|
||||||
|
if has('float')
|
||||||
|
call assert_equal(0.1, 0.1 ?? 456)
|
||||||
|
endif
|
||||||
|
|
||||||
|
call assert_equal(456, v:false ?? 456)
|
||||||
|
call assert_equal(456, 0 ?? 456)
|
||||||
|
call assert_equal(456, '' ?? 456)
|
||||||
|
call assert_equal(456, 0z ?? 456)
|
||||||
|
call assert_equal(456, [] ?? 456)
|
||||||
|
call assert_equal(456, {} ?? 456)
|
||||||
|
if has('float')
|
||||||
|
call assert_equal(456, 0.0 ?? 456)
|
||||||
|
endif
|
||||||
|
endfunc
|
||||||
|
|
||||||
func Test_dict()
|
func Test_dict()
|
||||||
let d = {'': 'empty', 'a': 'a', 0: 'zero'}
|
let d = {'': 'empty', 'a': 'a', 0: 'zero'}
|
||||||
call assert_equal('empty', d[''])
|
call assert_equal('empty', d[''])
|
||||||
|
@@ -1326,6 +1326,33 @@ def Test_disassemble_compare()
|
|||||||
delete('Xdisassemble')
|
delete('Xdisassemble')
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
def s:FalsyOp()
|
||||||
|
echo g:flag ?? "yes"
|
||||||
|
echo [] ?? "empty list"
|
||||||
|
echo "" ?? "empty string"
|
||||||
|
enddef
|
||||||
|
|
||||||
|
def Test_dsassemble_falsy_op()
|
||||||
|
var res = execute('disass s:FalsyOp')
|
||||||
|
assert_match('\<SNR>\d*_FalsyOp\_s*' ..
|
||||||
|
'echo g:flag ?? "yes"\_s*' ..
|
||||||
|
'0 LOADG g:flag\_s*' ..
|
||||||
|
'1 JUMP_AND_KEEP_IF_TRUE -> 3\_s*' ..
|
||||||
|
'2 PUSHS "yes"\_s*' ..
|
||||||
|
'3 ECHO 1\_s*' ..
|
||||||
|
'echo \[\] ?? "empty list"\_s*' ..
|
||||||
|
'4 NEWLIST size 0\_s*' ..
|
||||||
|
'5 JUMP_AND_KEEP_IF_TRUE -> 7\_s*' ..
|
||||||
|
'6 PUSHS "empty list"\_s*' ..
|
||||||
|
'7 ECHO 1\_s*' ..
|
||||||
|
'echo "" ?? "empty string"\_s*' ..
|
||||||
|
'\d\+ PUSHS "empty string"\_s*' ..
|
||||||
|
'\d\+ ECHO 1\_s*' ..
|
||||||
|
'\d\+ PUSHNR 0\_s*' ..
|
||||||
|
'\d\+ RETURN',
|
||||||
|
res)
|
||||||
|
enddef
|
||||||
|
|
||||||
def Test_disassemble_compare_const()
|
def Test_disassemble_compare_const()
|
||||||
var cases = [
|
var cases = [
|
||||||
['"xx" == "yy"', false],
|
['"xx" == "yy"', false],
|
||||||
|
@@ -12,7 +12,7 @@ def FuncTwo(arg: number): number
|
|||||||
enddef
|
enddef
|
||||||
|
|
||||||
" test cond ? expr : expr
|
" test cond ? expr : expr
|
||||||
def Test_expr1()
|
def Test_expr1_trinary()
|
||||||
assert_equal('one', true ? 'one' : 'two')
|
assert_equal('one', true ? 'one' : 'two')
|
||||||
assert_equal('one', 1 ?
|
assert_equal('one', 1 ?
|
||||||
'one' :
|
'one' :
|
||||||
@@ -61,7 +61,7 @@ def Test_expr1()
|
|||||||
assert_equal(123, Z(3))
|
assert_equal(123, Z(3))
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
def Test_expr1_vimscript()
|
def Test_expr1_trinary_vimscript()
|
||||||
# check line continuation
|
# check line continuation
|
||||||
var lines =<< trim END
|
var lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
@@ -139,7 +139,7 @@ def Test_expr1_vimscript()
|
|||||||
CheckScriptSuccess(lines)
|
CheckScriptSuccess(lines)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
func Test_expr1_fails()
|
func Test_expr1_trinary_fails()
|
||||||
call CheckDefFailure(["var x = 1 ? 'one'"], "Missing ':' after '?'", 1)
|
call CheckDefFailure(["var x = 1 ? 'one'"], "Missing ':' after '?'", 1)
|
||||||
|
|
||||||
let msg = "White space required before and after '?'"
|
let msg = "White space required before and after '?'"
|
||||||
@@ -160,6 +160,34 @@ func Test_expr1_fails()
|
|||||||
\ 'Z()'], 'E119:', 4)
|
\ 'Z()'], 'E119:', 4)
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
def Test_expr1_falsy()
|
||||||
|
var lines =<< trim END
|
||||||
|
assert_equal(v:true, v:true ?? 456)
|
||||||
|
assert_equal(123, 123 ?? 456)
|
||||||
|
assert_equal('yes', 'yes' ?? 456)
|
||||||
|
assert_equal([1], [1] ?? 456)
|
||||||
|
assert_equal(#{one: 1}, #{one: 1} ?? 456)
|
||||||
|
if has('float')
|
||||||
|
assert_equal(0.1, 0.1 ?? 456)
|
||||||
|
endif
|
||||||
|
|
||||||
|
assert_equal(456, v:false ?? 456)
|
||||||
|
assert_equal(456, 0 ?? 456)
|
||||||
|
assert_equal(456, '' ?? 456)
|
||||||
|
assert_equal(456, [] ?? 456)
|
||||||
|
assert_equal(456, {} ?? 456)
|
||||||
|
if has('float')
|
||||||
|
assert_equal(456, 0.0 ?? 456)
|
||||||
|
endif
|
||||||
|
END
|
||||||
|
CheckDefAndScriptSuccess(lines)
|
||||||
|
|
||||||
|
var msg = "White space required before and after '??'"
|
||||||
|
call CheckDefFailure(["var x = 1?? 'one' : 'two'"], msg, 1)
|
||||||
|
call CheckDefFailure(["var x = 1 ??'one' : 'two'"], msg, 1)
|
||||||
|
call CheckDefFailure(["var x = 1??'one' : 'two'"], msg, 1)
|
||||||
|
enddef
|
||||||
|
|
||||||
" TODO: define inside test function
|
" TODO: define inside test function
|
||||||
def Record(val: any): any
|
def Record(val: any): any
|
||||||
g:vals->add(val)
|
g:vals->add(val)
|
||||||
|
@@ -750,6 +750,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 */
|
||||||
|
/**/
|
||||||
|
1794,
|
||||||
/**/
|
/**/
|
||||||
1793,
|
1793,
|
||||||
/**/
|
/**/
|
||||||
|
@@ -4132,14 +4132,20 @@ compile_expr2(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Toplevel expression: expr2 ? expr1a : expr1b
|
* Toplevel expression: expr2 ? expr1a : expr1b
|
||||||
*
|
|
||||||
* Produces instructions:
|
* Produces instructions:
|
||||||
* EVAL expr2 Push result of "expr"
|
* EVAL expr2 Push result of "expr2"
|
||||||
* JUMP_IF_FALSE alt jump if false
|
* JUMP_IF_FALSE alt jump if false
|
||||||
* EVAL expr1a
|
* EVAL expr1a
|
||||||
* JUMP_ALWAYS end
|
* JUMP_ALWAYS end
|
||||||
* alt: EVAL expr1b
|
* alt: EVAL expr1b
|
||||||
* end:
|
* end:
|
||||||
|
*
|
||||||
|
* Toplevel expression: expr2 ?? expr1
|
||||||
|
* Produces instructions:
|
||||||
|
* EVAL expr2 Push result of "expr2"
|
||||||
|
* JUMP_AND_KEEP_IF_TRUE end jump if true
|
||||||
|
* EVAL expr1
|
||||||
|
* end:
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
compile_expr1(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
|
compile_expr1(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
|
||||||
@@ -4162,13 +4168,13 @@ compile_expr1(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
|
|||||||
p = may_peek_next_line(cctx, *arg, &next);
|
p = may_peek_next_line(cctx, *arg, &next);
|
||||||
if (*p == '?')
|
if (*p == '?')
|
||||||
{
|
{
|
||||||
|
int op_falsy = p[1] == '?';
|
||||||
garray_T *instr = &cctx->ctx_instr;
|
garray_T *instr = &cctx->ctx_instr;
|
||||||
garray_T *stack = &cctx->ctx_type_stack;
|
garray_T *stack = &cctx->ctx_type_stack;
|
||||||
int alt_idx = instr->ga_len;
|
int alt_idx = instr->ga_len;
|
||||||
int end_idx = 0;
|
int end_idx = 0;
|
||||||
isn_T *isn;
|
isn_T *isn;
|
||||||
type_T *type1 = NULL;
|
type_T *type1 = NULL;
|
||||||
type_T *type2;
|
|
||||||
int has_const_expr = FALSE;
|
int has_const_expr = FALSE;
|
||||||
int const_value = FALSE;
|
int const_value = FALSE;
|
||||||
int save_skip = cctx->ctx_skip;
|
int save_skip = cctx->ctx_skip;
|
||||||
@@ -4179,9 +4185,10 @@ compile_expr1(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
|
|||||||
p = skipwhite(*arg);
|
p = skipwhite(*arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IS_WHITE_OR_NUL(**arg) || !IS_WHITE_OR_NUL(p[1]))
|
if (!IS_WHITE_OR_NUL(**arg) || !IS_WHITE_OR_NUL(p[1 + op_falsy]))
|
||||||
{
|
{
|
||||||
semsg(_(e_white_space_required_before_and_after_str), "?");
|
semsg(_(e_white_space_required_before_and_after_str),
|
||||||
|
op_falsy ? "??" : "?");
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4191,20 +4198,32 @@ compile_expr1(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
|
|||||||
// expression is to be evaluated.
|
// expression is to be evaluated.
|
||||||
has_const_expr = TRUE;
|
has_const_expr = TRUE;
|
||||||
const_value = tv2bool(&ppconst->pp_tv[ppconst_used]);
|
const_value = tv2bool(&ppconst->pp_tv[ppconst_used]);
|
||||||
clear_tv(&ppconst->pp_tv[ppconst_used]);
|
cctx->ctx_skip = save_skip == SKIP_YES ||
|
||||||
--ppconst->pp_used;
|
(op_falsy ? const_value : !const_value) ? SKIP_YES : SKIP_NOT;
|
||||||
cctx->ctx_skip = save_skip == SKIP_YES || !const_value
|
|
||||||
? SKIP_YES : SKIP_NOT;
|
if (op_falsy && cctx->ctx_skip == SKIP_YES)
|
||||||
|
// "left ?? right" and "left" is truthy: produce "left"
|
||||||
|
generate_ppconst(cctx, ppconst);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clear_tv(&ppconst->pp_tv[ppconst_used]);
|
||||||
|
--ppconst->pp_used;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
generate_ppconst(cctx, ppconst);
|
generate_ppconst(cctx, ppconst);
|
||||||
generate_JUMP(cctx, JUMP_IF_FALSE, 0);
|
if (op_falsy)
|
||||||
|
end_idx = instr->ga_len;
|
||||||
|
generate_JUMP(cctx, op_falsy
|
||||||
|
? JUMP_AND_KEEP_IF_TRUE : JUMP_IF_FALSE, 0);
|
||||||
|
if (op_falsy)
|
||||||
|
type1 = ((type_T **)stack->ga_data)[stack->ga_len];
|
||||||
}
|
}
|
||||||
|
|
||||||
// evaluate the second expression; any type is accepted
|
// evaluate the second expression; any type is accepted
|
||||||
*arg = skipwhite(p + 1);
|
*arg = skipwhite(p + 1 + op_falsy);
|
||||||
if (may_get_next_line(p + 1, arg, cctx) == FAIL)
|
if (may_get_next_line(p + 1 + op_falsy, arg, cctx) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
if (compile_expr1(arg, cctx, ppconst) == FAIL)
|
if (compile_expr1(arg, cctx, ppconst) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
@@ -4213,56 +4232,64 @@ compile_expr1(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
|
|||||||
{
|
{
|
||||||
generate_ppconst(cctx, ppconst);
|
generate_ppconst(cctx, ppconst);
|
||||||
|
|
||||||
// remember the type and drop it
|
if (!op_falsy)
|
||||||
--stack->ga_len;
|
{
|
||||||
type1 = ((type_T **)stack->ga_data)[stack->ga_len];
|
// remember the type and drop it
|
||||||
|
--stack->ga_len;
|
||||||
|
type1 = ((type_T **)stack->ga_data)[stack->ga_len];
|
||||||
|
|
||||||
end_idx = instr->ga_len;
|
end_idx = instr->ga_len;
|
||||||
generate_JUMP(cctx, JUMP_ALWAYS, 0);
|
generate_JUMP(cctx, JUMP_ALWAYS, 0);
|
||||||
|
|
||||||
// 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 ":".
|
if (!op_falsy)
|
||||||
p = may_peek_next_line(cctx, *arg, &next);
|
|
||||||
if (*p != ':')
|
|
||||||
{
|
{
|
||||||
emsg(_(e_missing_colon));
|
// Check for the ":".
|
||||||
return FAIL;
|
p = may_peek_next_line(cctx, *arg, &next);
|
||||||
}
|
if (*p != ':')
|
||||||
if (next != NULL)
|
{
|
||||||
{
|
emsg(_(e_missing_colon));
|
||||||
*arg = next_line_from_context(cctx, TRUE);
|
return FAIL;
|
||||||
p = skipwhite(*arg);
|
}
|
||||||
}
|
if (next != NULL)
|
||||||
|
{
|
||||||
|
*arg = next_line_from_context(cctx, TRUE);
|
||||||
|
p = skipwhite(*arg);
|
||||||
|
}
|
||||||
|
|
||||||
if (!IS_WHITE_OR_NUL(**arg) || !IS_WHITE_OR_NUL(p[1]))
|
if (!IS_WHITE_OR_NUL(**arg) || !IS_WHITE_OR_NUL(p[1]))
|
||||||
{
|
{
|
||||||
semsg(_(e_white_space_required_before_and_after_str), ":");
|
semsg(_(e_white_space_required_before_and_after_str), ":");
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// evaluate the third expression
|
// evaluate the third expression
|
||||||
if (has_const_expr)
|
if (has_const_expr)
|
||||||
cctx->ctx_skip = save_skip == SKIP_YES || const_value
|
cctx->ctx_skip = save_skip == SKIP_YES || const_value
|
||||||
? SKIP_YES : SKIP_NOT;
|
? SKIP_YES : SKIP_NOT;
|
||||||
*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, ppconst) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
if (!has_const_expr)
|
if (!has_const_expr)
|
||||||
{
|
{
|
||||||
|
type_T **typep;
|
||||||
|
|
||||||
generate_ppconst(cctx, ppconst);
|
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];
|
typep = ((type_T **)stack->ga_data) + stack->ga_len - 1;
|
||||||
common_type(type1, type2, &type2, cctx->ctx_type_list);
|
common_type(type1, *typep, typep, cctx->ctx_type_list);
|
||||||
|
|
||||||
// jump here from JUMP_ALWAYS
|
// jump here from JUMP_ALWAYS or JUMP_AND_KEEP_IF_TRUE
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
@@ -924,6 +924,10 @@ common_type(type_T *type1, type_T *type2, type_T **dest, garray_T *type_gap)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
*dest = alloc_func_type(common, -1, type_gap);
|
*dest = alloc_func_type(common, -1, type_gap);
|
||||||
|
// Use the minimum of min_argcount.
|
||||||
|
(*dest)->tt_min_argcount =
|
||||||
|
type1->tt_min_argcount < type2->tt_min_argcount
|
||||||
|
? type1->tt_min_argcount : type2->tt_min_argcount;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user