vim-patch:8.2.0111: VAR_SPECIAL is also used for booleans

Problem:    VAR_SPECIAL is also used for booleans.
Solution:   Add VAR_BOOL for better type checking.
9b4a15d5db
This commit is contained in:
Billy Su
2020-04-28 23:21:50 +08:00
parent dbc8ec9446
commit 1805fb469a
20 changed files with 162 additions and 100 deletions

View File

@@ -1075,8 +1075,8 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err)
break; break;
case kObjectTypeBoolean: case kObjectTypeBoolean:
tv->v_type = VAR_SPECIAL; tv->v_type = VAR_BOOL;
tv->vval.v_special = obj.data.boolean? kSpecialVarTrue: kSpecialVarFalse; tv->vval.v_bool = obj.data.boolean? kBoolVarTrue: kBoolVarFalse;
break; break;
case kObjectTypeBuffer: case kObjectTypeBuffer:

View File

@@ -213,8 +213,8 @@ static struct vimvar {
VV(VV_ERRORS, "errors", VAR_LIST, 0), VV(VV_ERRORS, "errors", VAR_LIST, 0),
VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO), VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO),
VV(VV_EVENT, "event", VAR_DICT, VV_RO), VV(VV_EVENT, "event", VAR_DICT, VV_RO),
VV(VV_FALSE, "false", VAR_SPECIAL, VV_RO), VV(VV_FALSE, "false", VAR_BOOL, VV_RO),
VV(VV_TRUE, "true", VAR_SPECIAL, VV_RO), VV(VV_TRUE, "true", VAR_BOOL, VV_RO),
VV(VV_NULL, "null", VAR_SPECIAL, VV_RO), VV(VV_NULL, "null", VAR_SPECIAL, VV_RO),
VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO), VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO),
VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO), VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO),
@@ -237,6 +237,7 @@ static struct vimvar {
// shorthand // shorthand
#define vv_type vv_di.di_tv.v_type #define vv_type vv_di.di_tv.v_type
#define vv_nr vv_di.di_tv.vval.v_number #define vv_nr vv_di.di_tv.vval.v_number
#define vv_bool vv_di.di_tv.vval.v_bool
#define vv_special vv_di.di_tv.vval.v_special #define vv_special vv_di.di_tv.vval.v_special
#define vv_float vv_di.di_tv.vval.v_float #define vv_float vv_di.di_tv.vval.v_float
#define vv_str vv_di.di_tv.vval.v_string #define vv_str vv_di.di_tv.vval.v_string
@@ -388,8 +389,8 @@ void eval_init(void)
set_vim_var_nr(VV_TYPE_FLOAT, VAR_TYPE_FLOAT); set_vim_var_nr(VV_TYPE_FLOAT, VAR_TYPE_FLOAT);
set_vim_var_nr(VV_TYPE_BOOL, VAR_TYPE_BOOL); set_vim_var_nr(VV_TYPE_BOOL, VAR_TYPE_BOOL);
set_vim_var_special(VV_FALSE, kSpecialVarFalse); set_vim_var_bool(VV_FALSE, kBoolVarFalse);
set_vim_var_special(VV_TRUE, kSpecialVarTrue); set_vim_var_bool(VV_TRUE, kBoolVarTrue);
set_vim_var_special(VV_NULL, kSpecialVarNull); set_vim_var_special(VV_NULL, kSpecialVarNull);
set_vim_var_special(VV_EXITING, kSpecialVarNull); set_vim_var_special(VV_EXITING, kSpecialVarNull);
@@ -4199,6 +4200,7 @@ eval_index(
} }
return FAIL; return FAIL;
} }
case VAR_BOOL:
case VAR_SPECIAL: { case VAR_SPECIAL: {
if (verbose) { if (verbose) {
EMSG(_("E909: Cannot index a special variable")); EMSG(_("E909: Cannot index a special variable"));
@@ -4420,6 +4422,7 @@ eval_index(
*rettv = var1; *rettv = var1;
break; break;
} }
case VAR_BOOL:
case VAR_SPECIAL: case VAR_SPECIAL:
case VAR_FUNC: case VAR_FUNC:
case VAR_FLOAT: case VAR_FLOAT:
@@ -5273,6 +5276,7 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack,
abort = set_ref_in_func(tv->vval.v_string, NULL, copyID); abort = set_ref_in_func(tv->vval.v_string, NULL, copyID);
break; break;
case VAR_UNKNOWN: case VAR_UNKNOWN:
case VAR_BOOL:
case VAR_SPECIAL: case VAR_SPECIAL:
case VAR_FLOAT: case VAR_FLOAT:
case VAR_NUMBER: case VAR_NUMBER:
@@ -5743,11 +5747,11 @@ int assert_bool(typval_T *argvars, bool is_true)
if ((argvars[0].v_type != VAR_NUMBER if ((argvars[0].v_type != VAR_NUMBER
|| (tv_get_number_chk(&argvars[0], &error) == 0) == is_true || (tv_get_number_chk(&argvars[0], &error) == 0) == is_true
|| error) || error)
&& (argvars[0].v_type != VAR_SPECIAL && (argvars[0].v_type != VAR_BOOL
|| (argvars[0].vval.v_special || (argvars[0].vval.v_bool
!= (SpecialVarValue) (is_true != (BoolVarValue)(is_true
? kSpecialVarTrue ? kBoolVarTrue
: kSpecialVarFalse)))) { : kBoolVarFalse)))) {
prepare_assert_error(&ga); prepare_assert_error(&ga);
fill_assert_error(&ga, &argvars[1], fill_assert_error(&ga, &argvars[1],
(char_u *)(is_true ? "True" : "False"), (char_u *)(is_true ? "True" : "False"),
@@ -8049,6 +8053,17 @@ void set_vim_var_nr(const VimVarIndex idx, const varnumber_T val)
vimvars[idx].vv_nr = val; vimvars[idx].vv_nr = val;
} }
/// Set boolean v: {true, false} to the given value
///
/// @param[in] idx Index of variable to set.
/// @param[in] val Value to set to.
void set_vim_var_bool(const VimVarIndex idx, const BoolVarValue val)
{
tv_clear(&vimvars[idx].vv_tv);
vimvars[idx].vv_type = VAR_BOOL;
vimvars[idx].vv_bool = val;
}
/// Set special v: variable to the given value /// Set special v: variable to the given value
/// ///
/// @param[in] idx Index of variable to set. /// @param[in] idx Index of variable to set.
@@ -9143,6 +9158,7 @@ int var_item_copy(const vimconv_T *const conv,
case VAR_FLOAT: case VAR_FLOAT:
case VAR_FUNC: case VAR_FUNC:
case VAR_PARTIAL: case VAR_PARTIAL:
case VAR_BOOL:
case VAR_SPECIAL: case VAR_SPECIAL:
tv_copy(from, to); tv_copy(from, to);
break; break;

View File

@@ -795,9 +795,9 @@ json_decode_string_cycle_start:
} }
p += 3; p += 3;
POP(((typval_T) { POP(((typval_T) {
.v_type = VAR_SPECIAL, .v_type = VAR_BOOL,
.v_lock = VAR_UNLOCKED, .v_lock = VAR_UNLOCKED,
.vval = { .v_special = kSpecialVarTrue }, .vval = { .v_bool = kBoolVarTrue },
}), false); }), false);
break; break;
} }
@@ -808,9 +808,9 @@ json_decode_string_cycle_start:
} }
p += 4; p += 4;
POP(((typval_T) { POP(((typval_T) {
.v_type = VAR_SPECIAL, .v_type = VAR_BOOL,
.v_lock = VAR_UNLOCKED, .v_lock = VAR_UNLOCKED,
.vval = { .v_special = kSpecialVarFalse }, .vval = { .v_bool = kBoolVarFalse },
}), false); }), false);
break; break;
} }
@@ -954,10 +954,10 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
} }
case MSGPACK_OBJECT_BOOLEAN: { case MSGPACK_OBJECT_BOOLEAN: {
*rettv = (typval_T) { *rettv = (typval_T) {
.v_type = VAR_SPECIAL, .v_type = VAR_BOOL,
.v_lock = VAR_UNLOCKED, .v_lock = VAR_UNLOCKED,
.vval = { .vval = {
.v_special = mobj.via.boolean ? kSpecialVarTrue : kSpecialVarFalse .v_bool = mobj.via.boolean ? kBoolVarTrue : kBoolVarFalse
}, },
}; };
break; break;

View File

@@ -34,10 +34,13 @@
#define utf_ptr2len(b) ((size_t)utf_ptr2len((char_u *)b)) #define utf_ptr2len(b) ((size_t)utf_ptr2len((char_u *)b))
#define utf_char2len(b) ((size_t)utf_char2len(b)) #define utf_char2len(b) ((size_t)utf_char2len(b))
const char *const encode_bool_var_names[] = {
[kBoolVarTrue] = "true",
[kBoolVarFalse] = "false",
};
const char *const encode_special_var_names[] = { const char *const encode_special_var_names[] = {
[kSpecialVarNull] = "null", [kSpecialVarNull] = "null",
[kSpecialVarTrue] = "true",
[kSpecialVarFalse] = "false",
}; };
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS

View File

@@ -55,6 +55,7 @@ static inline ListReaderState encode_init_lrstate(const list_T *const list)
} }
/// Array mapping values from SpecialVarValue enum to names /// Array mapping values from SpecialVarValue enum to names
extern const char *const encode_bool_var_names[];
extern const char *const encode_special_var_names[]; extern const char *const encode_special_var_names[];
/// First codepoint in high surrogates block /// First codepoint in high surrogates block

