0
0
mirror of https://github.com/vim/vim.git synced 2025-09-25 03:54:15 -04:00

patch 8.2.1795: Vim9: operators && and || have a confusing result

Problem:    Vim9: operators && and || have a confusing result.
Solution:   Make the result a boolean.
This commit is contained in:
Bram Moolenaar
2020-10-03 22:52:39 +02:00
parent 92f26c256e
commit 2bb2658bef
12 changed files with 254 additions and 216 deletions

View File

@@ -154,25 +154,25 @@ Functions and variables are script-local by default ~
*vim9-scopes* *vim9-scopes*
When using `:function` or `:def` to specify a new function at the script level When using `:function` or `:def` to specify a new function at the script level
in a Vim9 script, the function is local to the script, as if "s:" was in a Vim9 script, the function is local to the script, as if "s:" was
prefixed. Using the "s:" prefix is optional. To define or use a global prefixed. Using the "s:" prefix is optional. To define a global function or
function or variable the "g:" prefix should be used. For functions in an variable the "g:" prefix must be used. For functions in an autoload script
autoload script the "name#" prefix is sufficient. > the "name#" prefix is sufficient. >
def ThisFunction() # script-local def ThisFunction() # script-local
def s:ThisFunction() # script-local def s:ThisFunction() # script-local
def g:ThatFunction() # global def g:ThatFunction() # global
def ThatFunction() # global if no local ThatFunction()
def scriptname#function() # autoload def scriptname#function() # autoload
When using `:function` or `:def` to specify a new function inside a function, When using `:function` or `:def` to specify a nested function inside a `:def`
the function is local to the function. It is not possible to define a function, this nested function is local to the code block it is defined in.
script-local function inside a function. It is possible to define a global In a `:def` function IT is not possible to define a script-local function. it
function, using the "g:" prefix. is possible to define a global function by using the "g:" prefix.
When referring to a function and no "s:" or "g:" prefix is used, Vim will When referring to a function and no "s:" or "g:" prefix is used, Vim will
prefer using a local function (in the function scope, script scope or prefer using a local function (in the function scope, script scope or
imported) before looking for a global function. imported) before looking for a global function. However, it is recommended to
In all cases the function must be defined before used. That is when it is always use "g:" to refer to a local function for clarity. In all cases the
first called or when `:defcompile` causes the call to be compiled. function must be defined before used. That is when it is first called or when
`:defcompile` causes the call to be compiled.
The result is that functions and variables without a namespace can usually be The result is that functions and variables without a namespace can usually be
found in the script, either defined there or imported. Global functions and found in the script, either defined there or imported. Global functions and
@@ -184,7 +184,7 @@ and cannot be deleted or replaced.
Variable declarations with :var, :final and :const ~ Variable declarations with :var, :final and :const ~
*vim9-declaration* *vim9-declaration* *:var*
Local variables need to be declared with `:var`. Local constants need to be Local variables need to be declared with `:var`. Local constants need to be
declared with `:final` or `:const`. We refer to both as "variables" in this declared with `:final` or `:const`. We refer to both as "variables" in this
section. section.
@@ -261,7 +261,7 @@ Example: >
myList = [3, 4] # Error! myList = [3, 4] # Error!
myList[0] = 9 # Error! myList[0] = 9 # Error!
muList->add(3) # Error! muList->add(3) # Error!
< *:final*
`:final` is used for making only the variable a constant, the value can be `:final` is used for making only the variable a constant, the value can be
changed. This is well known from Java. Example: > changed. This is well known from Java. Example: >
final myList = [1, 2] final myList = [1, 2]
@@ -471,10 +471,6 @@ Conditions and expressions are mostly working like they do in JavaScript. A
difference is made where JavaScript does not work like most people expect. difference is made where JavaScript does not work like most people expect.
Specifically, an empty list is falsy. Specifically, an empty list is falsy.
Any type of variable can be used as a condition, there is no error, not even
for using a list or job. This is very much like JavaScript, but there are a
few exceptions.
type TRUE when ~ type TRUE when ~
bool v:true or 1 bool v:true or 1
number non-zero number non-zero
@@ -490,17 +486,25 @@ few exceptions.
class when not NULL class when not NULL
object when not NULL (TODO: when isTrue() returns v:true) object when not NULL (TODO: when isTrue() returns v:true)
The boolean operators "||" and "&&" do not change the value: > The boolean operators "||" and "&&" expect the values to be boolean, zero or
8 || 2 == 8 one: >
0 || 2 == 2 1 || false == true
0 || '' == '' 0 || 1 == true
8 && 2 == 2 0 || false == false
0 && 2 == 0 1 && true == true
2 && 0 == 0 0 && 1 == false
[] && 2 == [] 8 || 0 Error!
'yes' && 0 Error!
[] || 99 Error!
When using `..` for string concatenation arguments of simple types are always When using "!" for inverting, there is no error for using any type and the
converted to string. > result is a boolean: >
!'yes' == false
var myList = [1, 2, 3]
!!myList == true
When using "`.."` for string concatenation arguments of simple types are
always converted to string. >
'hello ' .. 123 == 'hello 123' 'hello ' .. 123 == 'hello 123'
'hello ' .. v:true == 'hello v:true' 'hello ' .. v:true == 'hello v:true'

View File

@@ -2296,7 +2296,7 @@ eval2(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
int orig_flags; int orig_flags;
long result = FALSE; long result = FALSE;
typval_T var2; typval_T var2;
int error; int error = FALSE;
int vim9script = in_vim9script(); int vim9script = in_vim9script();
if (evalarg == NULL) if (evalarg == NULL)
@@ -2309,18 +2309,12 @@ eval2(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
if (evaluate) if (evaluate)
{ {
if (vim9script) if (vim9script)
{ result = tv_get_bool_chk(rettv, &error);
result = tv2bool(rettv); else if (tv_get_number_chk(rettv, &error) != 0)
} result = TRUE;
else clear_tv(rettv);
{ if (error)
error = FALSE; return FAIL;
if (tv_get_number_chk(rettv, &error) != 0)
result = TRUE;
clear_tv(rettv);
if (error)
return FAIL;
}
} }
/* /*
@@ -2360,27 +2354,28 @@ eval2(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
* Compute the result. * Compute the result.
*/ */
if (evaluate && !result) if (evaluate && !result)
{
if (vim9script)
result = tv_get_bool_chk(&var2, &error);
else if (tv_get_number_chk(&var2, &error) != 0)
result = TRUE;
clear_tv(&var2);
if (error)
return FAIL;
}
if (evaluate)
{ {
if (vim9script) if (vim9script)
{ {
clear_tv(rettv); rettv->v_type = VAR_BOOL;
*rettv = var2; rettv->vval.v_number = result ? VVAL_TRUE : VVAL_FALSE;
result = tv2bool(rettv);
} }
else else
{ {
if (tv_get_number_chk(&var2, &error) != 0) rettv->v_type = VAR_NUMBER;
result = TRUE; rettv->vval.v_number = result;
clear_tv(&var2);
if (error)
return FAIL;
} }
} }
if (evaluate && !vim9script)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = result;
}
p = eval_next_non_blank(*arg, evalarg_used, &getnext); p = eval_next_non_blank(*arg, evalarg_used, &getnext);
} }
@@ -2389,9 +2384,6 @@ eval2(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
clear_evalarg(&local_evalarg, NULL); clear_evalarg(&local_evalarg, NULL);
else else
evalarg->eval_flags = orig_flags; evalarg->eval_flags = orig_flags;
// Resulting value can be assigned to a bool.
rettv->v_lock |= VAR_BOOL_OK;
} }
return OK; return OK;
@@ -2430,7 +2422,7 @@ eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
int evaluate; int evaluate;
long result = TRUE; long result = TRUE;
typval_T var2; typval_T var2;
int error; int error = FALSE;
int vim9script = in_vim9script(); int vim9script = in_vim9script();
if (evalarg == NULL) if (evalarg == NULL)
@@ -2443,18 +2435,12 @@ eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
if (evaluate) if (evaluate)
{ {
if (vim9script) if (vim9script)
{ result = tv_get_bool_chk(rettv, &error);
result = tv2bool(rettv); else if (tv_get_number_chk(rettv, &error) == 0)
} result = FALSE;
else clear_tv(rettv);
{ if (error)
error = FALSE; return FAIL;
if (tv_get_number_chk(rettv, &error) == 0)
result = FALSE;
clear_tv(rettv);
if (error)
return FAIL;
}
} }
/* /*
@@ -2466,7 +2452,7 @@ eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
*arg = eval_next_line(evalarg_used); *arg = eval_next_line(evalarg_used);
else else
{ {
if (evaluate && in_vim9script() && !VIM_ISWHITE(p[-1])) if (evaluate && vim9script && !VIM_ISWHITE(p[-1]))
{ {
error_white_both(p, 2); error_white_both(p, 2);
clear_tv(rettv); clear_tv(rettv);
@@ -2495,27 +2481,28 @@ eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
* Compute the result. * Compute the result.
*/ */
if (evaluate && result) if (evaluate && result)
{
if (vim9script)
result = tv_get_bool_chk(&var2, &error);
else if (tv_get_number_chk(&var2, &error) == 0)
result = FALSE;
clear_tv(&var2);
if (error)
return FAIL;
}
if (evaluate)
{ {
if (vim9script) if (vim9script)
{ {
clear_tv(rettv); rettv->v_type = VAR_BOOL;
*rettv = var2; rettv->vval.v_number = result ? VVAL_TRUE : VVAL_FALSE;
result = tv2bool(rettv);
} }
else else
{ {
if (tv_get_number_chk(&var2, &error) == 0) rettv->v_type = VAR_NUMBER;
result = FALSE; rettv->vval.v_number = result;
clear_tv(&var2);
if (error)
return FAIL;
} }
} }
if (evaluate && !vim9script)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = result;
}
p = eval_next_non_blank(*arg, evalarg_used, &getnext); p = eval_next_non_blank(*arg, evalarg_used, &getnext);
} }
@@ -2524,9 +2511,6 @@ eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
clear_evalarg(&local_evalarg, NULL); clear_evalarg(&local_evalarg, NULL);
else else
evalarg->eval_flags = orig_flags; evalarg->eval_flags = orig_flags;
// Resulting value can be assigned to a bool.
rettv->v_lock |= VAR_BOOL_OK;
} }
return OK; return OK;

View File

@@ -1382,7 +1382,7 @@ struct type_S {
typedef struct typedef struct
{ {
vartype_T v_type; vartype_T v_type;
char v_lock; // see below: VAR_LOCKED, VAR_FIXED, VAR_BOOL_OK char v_lock; // see below: VAR_LOCKED, VAR_FIXED
union union
{ {
varnumber_T v_number; // number value varnumber_T v_number; // number value
@@ -1409,7 +1409,6 @@ typedef struct
// Values for "v_lock". // Values for "v_lock".
#define VAR_LOCKED 1 // locked with lock(), can use unlock() #define VAR_LOCKED 1 // locked with lock(), can use unlock()
#define VAR_FIXED 2 // locked forever #define VAR_FIXED 2 // locked forever
#define VAR_BOOL_OK 4 // can be convered to bool
/* /*
* Structure to hold an item of a list: an internal variable without a name. * Structure to hold an item of a list: an internal variable without a name.

View File

@@ -22,11 +22,11 @@ def Test_assignment_bool()
var bool4: bool = 1 var bool4: bool = 1
assert_equal(true, bool4) assert_equal(true, bool4)
var bool5: bool = 'yes' && 'no' var bool5: bool = 1 && true
assert_equal(true, bool5) assert_equal(true, bool5)
var bool6: bool = [] && 99 var bool6: bool = 0 && 1
assert_equal(false, bool6) assert_equal(false, bool6)
var bool7: bool = [] || #{a: 1} && 99 var bool7: bool = 0 || 1 && true
assert_equal(true, bool7) assert_equal(true, bool7)
var lines =<< trim END var lines =<< trim END
@@ -41,9 +41,9 @@ def Test_assignment_bool()
assert_equal(false, flag) assert_equal(false, flag)
flag = 1 flag = 1
assert_equal(true, flag) assert_equal(true, flag)
flag = 99 || 123 flag = 1 || true
assert_equal(true, flag) assert_equal(true, flag)
flag = 'yes' && [] flag = 1 && false
assert_equal(false, flag) assert_equal(false, flag)
END END
CheckScriptSuccess(lines) CheckScriptSuccess(lines)

View File

@@ -72,8 +72,8 @@ def Test_if_linebreak()
var lines =<< trim END var lines =<< trim END
vim9script vim9script
if 1 && if 1 &&
2 true
|| 3 || 1
g:res = 42 g:res = 42
endif endif
assert_equal(42, g:res) assert_equal(42, g:res)

View File

@@ -766,11 +766,11 @@ def Test_disassemble_and_or()
'\d LOAD arg\[-1]\_s*' .. '\d LOAD arg\[-1]\_s*' ..
'\d PUSHNR 1\_s*' .. '\d PUSHNR 1\_s*' ..
'\d COMPAREANY ==\_s*' .. '\d COMPAREANY ==\_s*' ..
'\d JUMP_AND_KEEP_IF_FALSE -> \d\+\_s*' .. '\d JUMP_IF_COND_FALSE -> \d\+\_s*' ..
'\d LOAD arg\[-1]\_s*' .. '\d LOAD arg\[-1]\_s*' ..
'\d PUSHNR 2\_s*' .. '\d PUSHNR 2\_s*' ..
'\d COMPAREANY !=\_s*' .. '\d COMPAREANY !=\_s*' ..
'\d JUMP_AND_KEEP_IF_TRUE -> \d\+\_s*' .. '\d JUMP_IF_COND_TRUE -> \d\+\_s*' ..
'\d LOAD arg\[-1]\_s*' .. '\d LOAD arg\[-1]\_s*' ..
'\d\+ PUSHNR 4\_s*' .. '\d\+ PUSHNR 4\_s*' ..
'\d\+ COMPAREANY ==\_s*' .. '\d\+ COMPAREANY ==\_s*' ..
@@ -1200,22 +1200,23 @@ def Test_disassemble_invert_bool()
enddef enddef
def ReturnBool(): bool def ReturnBool(): bool
var var: bool = "no" && [] || 123 var name: bool = 1 && 0 || 1
return var return name
enddef enddef
def Test_disassemble_return_bool() def Test_disassemble_return_bool()
var instr = execute('disassemble ReturnBool') var instr = execute('disassemble ReturnBool')
assert_match('ReturnBool\_s*' .. assert_match('ReturnBool\_s*' ..
'var var: bool = "no" && \[\] || 123\_s*' .. 'var name: bool = 1 && 0 || 1\_s*' ..
'0 PUSHS "no"\_s*' .. '0 PUSHNR 1\_s*' ..
'1 JUMP_AND_KEEP_IF_FALSE -> 3\_s*' .. '1 JUMP_IF_COND_FALSE -> 3\_s*' ..
'2 NEWLIST size 0\_s*' .. '2 PUSHNR 0\_s*' ..
'3 JUMP_AND_KEEP_IF_TRUE -> 5\_s*' .. '3 COND2BOOL\_s*' ..
'4 PUSHNR 123\_s*' .. '4 JUMP_IF_COND_TRUE -> 6\_s*' ..
'5 2BOOL (!!val)\_s*' .. '5 PUSHNR 1\_s*' ..
'6 2BOOL (!!val)\_s*' ..
'\d STORE $0\_s*' .. '\d STORE $0\_s*' ..
'return var\_s*' .. 'return name\_s*' ..
'\d LOAD $0\_s*' .. '\d LOAD $0\_s*' ..
'\d RETURN', '\d RETURN',
instr) instr)

View File

@@ -196,32 +196,32 @@ enddef
" test || " test ||
def Test_expr2() def Test_expr2()
assert_equal(2, 2 || 0) assert_equal(true, 1 || 0)
assert_equal(7, 0 || assert_equal(true, 0 ||
0 || 0 ||
7) 1)
assert_equal(0, 0 || 0) assert_equal(false, 0 || 0)
assert_equal(0, 0 assert_equal(false, 0
|| 0) || 0)
assert_equal('', 0 || '') assert_equal(false, 0 || false)
g:vals = [] g:vals = []
assert_equal(3, Record(3) || Record(1)) assert_equal(true, Record(1) || Record(3))
assert_equal([3], g:vals) assert_equal([1], g:vals)
g:vals = [] g:vals = []
assert_equal(5, Record(0) || Record(5)) assert_equal(true, Record(0) || Record(1))
assert_equal([0, 5], g:vals) assert_equal([0, 1], g:vals)
g:vals = [] g:vals = []
assert_equal(4, Record(0) assert_equal(true, Record(0)
|| Record(4) || Record(1)
|| Record(0)) || Record(0))
assert_equal([0, 4], g:vals) assert_equal([0, 1], g:vals)
g:vals = [] g:vals = []
assert_equal(0, Record([]) || Record('') || Record(0)) assert_equal(false, Record(0) || Record(false) || Record(0))
assert_equal([[], '', 0], g:vals) assert_equal([0, false, 0], g:vals)
enddef enddef
def Test_expr2_vimscript() def Test_expr2_vimscript()
@@ -230,7 +230,7 @@ def Test_expr2_vimscript()
vim9script vim9script
var name = 0 var name = 0
|| 1 || 1
assert_equal(1, name) assert_equal(true, name)
END END
CheckScriptSuccess(lines) CheckScriptSuccess(lines)
@@ -269,80 +269,85 @@ def Test_expr2_vimscript()
END END
CheckScriptFailure(lines, 'E1004:', 2) CheckScriptFailure(lines, 'E1004:', 2)
# check keeping the value # check evaluating to bool
lines =<< trim END lines =<< trim END
vim9script assert_equal(true, 1 || 0)
assert_equal(2, 2 || 0) assert_equal(true, 0 ||
assert_equal(7, 0 ||
0 || 0 ||
7) !!7)
assert_equal(0, 0 || 0) assert_equal(false, 0 || 0)
assert_equal(0, 0 assert_equal(false, 0
|| 0) || 0)
assert_equal('', 0 || '') assert_equal(false, 0 || false)
g:vals = [] g:vals = []
assert_equal(3, Record(3) || Record(1)) assert_equal(true, Record(true) || Record(false))
assert_equal([3], g:vals) assert_equal([true], g:vals)
g:vals = [] g:vals = []
assert_equal(5, Record(0) || Record(5)) assert_equal(true, Record(0) || Record(true))
assert_equal([0, 5], g:vals) assert_equal([0, true], g:vals)
g:vals = [] g:vals = []
assert_equal(4, Record(0) assert_equal(true, Record(0)
|| Record(4) || Record(true)
|| Record(0)) || Record(0))
assert_equal([0, 4], g:vals) assert_equal([0, true], g:vals)
g:vals = [] g:vals = []
assert_equal(0, Record([]) || Record('') || Record(0)) assert_equal(false, Record(0) || Record(false) || Record(0))
assert_equal([[], '', 0], g:vals) assert_equal([0, false, 0], g:vals)
END END
CheckScriptSuccess(lines) CheckDefAndScriptSuccess(lines)
enddef enddef
func Test_expr2_fails() def Test_expr2_fails()
let msg = "White space required before and after '||'" var msg = "White space required before and after '||'"
call CheckDefFailure(["var x = 1||2"], msg, 1) call CheckDefFailure(["var x = 1||2"], msg, 1)
call CheckDefFailure(["var x = 1 ||2"], msg, 1) call CheckDefFailure(["var x = 1 ||2"], msg, 1)
call CheckDefFailure(["var x = 1|| 2"], msg, 1) call CheckDefFailure(["var x = 1|| 2"], msg, 1)
call CheckDefFailure(["var x = 1 || xxx"], 'E1001:', 1) call CheckDefFailure(["var x = 1 || xxx"], 'E1001:', 1)
endfunc
# TODO: should fail at compile time
call CheckDefExecFailure(["var x = 3 || 7"], 'E1023:', 1)
call CheckScriptFailure(["vim9script", "var x = 3 || 7"], 'E1023:', 2)
call CheckDefExecFailure(["var x = [] || false"], 'E745:', 1)
call CheckScriptFailure(["vim9script", "var x = [] || false"], 'E745:', 2)
enddef
" test && " test &&
def Test_expr3() def Test_expr3()
assert_equal(0, 2 && 0) assert_equal(false, 1 && 0)
assert_equal(0, 0 && assert_equal(false, 0 &&
0 && 0 &&
7) 1)
assert_equal(7, 2 assert_equal(true, 1
&& 3 && true
&& 7) && 1)
assert_equal(0, 0 && 0) assert_equal(false, 0 && 0)
assert_equal(0, 0 && '') assert_equal(false, 0 && false)
assert_equal('', 8 && '') assert_equal(true, 1 && true)
g:vals = [] g:vals = []
assert_equal(1, Record(3) && Record(1)) assert_equal(true, Record(true) && Record(1))
assert_equal([3, 1], g:vals) assert_equal([true, 1], g:vals)
g:vals = [] g:vals = []
assert_equal(0, Record(0) && Record(5)) assert_equal(false, Record(0) && Record(1))
assert_equal([0], g:vals) assert_equal([0], g:vals)
g:vals = [] g:vals = []
assert_equal(0, Record(0) && Record(4) && Record(0)) assert_equal(false, Record(0) && Record(4) && Record(0))
assert_equal([0], g:vals) assert_equal([0], g:vals)
g:vals = [] g:vals = []
assert_equal(0, Record(8) && Record(4) && Record(0)) assert_equal(false, Record(1) && Record(true) && Record(0))
assert_equal([8, 4, 0], g:vals) assert_equal([1, true, 0], g:vals)
g:vals = [] g:vals = []
assert_equal(0, Record([1]) && Record('z') && Record(0)) assert_equal(false, Record(1) && Record(true) && Record(0))
assert_equal([[1], 'z', 0], g:vals) assert_equal([1, true, 0], g:vals)
enddef enddef
def Test_expr3_vimscript() def Test_expr3_vimscript()
@@ -351,7 +356,7 @@ def Test_expr3_vimscript()
vim9script vim9script
var name = 0 var name = 0
&& 1 && 1
assert_equal(0, name) assert_equal(false, name)
END END
CheckScriptSuccess(lines) CheckScriptSuccess(lines)
@@ -393,36 +398,32 @@ def Test_expr3_vimscript()
# check keeping the value # check keeping the value
lines =<< trim END lines =<< trim END
vim9script vim9script
assert_equal(0, 2 && 0) assert_equal(false, 1 && 0)
assert_equal(0, 0 && assert_equal(false, 0 &&
0 && 0 &&
7) 1)
assert_equal(7, 2 assert_equal(true, 1
&& 3 && true
&& 7) && 1)
assert_equal(0, 0 && 0) assert_equal(false, 0 && 0)
assert_equal(0, 0 && '') assert_equal(false, 0 && false)
assert_equal('', 8 && '') assert_equal(false, 1 && 0)
g:vals = [] g:vals = []
assert_equal(1, Record(3) && Record(1)) assert_equal(true, Record(1) && Record(true))
assert_equal([3, 1], g:vals) assert_equal([1, true], g:vals)
g:vals = [] g:vals = []
assert_equal(0, Record(0) && Record(5)) assert_equal(false, Record(0) && Record(1))
assert_equal([0], g:vals) assert_equal([0], g:vals)
g:vals = [] g:vals = []
assert_equal(0, Record(0) && Record(4) && Record(0)) assert_equal(false, Record(0) && Record(1) && Record(0))
assert_equal([0], g:vals) assert_equal([0], g:vals)
g:vals = [] g:vals = []
assert_equal(0, Record(8) && Record(4) && Record(0)) assert_equal(false, Record(1) && Record(true) && Record(0))
assert_equal([8, 4, 0], g:vals) assert_equal([1, true, 0], g:vals)
g:vals = []
assert_equal(0, Record([1]) && Record('z') && Record(0))
assert_equal([[1], 'z', 0], g:vals)
END END
CheckScriptSuccess(lines) CheckScriptSuccess(lines)
enddef enddef

View File

@@ -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 */
/**/
1795,
/**/ /**/
1794, 1794,
/**/ /**/

View File

@@ -128,7 +128,8 @@ typedef enum {
ISN_GETITEM, // push list item, isn_arg.number is the index ISN_GETITEM, // push list item, isn_arg.number is the index
ISN_MEMBER, // dict[member] ISN_MEMBER, // dict[member]
ISN_STRINGMEMBER, // dict.member using isn_arg.string ISN_STRINGMEMBER, // dict.member using isn_arg.string
ISN_2BOOL, // convert value to bool, invert if isn_arg.number != 0 ISN_2BOOL, // falsy/truthy to bool, invert if isn_arg.number != 0
ISN_COND2BOOL, // convert value to bool
ISN_2STRING, // convert value to string at isn_arg.number on stack ISN_2STRING, // convert value to string at isn_arg.number on stack
ISN_2STRING_ANY, // like ISN_2STRING but check type ISN_2STRING_ANY, // like ISN_2STRING but check type
ISN_NEGATENR, // apply "-" to number ISN_NEGATENR, // apply "-" to number
@@ -171,8 +172,10 @@ typedef struct {
typedef enum { typedef enum {
JUMP_ALWAYS, JUMP_ALWAYS,
JUMP_IF_FALSE, // pop and jump if false JUMP_IF_FALSE, // pop and jump if false
JUMP_AND_KEEP_IF_TRUE, // jump if top of stack is true, drop if not JUMP_AND_KEEP_IF_TRUE, // jump if top of stack is truthy, drop if not
JUMP_AND_KEEP_IF_FALSE, // jump if top of stack is false, drop if not JUMP_AND_KEEP_IF_FALSE, // jump if top of stack is falsy, drop if not
JUMP_IF_COND_TRUE, // jump if top of stack is true, drop if not
JUMP_IF_COND_FALSE, // jump if top of stack is false, drop if not
} jumpwhen_T; } jumpwhen_T;
// arguments to ISN_JUMP // arguments to ISN_JUMP

View File

@@ -706,6 +706,25 @@ generate_2BOOL(cctx_T *cctx, int invert)
return OK; return OK;
} }
/*
* Generate an ISN_COND2BOOL instruction.
*/
static int
generate_COND2BOOL(cctx_T *cctx)
{
isn_T *isn;
garray_T *stack = &cctx->ctx_type_stack;
RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr(cctx, ISN_COND2BOOL)) == NULL)
return FAIL;
// type becomes bool
((type_T **)stack->ga_data)[stack->ga_len - 1] = &t_bool;
return OK;
}
static int static int
generate_TYPECHECK( generate_TYPECHECK(
cctx_T *cctx, cctx_T *cctx,
@@ -4003,7 +4022,7 @@ compile_and_or(
garray_T *instr = &cctx->ctx_instr; garray_T *instr = &cctx->ctx_instr;
garray_T end_ga; garray_T end_ga;
garray_T *stack = &cctx->ctx_type_stack; garray_T *stack = &cctx->ctx_type_stack;
type_T **typep; int all_bool_values = TRUE;
/* /*
* Repeat until there is no following "||" or "&&" * Repeat until there is no following "||" or "&&"
@@ -4023,9 +4042,13 @@ compile_and_or(
return FAIL; return FAIL;
} }
// TODO: use ppconst if the value is a constant // TODO: use ppconst if the value is a constant and check
// evaluating to bool
generate_ppconst(cctx, ppconst); generate_ppconst(cctx, ppconst);
if (((type_T **)stack->ga_data)[stack->ga_len - 1] != &t_bool)
all_bool_values = FALSE;
if (ga_grow(&end_ga, 1) == FAIL) if (ga_grow(&end_ga, 1) == FAIL)
{ {
ga_clear(&end_ga); ga_clear(&end_ga);
@@ -4034,7 +4057,7 @@ compile_and_or(
*(((int *)end_ga.ga_data) + end_ga.ga_len) = instr->ga_len; *(((int *)end_ga.ga_data) + end_ga.ga_len) = instr->ga_len;
++end_ga.ga_len; ++end_ga.ga_len;
generate_JUMP(cctx, opchar == '|' generate_JUMP(cctx, opchar == '|'
? JUMP_AND_KEEP_IF_TRUE : JUMP_AND_KEEP_IF_FALSE, 0); ? JUMP_IF_COND_TRUE : JUMP_IF_COND_FALSE, 0);
// eval the next expression // eval the next expression
*arg = skipwhite(p + 2); *arg = skipwhite(p + 2);
@@ -4064,19 +4087,9 @@ compile_and_or(
} }
ga_clear(&end_ga); ga_clear(&end_ga);
// The resulting type can be used as a bool. // The resulting type is converted to bool if needed.
typep = ((type_T **)stack->ga_data) + stack->ga_len - 1; if (!all_bool_values)
if (*typep != &t_bool) generate_COND2BOOL(cctx);
{
type_T *type = get_type_ptr(cctx->ctx_type_list);
if (type != NULL)
{
*type = **typep;
type->tt_flags |= TTFLAG_BOOL_OK;
*typep = type;
}
}
} }
return OK; return OK;
@@ -4087,10 +4100,11 @@ compile_and_or(
* *
* Produces instructions: * Produces instructions:
* EVAL expr4a Push result of "expr4a" * EVAL expr4a Push result of "expr4a"
* JUMP_AND_KEEP_IF_FALSE end * JUMP_IF_COND_FALSE end
* EVAL expr4b Push result of "expr4b" * EVAL expr4b Push result of "expr4b"
* JUMP_AND_KEEP_IF_FALSE end * JUMP_IF_COND_FALSE end
* EVAL expr4c Push result of "expr4c" * EVAL expr4c Push result of "expr4c"
* COND2BOOL
* end: * end:
*/ */
static int static int
@@ -4111,10 +4125,11 @@ compile_expr3(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
* *
* Produces instructions: * Produces instructions:
* EVAL expr3a Push result of "expr3a" * EVAL expr3a Push result of "expr3a"
* JUMP_AND_KEEP_IF_TRUE end * JUMP_IF_COND_TRUE end
* EVAL expr3b Push result of "expr3b" * EVAL expr3b Push result of "expr3b"
* JUMP_AND_KEEP_IF_TRUE end * JUMP_IF_COND_TRUE end
* EVAL expr3c Push result of "expr3c" * EVAL expr3c Push result of "expr3c"
* COND2BOOL
* end: * end:
*/ */
static int static int
@@ -7415,6 +7430,7 @@ delete_instr(isn_T *isn)
case ISN_COMPARESPECIAL: case ISN_COMPARESPECIAL:
case ISN_COMPARESTRING: case ISN_COMPARESTRING:
case ISN_CONCAT: case ISN_CONCAT:
case ISN_COND2BOOL:
case ISN_DROP: case ISN_DROP:
case ISN_ECHO: case ISN_ECHO:
case ISN_ECHOERR: case ISN_ECHOERR:

View File

@@ -1901,14 +1901,25 @@ call_def_function(
case ISN_JUMP: case ISN_JUMP:
{ {
jumpwhen_T when = iptr->isn_arg.jump.jump_when; jumpwhen_T when = iptr->isn_arg.jump.jump_when;
int error = FALSE;
int jump = TRUE; int jump = TRUE;
if (when != JUMP_ALWAYS) if (when != JUMP_ALWAYS)
{ {
tv = STACK_TV_BOT(-1); tv = STACK_TV_BOT(-1);
jump = tv2bool(tv); if (when == JUMP_IF_COND_FALSE
|| when == JUMP_IF_COND_TRUE)
{
SOURCING_LNUM = iptr->isn_lnum;
jump = tv_get_bool_chk(tv, &error);
if (error)
goto on_error;
}
else
jump = tv2bool(tv);
if (when == JUMP_IF_FALSE if (when == JUMP_IF_FALSE
|| when == JUMP_AND_KEEP_IF_FALSE) || when == JUMP_AND_KEEP_IF_FALSE
|| when == JUMP_IF_COND_FALSE)
jump = !jump; jump = !jump;
if (when == JUMP_IF_FALSE || !jump) if (when == JUMP_IF_FALSE || !jump)
{ {
@@ -2624,13 +2635,25 @@ call_def_function(
break; break;
case ISN_2BOOL: case ISN_2BOOL:
case ISN_COND2BOOL:
{ {
int n; int n;
int error = FALSE;
tv = STACK_TV_BOT(-1); tv = STACK_TV_BOT(-1);
n = tv2bool(tv); if (iptr->isn_type == ISN_2BOOL)
if (iptr->isn_arg.number) // invert {
n = !n; n = tv2bool(tv);
if (iptr->isn_arg.number) // invert
n = !n;
}
else
{
SOURCING_LNUM = iptr->isn_lnum;
n = tv_get_bool_chk(tv, &error);
if (error)
goto on_error;
}
clear_tv(tv); clear_tv(tv);
tv->v_type = VAR_BOOL; tv->v_type = VAR_BOOL;
tv->vval.v_number = n ? VVAL_TRUE : VVAL_FALSE; tv->vval.v_number = n ? VVAL_TRUE : VVAL_FALSE;
@@ -3192,6 +3215,12 @@ ex_disassemble(exarg_T *eap)
case JUMP_AND_KEEP_IF_FALSE: case JUMP_AND_KEEP_IF_FALSE:
when = "JUMP_AND_KEEP_IF_FALSE"; when = "JUMP_AND_KEEP_IF_FALSE";
break; break;
case JUMP_IF_COND_FALSE:
when = "JUMP_IF_COND_FALSE";
break;
case JUMP_IF_COND_TRUE:
when = "JUMP_IF_COND_TRUE";
break;
} }
smsg("%4d %s -> %d", current, when, smsg("%4d %s -> %d", current, when,
iptr->isn_arg.jump.jump_where); iptr->isn_arg.jump.jump_where);
@@ -3342,6 +3371,7 @@ ex_disassemble(exarg_T *eap)
iptr->isn_arg.checklen.cl_more_OK ? ">= " : "", iptr->isn_arg.checklen.cl_more_OK ? ">= " : "",
iptr->isn_arg.checklen.cl_min_len); iptr->isn_arg.checklen.cl_min_len);
break; break;
case ISN_COND2BOOL: smsg("%4d COND2BOOL", current); break;
case ISN_2BOOL: if (iptr->isn_arg.number) case ISN_2BOOL: if (iptr->isn_arg.number)
smsg("%4d INVERT (!val)", current); smsg("%4d INVERT (!val)", current);
else else

View File

@@ -360,13 +360,12 @@ typval2type_int(typval_T *tv, garray_T *type_gap)
need_convert_to_bool(type_T *type, typval_T *tv) need_convert_to_bool(type_T *type, typval_T *tv)
{ {
return type != NULL && type == &t_bool && tv->v_type != VAR_BOOL return type != NULL && type == &t_bool && tv->v_type != VAR_BOOL
&& ((tv->v_lock & VAR_BOOL_OK) && (tv->v_type == VAR_NUMBER
|| (tv->v_type == VAR_NUMBER && (tv->vval.v_number == 0 || tv->vval.v_number == 1));
&& (tv->vval.v_number == 0 || tv->vval.v_number == 1)));
} }
/* /*
* Get a type_T for a typval_T and handle VAR_BOOL_OK. * Get a type_T for a typval_T.
* "type_list" is used to temporarily create types in. * "type_list" is used to temporarily create types in.
*/ */
type_T * type_T *
@@ -375,9 +374,8 @@ typval2type(typval_T *tv, garray_T *type_gap)
type_T *type = typval2type_int(tv, type_gap); type_T *type = typval2type_int(tv, type_gap);
if (type != NULL && type != &t_bool if (type != NULL && type != &t_bool
&& ((tv->v_type == VAR_NUMBER && (tv->v_type == VAR_NUMBER
&& (tv->vval.v_number == 0 || tv->vval.v_number == 1)) && (tv->vval.v_number == 0 || tv->vval.v_number == 1)))
|| (tv->v_lock & VAR_BOOL_OK)))
{ {
type_T *newtype = get_type_ptr(type_gap); type_T *newtype = get_type_ptr(type_gap);