View File

@@ -28,11 +28,13 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2,
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NO_SANITIZE_UNDEFINED FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NO_SANITIZE_UNDEFINED
{ {
// Can't do anything with a Funcref, a Dict or special value on the right. // Can't do anything with a Funcref, a Dict or special value on the right.
if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT) { if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT
&& tv2->v_type != VAR_BOOL && tv2->v_type != VAR_SPECIAL) {
switch (tv1->v_type) { switch (tv1->v_type) {
case VAR_DICT: case VAR_DICT:
case VAR_FUNC: case VAR_FUNC:
case VAR_PARTIAL: case VAR_PARTIAL:
case VAR_BOOL:
case VAR_SPECIAL: { case VAR_SPECIAL: {
break; break;
} }

View File

@@ -175,8 +175,8 @@ static int non_zero_arg(typval_T *argvars)
{ {
return ((argvars[0].v_type == VAR_NUMBER return ((argvars[0].v_type == VAR_NUMBER
&& argvars[0].vval.v_number != 0) && argvars[0].vval.v_number != 0)
|| (argvars[0].v_type == VAR_SPECIAL || (argvars[0].v_type == VAR_BOOL
&& argvars[0].vval.v_special == kSpecialVarTrue) && argvars[0].vval.v_bool == kBoolVarTrue)
|| (argvars[0].v_type == VAR_STRING || (argvars[0].v_type == VAR_STRING
&& argvars[0].vval.v_string != NULL && argvars[0].vval.v_string != NULL
&& *argvars[0].vval.v_string != NUL)); && *argvars[0].vval.v_string != NUL));
@@ -1758,21 +1758,23 @@ static void f_empty(typval_T *argvars, typval_T *rettv, FunPtr fptr)
n = (tv_dict_len(argvars[0].vval.v_dict) == 0); n = (tv_dict_len(argvars[0].vval.v_dict) == 0);
break; break;
} }
case VAR_SPECIAL: { case VAR_BOOL: {
// Using switch to get warning if SpecialVarValue receives more values. switch (argvars[0].vval.v_bool) {
switch (argvars[0].vval.v_special) { case kBoolVarTrue: {
case kSpecialVarTrue: {
n = false; n = false;
break; break;
} }
case kSpecialVarFalse: case kBoolVarFalse: {
case kSpecialVarNull: {
n = true; n = true;
break; break;
} }
} }
break; break;
} }
case VAR_SPECIAL: {
n = argvars[0].vval.v_special == kSpecialVarNull;
break;
}
case VAR_UNKNOWN: { case VAR_UNKNOWN: {
internal_error("f_empty(UNKNOWN)"); internal_error("f_empty(UNKNOWN)");
break; break;
@@ -5189,6 +5191,7 @@ static void f_len(typval_T *argvars, typval_T *rettv, FunPtr fptr)
break; break;
} }
case VAR_UNKNOWN: case VAR_UNKNOWN:
case VAR_BOOL:
case VAR_SPECIAL: case VAR_SPECIAL:
case VAR_FLOAT: case VAR_FLOAT:
case VAR_PARTIAL: case VAR_PARTIAL:
@@ -10808,20 +10811,8 @@ static void f_type(typval_T *argvars, typval_T *rettv, FunPtr fptr)
case VAR_LIST: n = VAR_TYPE_LIST; break; case VAR_LIST: n = VAR_TYPE_LIST; break;
case VAR_DICT: n = VAR_TYPE_DICT; break; case VAR_DICT: n = VAR_TYPE_DICT; break;
case VAR_FLOAT: n = VAR_TYPE_FLOAT; break; case VAR_FLOAT: n = VAR_TYPE_FLOAT; break;
case VAR_SPECIAL: { case VAR_BOOL: n = VAR_TYPE_BOOL; break;
switch (argvars[0].vval.v_special) { case VAR_SPECIAL:n = VAR_TYPE_SPECIAL; break;
case kSpecialVarTrue:
case kSpecialVarFalse: {
n = VAR_TYPE_BOOL;
break;
}
case kSpecialVarNull: {
n = 7;
break;
}
}
break;
}
case VAR_UNKNOWN: { case VAR_UNKNOWN: {
internal_error("f_type(UNKNOWN)"); internal_error("f_type(UNKNOWN)");
break; break;

View File

@@ -1693,21 +1693,21 @@ int tv_dict_add_float(dict_T *const d, const char *const key,
return OK; return OK;
} }
/// Add a special entry to dictionary /// Add a boolean entry to dictionary
/// ///
/// @param[out] d Dictionary to add entry to. /// @param[out] d Dictionary to add entry to.
/// @param[in] key Key to add. /// @param[in] key Key to add.
/// @param[in] key_len Key length. /// @param[in] key_len Key length.
/// @param[in] val SpecialVarValue to add. /// @param[in] val BoolVarValue to add.
/// ///
/// @return OK in case of success, FAIL when key already exists. /// @return OK in case of success, FAIL when key already exists.
int tv_dict_add_special(dict_T *const d, const char *const key, int tv_dict_add_bool(dict_T *const d, const char *const key,
const size_t key_len, SpecialVarValue val) const size_t key_len, BoolVarValue val)
{ {
dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); dictitem_T *const item = tv_dict_item_alloc_len(key, key_len);
item->di_tv.v_type = VAR_SPECIAL; item->di_tv.v_type = VAR_BOOL;
item->di_tv.vval.v_special = val; item->di_tv.vval.v_bool = val;
if (tv_dict_add(d, item) == FAIL) { if (tv_dict_add(d, item) == FAIL) {
tv_dict_item_free(item); tv_dict_item_free(item);
return FAIL; return FAIL;
@@ -2013,12 +2013,15 @@ void tv_dict_alloc_ret(typval_T *const ret_tv)
#define TYPVAL_ENCODE_CONV_NIL(tv) \ #define TYPVAL_ENCODE_CONV_NIL(tv) \
do { \ do { \
tv->vval.v_special = kSpecialVarFalse; \ tv->vval.v_special = kSpecialVarNull; \
tv->v_lock = VAR_UNLOCKED; \ tv->v_lock = VAR_UNLOCKED; \
} while (0) } while (0)
#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \ #define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
TYPVAL_ENCODE_CONV_NIL(tv) do { \
tv->vval.v_bool = kBoolVarFalse; \
tv->v_lock = VAR_UNLOCKED; \
} while (0)
#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \ #define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
do { \ do { \
@@ -2293,6 +2296,7 @@ void tv_free(typval_T *tv)
tv_dict_unref(tv->vval.v_dict); tv_dict_unref(tv->vval.v_dict);
break; break;
} }
case VAR_BOOL:
case VAR_SPECIAL: case VAR_SPECIAL:
case VAR_NUMBER: case VAR_NUMBER:
case VAR_FLOAT: case VAR_FLOAT:
@@ -2324,6 +2328,7 @@ void tv_copy(const typval_T *const from, typval_T *const to)
switch (from->v_type) { switch (from->v_type) {
case VAR_NUMBER: case VAR_NUMBER:
case VAR_FLOAT: case VAR_FLOAT:
case VAR_BOOL:
case VAR_SPECIAL: { case VAR_SPECIAL: {
break; break;
} }
@@ -2425,6 +2430,7 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock)
case VAR_STRING: case VAR_STRING:
case VAR_FUNC: case VAR_FUNC:
case VAR_PARTIAL: case VAR_PARTIAL:
case VAR_BOOL:
case VAR_SPECIAL: { case VAR_SPECIAL: {
break; break;
} }
@@ -2588,6 +2594,9 @@ bool tv_equal(typval_T *const tv1, typval_T *const tv2, const bool ic,
const char *s2 = tv_get_string_buf(tv2, buf2); const char *s2 = tv_get_string_buf(tv2, buf2);
return mb_strcmp_ic((bool)ic, s1, s2) == 0; return mb_strcmp_ic((bool)ic, s1, s2) == 0;
} }
case VAR_BOOL: {
return tv1->vval.v_bool == tv2->vval.v_bool;
}
case VAR_SPECIAL: { case VAR_SPECIAL: {
return tv1->vval.v_special == tv2->vval.v_special; return tv1->vval.v_special == tv2->vval.v_special;
} }
@@ -2638,6 +2647,10 @@ bool tv_check_str_or_nr(const typval_T *const tv)
EMSG(_("E728: Expected a Number or a String, Dictionary found")); EMSG(_("E728: Expected a Number or a String, Dictionary found"));
return false; return false;
} }
case VAR_BOOL: {
EMSG(_("E5299: Expected a Number or a String, Boolean found"));
return false;
}
case VAR_SPECIAL: { case VAR_SPECIAL: {
EMSG(_("E5300: Expected a Number or a String")); EMSG(_("E5300: Expected a Number or a String"));
return false; return false;
@@ -2677,6 +2690,7 @@ bool tv_check_num(const typval_T *const tv)
{ {
switch (tv->v_type) { switch (tv->v_type) {
case VAR_NUMBER: case VAR_NUMBER:
case VAR_BOOL:
case VAR_SPECIAL: case VAR_SPECIAL:
case VAR_STRING: { case VAR_STRING: {
return true; return true;
@@ -2721,6 +2735,7 @@ bool tv_check_str(const typval_T *const tv)
{ {
switch (tv->v_type) { switch (tv->v_type) {
case VAR_NUMBER: case VAR_NUMBER:
case VAR_BOOL:
case VAR_SPECIAL: case VAR_SPECIAL:
case VAR_STRING: { case VAR_STRING: {
return true; return true;
@@ -2791,8 +2806,11 @@ varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error)
} }
return n; return n;
} }
case VAR_BOOL: {
return tv->vval.v_bool == kBoolVarTrue ? 1 : 0;
}
case VAR_SPECIAL: { case VAR_SPECIAL: {
return tv->vval.v_special == kSpecialVarTrue ? 1 : 0; return 0;
} }
case VAR_UNKNOWN: { case VAR_UNKNOWN: {
emsgf(_(e_intern2), "tv_get_number(UNKNOWN)"); emsgf(_(e_intern2), "tv_get_number(UNKNOWN)");
@@ -2860,6 +2878,10 @@ float_T tv_get_float(const typval_T *const tv)
EMSG(_("E894: Using a Dictionary as a Float")); EMSG(_("E894: Using a Dictionary as a Float"));
break; break;
} }
case VAR_BOOL: {
EMSG(_("E362: Using a boolean value as a Float"));
break;
}
case VAR_SPECIAL: { case VAR_SPECIAL: {
EMSG(_("E907: Using a special value as a Float")); EMSG(_("E907: Using a special value as a Float"));
break; break;
@@ -2897,6 +2919,10 @@ const char *tv_get_string_buf_chk(const typval_T *const tv, char *const buf)
} }
return ""; return "";
} }
case VAR_BOOL: {
STRCPY(buf, encode_bool_var_names[tv->vval.v_bool]);
return buf;
}
case VAR_SPECIAL: { case VAR_SPECIAL: {
STRCPY(buf, encode_special_var_names[tv->vval.v_special]); STRCPY(buf, encode_special_var_names[tv->vval.v_special]);
return buf; return buf;

View File

@@ -91,10 +91,14 @@ typedef struct dict_watcher {
bool busy; // prevent recursion if the dict is changed in the callback bool busy; // prevent recursion if the dict is changed in the callback
} DictWatcher; } DictWatcher;
/// Bool variable values
typedef enum {
kBoolVarFalse, ///< v:false
kBoolVarTrue, ///< v:true
} BoolVarValue;
/// Special variable values /// Special variable values
typedef enum { typedef enum {
kSpecialVarFalse, ///< v:false
kSpecialVarTrue, ///< v:true
kSpecialVarNull, ///< v:null kSpecialVarNull, ///< v:null
} SpecialVarValue; } SpecialVarValue;
@@ -114,6 +118,7 @@ typedef enum {
VAR_LIST, ///< List, .v_list is used. VAR_LIST, ///< List, .v_list is used.
VAR_DICT, ///< Dictionary, .v_dict is used. VAR_DICT, ///< Dictionary, .v_dict is used.
VAR_FLOAT, ///< Floating-point value, .v_float is used. VAR_FLOAT, ///< Floating-point value, .v_float is used.
VAR_BOOL, ///< true, false
VAR_SPECIAL, ///< Special value (true, false, null), .v_special VAR_SPECIAL, ///< Special value (true, false, null), .v_special
///< is used. ///< is used.
VAR_PARTIAL, ///< Partial, .v_partial is used. VAR_PARTIAL, ///< Partial, .v_partial is used.
@@ -125,6 +130,7 @@ typedef struct {
VarLockStatus v_lock; ///< Variable lock status. VarLockStatus v_lock; ///< Variable lock status.
union typval_vval_union { union typval_vval_union {
varnumber_T v_number; ///< Number, for VAR_NUMBER. varnumber_T v_number; ///< Number, for VAR_NUMBER.
BoolVarValue v_bool; ///< Bool value, for VAR_BOOL
SpecialVarValue v_special; ///< Special value, for VAR_SPECIAL. SpecialVarValue v_special; ///< Special value, for VAR_SPECIAL.
float_T v_float; ///< Floating-point number, for VAR_FLOAT. float_T v_float; ///< Floating-point number, for VAR_FLOAT.
char_u *v_string; ///< String, for VAR_STRING and VAR_FUNC, can be NULL. char_u *v_string; ///< String, for VAR_STRING and VAR_FUNC, can be NULL.

View File

@@ -379,17 +379,22 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, _mp_last(*mpstack)); TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, _mp_last(*mpstack));
break; break;
} }
case VAR_BOOL: {
switch (tv->vval.v_bool) {
case kBoolVarTrue:
case kBoolVarFalse: {
TYPVAL_ENCODE_CONV_BOOL(tv, tv->vval.v_bool == kBoolVarTrue);
break;
}
}
break;
}
case VAR_SPECIAL: { case VAR_SPECIAL: {
switch (tv->vval.v_special) { switch (tv->vval.v_special) {
case kSpecialVarNull: { case kSpecialVarNull: {
TYPVAL_ENCODE_CONV_NIL(tv); // -V1037 TYPVAL_ENCODE_CONV_NIL(tv); // -V1037
break; break;
} }
case kSpecialVarTrue:
case kSpecialVarFalse: {
TYPVAL_ENCODE_CONV_BOOL(tv, tv->vval.v_special == kSpecialVarTrue);
break;
}
} }
break; break;
} }

View File

@@ -432,8 +432,8 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
tv_dict_add_nr(dict, S_LEN("cmdlevel"), ccline.level); tv_dict_add_nr(dict, S_LEN("cmdlevel"), ccline.level);
tv_dict_set_keys_readonly(dict); tv_dict_set_keys_readonly(dict);
// not readonly: // not readonly:
tv_dict_add_special(dict, S_LEN("abort"), tv_dict_add_bool(dict, S_LEN("abort"),
s->gotesc ? kSpecialVarTrue : kSpecialVarFalse); s->gotesc ? kBoolVarTrue : kBoolVarFalse);
try_enter(&tstate); try_enter(&tstate);
apply_autocmds(EVENT_CMDLINELEAVE, (char_u *)firstcbuf, (char_u *)firstcbuf, apply_autocmds(EVENT_CMDLINELEAVE, (char_u *)firstcbuf, (char_u *)firstcbuf,
false, curbuf); false, curbuf);

View File

@@ -286,10 +286,10 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
break; break;
} }
case LUA_TBOOLEAN: { case LUA_TBOOLEAN: {
cur.tv->v_type = VAR_SPECIAL; cur.tv->v_type = VAR_BOOL;
cur.tv->vval.v_special = (lua_toboolean(lstate, -1) cur.tv->vval.v_bool = (lua_toboolean(lstate, -1)
? kSpecialVarTrue ? kBoolVarTrue
: kSpecialVarFalse); : kBoolVarFalse);
break; break;
} }
case LUA_TSTRING: { case LUA_TSTRING: {

View File

@@ -2740,8 +2740,8 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
tv_dict_add_str(dict, S_LEN("regname"), buf); tv_dict_add_str(dict, S_LEN("regname"), buf);
// Motion type: inclusive or exclusive. // Motion type: inclusive or exclusive.
tv_dict_add_special(dict, S_LEN("inclusive"), tv_dict_add_bool(dict, S_LEN("inclusive"),
oap->inclusive ? kSpecialVarTrue : kSpecialVarFalse); oap->inclusive ? kBoolVarTrue : kBoolVarFalse);
// Kind of operation: yank, delete, change). // Kind of operation: yank, delete, change).
buf[0] = (char)get_op_char(oap->op_type); buf[0] = (char)get_op_char(oap->op_type);
@@ -2749,8 +2749,8 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
tv_dict_add_str(dict, S_LEN("operator"), buf); tv_dict_add_str(dict, S_LEN("operator"), buf);
// Selection type: visual or not. // Selection type: visual or not.
tv_dict_add_special(dict, S_LEN("visual"), tv_dict_add_bool(dict, S_LEN("visual"),
oap->is_VIsual ? kSpecialVarTrue : kSpecialVarFalse); oap->is_VIsual ? kBoolVarTrue : kBoolVarFalse);
tv_dict_set_keys_readonly(dict); tv_dict_set_keys_readonly(dict);
textlock++; textlock++;

View File

@@ -923,6 +923,6 @@ void pum_set_event_info(dict_T *dict)
tv_dict_add_float(dict, S_LEN("row"), r); tv_dict_add_float(dict, S_LEN("row"), r);
tv_dict_add_float(dict, S_LEN("col"), c); tv_dict_add_float(dict, S_LEN("col"), c);
tv_dict_add_nr(dict, S_LEN("size"), pum_size); tv_dict_add_nr(dict, S_LEN("size"), pum_size);
tv_dict_add_special(dict, S_LEN("scrollbar"), tv_dict_add_bool(dict, S_LEN("scrollbar"),
pum_scrollbar ? kSpecialVarTrue : kSpecialVarFalse); pum_scrollbar ? kBoolVarTrue : kBoolVarFalse);
} }

View File

@@ -101,6 +101,7 @@ typedef enum {
#define VAR_TYPE_DICT 4 #define VAR_TYPE_DICT 4
#define VAR_TYPE_FLOAT 5 #define VAR_TYPE_FLOAT 5
#define VAR_TYPE_BOOL 6 #define VAR_TYPE_BOOL 6
#define VAR_TYPE_SPECIAL 7
// values for xp_context when doing command line completion // values for xp_context when doing command line completion

View File

@@ -31,10 +31,12 @@ for _, func in ipairs({'bufname(%s)', 'bufnr(%s)', 'bufwinnr(%s)',
it('errors out when receives v:true/v:false/v:null', function() it('errors out when receives v:true/v:false/v:null', function()
-- Not compatible with Vim: in Vim it always results in buffer not found -- Not compatible with Vim: in Vim it always results in buffer not found
-- without any error messages. -- without any error messages.
for _, var in ipairs({'v:true', 'v:false', 'v:null'}) do for _, var in ipairs({'v:true', 'v:false'}) do
eq('Vim(call):E5300: Expected a Number or a String', eq('Vim(call):E5299: Expected a Number or a String, Boolean found',
exc_exec('call ' .. func:format(var))) exc_exec('call ' .. func:format(var)))
end end
eq('Vim(call):E5300: Expected a Number or a String',
exc_exec('call ' .. func:format('v:null')))
end) end)
it('errors out when receives invalid argument', function() it('errors out when receives invalid argument', function()
eq('Vim(call):E745: Expected a Number or a String, List found', eq('Vim(call):E745: Expected a Number or a String, List found',

View File

@@ -14,7 +14,7 @@ before_each(clear)
describe('sort()', function() describe('sort()', function()
it('errors out when sorting special values', function() it('errors out when sorting special values', function()
eq('Vim(call):E907: Using a special value as a Float', eq('Vim(call):E362: Using a boolean value as a Float',
exc_exec('call sort([v:true, v:false], "f")')) exc_exec('call sort([v:true, v:false], "f")'))
end) end)
@@ -30,6 +30,7 @@ describe('sort()', function()
errors[err] = true errors[err] = true
end end
eq({ eq({
['E362: Using a boolean value as a Float']=true,
['E891: Using a Funcref as a Float']=true, ['E891: Using a Funcref as a Float']=true,
['E892: Using a String as a Float']=true, ['E892: Using a String as a Float']=true,
['E893: Using a List as a Float']=true, ['E893: Using a List as a Float']=true,

View File

@@ -11,7 +11,7 @@ before_each(clear)
describe('uniq()', function() describe('uniq()', function()
it('errors out when processing special values', function() it('errors out when processing special values', function()
eq('Vim(call):E907: Using a special value as a Float', eq('Vim(call):E362: Using a boolean value as a Float',
exc_exec('call uniq([v:true, v:false], "f")')) exc_exec('call uniq([v:true, v:false], "f")'))
end) end)

View File

@@ -136,11 +136,15 @@ local function typvalt2lua_tab_init()
return return
end end
typvalt2lua_tab = { typvalt2lua_tab = {
[tonumber(eval.VAR_BOOL)] = function(t)
return ({
[tonumber(eval.kBoolVarFalse)] = false,
[tonumber(eval.kBoolVarTrue)] = true,
})[tonumber(t.vval.v_bool)]
end,
[tonumber(eval.VAR_SPECIAL)] = function(t) [tonumber(eval.VAR_SPECIAL)] = function(t)
return ({ return ({
[tonumber(eval.kSpecialVarFalse)] = false,
[tonumber(eval.kSpecialVarNull)] = nil_value, [tonumber(eval.kSpecialVarNull)] = nil_value,
[tonumber(eval.kSpecialVarTrue)] = true,
})[tonumber(t.vval.v_special)] })[tonumber(t.vval.v_special)]
end, end,
[tonumber(eval.VAR_NUMBER)] = function(t) [tonumber(eval.VAR_NUMBER)] = function(t)
@@ -349,8 +353,8 @@ lua2typvalt = function(l, processed)
[null_list] = {'VAR_LIST', {v_list=ffi.cast('list_T*', nil)}}, [null_list] = {'VAR_LIST', {v_list=ffi.cast('list_T*', nil)}},
[null_dict] = {'VAR_DICT', {v_dict=ffi.cast('dict_T*', nil)}}, [null_dict] = {'VAR_DICT', {v_dict=ffi.cast('dict_T*', nil)}},
[nil_value] = {'VAR_SPECIAL', {v_special=eval.kSpecialVarNull}}, [nil_value] = {'VAR_SPECIAL', {v_special=eval.kSpecialVarNull}},
[true] = {'VAR_SPECIAL', {v_special=eval.kSpecialVarTrue}}, [true] = {'VAR_BOOL', {v_bool=eval.kBoolVarTrue}},
[false] = {'VAR_SPECIAL', {v_special=eval.kSpecialVarFalse}}, [false] = {'VAR_BOOL', {v_bool=eval.kBoolVarFalse}},
} }
for k, v in pairs(special_vals) do for k, v in pairs(special_vals) do

View File

@@ -48,8 +48,7 @@ local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h',
local function vimconv_alloc() local function vimconv_alloc()
return ffi.gc( return ffi.gc(
ffi.cast('vimconv_T*', lib.xcalloc(1, ffi.sizeof('vimconv_T'))), ffi.cast('vimconv_T*', lib.xcalloc(1, ffi.sizeof('vimconv_T'))), function(vc)
function(vc)
lib.convert_setup(vc, nil, nil) lib.convert_setup(vc, nil, nil)
lib.xfree(vc) lib.xfree(vc)
end) end)
@@ -2838,6 +2837,7 @@ describe('typval.c', function()
{lib.VAR_FUNC, 'E729: using Funcref as a String'}, {lib.VAR_FUNC, 'E729: using Funcref as a String'},
{lib.VAR_LIST, 'E730: using List as a String'}, {lib.VAR_LIST, 'E730: using List as a String'},
{lib.VAR_DICT, 'E731: using Dictionary as a String'}, {lib.VAR_DICT, 'E731: using Dictionary as a String'},
{lib.VAR_BOOL, nil},
{lib.VAR_SPECIAL, nil}, {lib.VAR_SPECIAL, nil},
{lib.VAR_UNKNOWN, 'E908: using an invalid value as a String'}, {lib.VAR_UNKNOWN, 'E908: using an invalid value as a String'},
}) do }) do
@@ -2868,8 +2868,8 @@ describe('typval.c', function()
{lib.VAR_LIST, {v_list=NULL}, 'E745: Using a List as a Number', 0}, {lib.VAR_LIST, {v_list=NULL}, 'E745: Using a List as a Number', 0},
{lib.VAR_DICT, {v_dict=NULL}, 'E728: Using a Dictionary as a Number', 0}, {lib.VAR_DICT, {v_dict=NULL}, 'E728: Using a Dictionary as a Number', 0},
{lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 0}, {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 0},
{lib.VAR_SPECIAL, {v_special=lib.kSpecialVarTrue}, nil, 1}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 1},
{lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, nil, 0}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 0},
{lib.VAR_UNKNOWN, nil, 'E685: Internal error: tv_get_number(UNKNOWN)', 0}, {lib.VAR_UNKNOWN, nil, 'E685: Internal error: tv_get_number(UNKNOWN)', 0},
}) do }) do
-- Using to_cstr, cannot free with tv_clear -- Using to_cstr, cannot free with tv_clear
@@ -2897,8 +2897,8 @@ describe('typval.c', function()
{lib.VAR_LIST, {v_list=NULL}, 'E745: Using a List as a Number', 0}, {lib.VAR_LIST, {v_list=NULL}, 'E745: Using a List as a Number', 0},
{lib.VAR_DICT, {v_dict=NULL}, 'E728: Using a Dictionary as a Number', 0}, {lib.VAR_DICT, {v_dict=NULL}, 'E728: Using a Dictionary as a Number', 0},
{lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 0}, {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 0},
{lib.VAR_SPECIAL, {v_special=lib.kSpecialVarTrue}, nil, 1}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 1},
{lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, nil, 0}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 0},
{lib.VAR_UNKNOWN, nil, 'E685: Internal error: tv_get_number(UNKNOWN)', 0}, {lib.VAR_UNKNOWN, nil, 'E685: Internal error: tv_get_number(UNKNOWN)', 0},
}) do }) do
-- Using to_cstr, cannot free with tv_clear -- Using to_cstr, cannot free with tv_clear
@@ -2931,8 +2931,8 @@ describe('typval.c', function()
{lib.VAR_LIST, {v_list=NULL}, 'E745: Using a List as a Number', -1}, {lib.VAR_LIST, {v_list=NULL}, 'E745: Using a List as a Number', -1},
{lib.VAR_DICT, {v_dict=NULL}, 'E728: Using a Dictionary as a Number', -1}, {lib.VAR_DICT, {v_dict=NULL}, 'E728: Using a Dictionary as a Number', -1},
{lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 0}, {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 0},
{lib.VAR_SPECIAL, {v_special=lib.kSpecialVarTrue}, nil, 1}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 1},
{lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, nil, 0}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 0},
{lib.VAR_UNKNOWN, nil, 'E685: Internal error: tv_get_number(UNKNOWN)', -1}, {lib.VAR_UNKNOWN, nil, 'E685: Internal error: tv_get_number(UNKNOWN)', -1},
}) do }) do
lib.curwin.w_cursor.lnum = 46 lib.curwin.w_cursor.lnum = 46
@@ -2961,8 +2961,8 @@ describe('typval.c', function()
{lib.VAR_LIST, {v_list=NULL}, 'E893: Using a List as a Float', 0}, {lib.VAR_LIST, {v_list=NULL}, 'E893: Using a List as a Float', 0},
{lib.VAR_DICT, {v_dict=NULL}, 'E894: Using a Dictionary as a Float', 0}, {lib.VAR_DICT, {v_dict=NULL}, 'E894: Using a Dictionary as a Float', 0},
{lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, 'E907: Using a special value as a Float', 0}, {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, 'E907: Using a special value as a Float', 0},
{lib.VAR_SPECIAL, {v_special=lib.kSpecialVarTrue}, 'E907: Using a special value as a Float', 0}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, 'E362: Using a boolean value as a Float', 0},
{lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, 'E907: Using a special value as a Float', 0}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, 'E362: Using a boolean value as a Float', 0},
{lib.VAR_UNKNOWN, nil, 'E685: Internal error: tv_get_float(UNKNOWN)', 0}, {lib.VAR_UNKNOWN, nil, 'E685: Internal error: tv_get_float(UNKNOWN)', 0},
}) do }) do
-- Using to_cstr, cannot free with tv_clear -- Using to_cstr, cannot free with tv_clear
@@ -2993,8 +2993,8 @@ describe('typval.c', function()
{lib.VAR_LIST, {v_list=NULL}, 'E730: using List as a String', ''}, {lib.VAR_LIST, {v_list=NULL}, 'E730: using List as a String', ''},
{lib.VAR_DICT, {v_dict=NULL}, 'E731: using Dictionary as a String', ''}, {lib.VAR_DICT, {v_dict=NULL}, 'E731: using Dictionary as a String', ''},
{lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'null'}, {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'null'},
{lib.VAR_SPECIAL, {v_special=lib.kSpecialVarTrue}, nil, 'true'}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 'true'},
{lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, nil, 'false'}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 'false'},
{lib.VAR_UNKNOWN, nil, 'E908: using an invalid value as a String', ''}, {lib.VAR_UNKNOWN, nil, 'E908: using an invalid value as a String', ''},
}) do }) do
-- Using to_cstr in place of Neovim allocated string, cannot -- Using to_cstr in place of Neovim allocated string, cannot
@@ -3005,7 +3005,8 @@ describe('typval.c', function()
local ret = v[4] local ret = v[4]
eq(ret, check_emsg(function() eq(ret, check_emsg(function()
local res = lib.tv_get_string(tv) local res = lib.tv_get_string(tv)
if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_SPECIAL then if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_SPECIAL
or tv.v_type == lib.VAR_BOOL then
eq(buf, res) eq(buf, res)
else else
neq(buf, res) neq(buf, res)
@@ -3036,8 +3037,8 @@ describe('typval.c', function()
{lib.VAR_LIST, {v_list=NULL}, 'E730: using List as a String', nil}, {lib.VAR_LIST, {v_list=NULL}, 'E730: using List as a String', nil},
{lib.VAR_DICT, {v_dict=NULL}, 'E731: using Dictionary as a String', nil}, {lib.VAR_DICT, {v_dict=NULL}, 'E731: using Dictionary as a String', nil},
{lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'null'}, {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'null'},
{lib.VAR_SPECIAL, {v_special=lib.kSpecialVarTrue}, nil, 'true'}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 'true'},
{lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, nil, 'false'}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 'false'},
{lib.VAR_UNKNOWN, nil, 'E908: using an invalid value as a String', nil}, {lib.VAR_UNKNOWN, nil, 'E908: using an invalid value as a String', nil},
}) do }) do
-- Using to_cstr, cannot free with tv_clear -- Using to_cstr, cannot free with tv_clear
@@ -3047,7 +3048,8 @@ describe('typval.c', function()
local ret = v[4] local ret = v[4]
eq(ret, check_emsg(function() eq(ret, check_emsg(function()
local res = lib.tv_get_string_chk(tv) local res = lib.tv_get_string_chk(tv)
if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_SPECIAL then if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_SPECIAL
or tv.v_type == lib.VAR_BOOL then
eq(buf, res) eq(buf, res)
else else
neq(buf, res) neq(buf, res)
@@ -3077,8 +3079,8 @@ describe('typval.c', function()
{lib.VAR_LIST, {v_list=NULL}, 'E730: using List as a String', ''}, {lib.VAR_LIST, {v_list=NULL}, 'E730: using List as a String', ''},
{lib.VAR_DICT, {v_dict=NULL}, 'E731: using Dictionary as a String', ''}, {lib.VAR_DICT, {v_dict=NULL}, 'E731: using Dictionary as a String', ''},
{lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'null'}, {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'null'},
{lib.VAR_SPECIAL, {v_special=lib.kSpecialVarTrue}, nil, 'true'}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 'true'},
{lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, nil, 'false'}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 'false'},
{lib.VAR_UNKNOWN, nil, 'E908: using an invalid value as a String', ''}, {lib.VAR_UNKNOWN, nil, 'E908: using an invalid value as a String', ''},
}) do }) do
-- Using to_cstr, cannot free with tv_clear -- Using to_cstr, cannot free with tv_clear
@@ -3089,7 +3091,8 @@ describe('typval.c', function()
eq(ret, check_emsg(function() eq(ret, check_emsg(function()
local buf = ffi.new('char[?]', lib.NUMBUFLEN, {0}) local buf = ffi.new('char[?]', lib.NUMBUFLEN, {0})
local res = lib.tv_get_string_buf(tv, buf) local res = lib.tv_get_string_buf(tv, buf)
if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_SPECIAL then if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_SPECIAL
or tv.v_type == lib.VAR_BOOL then
eq(buf, res) eq(buf, res)
else else
neq(buf, res) neq(buf, res)
@@ -3119,8 +3122,8 @@ describe('typval.c', function()
{lib.VAR_LIST, {v_list=NULL}, 'E730: using List as a String', nil}, {lib.VAR_LIST, {v_list=NULL}, 'E730: using List as a String', nil},
{lib.VAR_DICT, {v_dict=NULL}, 'E731: using Dictionary as a String', nil}, {lib.VAR_DICT, {v_dict=NULL}, 'E731: using Dictionary as a String', nil},
{lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'null'}, {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'null'},
{lib.VAR_SPECIAL, {v_special=lib.kSpecialVarTrue}, nil, 'true'}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 'true'},
{lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, nil, 'false'}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 'false'},
{lib.VAR_UNKNOWN, nil, 'E908: using an invalid value as a String', nil}, {lib.VAR_UNKNOWN, nil, 'E908: using an invalid value as a String', nil},
}) do }) do
-- Using to_cstr, cannot free with tv_clear -- Using to_cstr, cannot free with tv_clear
@@ -3131,7 +3134,8 @@ describe('typval.c', function()
eq(ret, check_emsg(function() eq(ret, check_emsg(function()
local buf = ffi.new('char[?]', lib.NUMBUFLEN, {0}) local buf = ffi.new('char[?]', lib.NUMBUFLEN, {0})
local res = lib.tv_get_string_buf_chk(tv, buf) local res = lib.tv_get_string_buf_chk(tv, buf)
if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_SPECIAL then if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_SPECIAL
or tv.v_type == lib.VAR_BOOL then
eq(buf, res) eq(buf, res)
else else
neq(buf, res) neq(buf, res)