eval/typval_encode: Refactor big-big macros into .c.h file

This makes gdb backtraces much more meaningful: specifically I now know at which 
line it crashes in place of seeing that it crashes at 
TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS macros invocation.
This commit is contained in:
ZyX
2016-12-25 20:54:06 +03:00
parent c5c75513b8
commit b3163d06b3
5 changed files with 763 additions and 472 deletions

View File

@@ -0,0 +1,656 @@
/// @file eval/typval_encode.c.h
///
/// Contains set of macros used to convert (possibly recursive) typval_T into
/// something else. For these macros to work the following macros must be
/// defined:
/// @def TYPVAL_ENCODE_CONV_NIL
/// @brief Macros used to convert NIL value
///
/// Is called both for special dictionary (unless #TYPVAL_ENCODE_ALLOW_SPECIALS
/// is false) and `v:null`. Accepts no arguments, but still must be
/// a function-like macros.
/// @def TYPVAL_ENCODE_CONV_BOOL
/// @brief Macros used to convert boolean value
///
/// Is called both for special dictionary (unless #TYPVAL_ENCODE_ALLOW_SPECIALS
/// is false) and `v:true`/`v:false`.
///
/// @param num Boolean value to convert. Value is an expression which
/// evaluates to some integer.
/// @def TYPVAL_ENCODE_CONV_NUMBER
/// @brief Macros used to convert integer
///
/// @param num Integer to convert, must accept both varnumber_T and int64_t.
/// @def TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
/// @brief Macros used to convert unsigned integer
///
/// Not used if #TYPVAL_ENCODE_ALLOW_SPECIALS is false, but still must be
/// defined.
///
/// @param num Integer to convert, must accept uint64_t.
/// @def TYPVAL_ENCODE_CONV_FLOAT
/// @brief Macros used to convert floating-point number
///
/// @param flt Number to convert, must accept float_T.
/// @def TYPVAL_ENCODE_CONV_STRING
/// @brief Macros used to convert plain string
///
/// Is used to convert VAR_STRING objects as well as BIN strings represented as
/// special dictionary.
///
/// @param buf String to convert. Is a char[] buffer, not NUL-terminated.
/// @param len String length.
/// @def TYPVAL_ENCODE_CONV_STR_STRING
/// @brief Like #TYPVAL_ENCODE_CONV_STRING, but for STR strings
///
/// Is used to convert dictionary keys and STR strings represented as special
/// dictionaries.
/// @def TYPVAL_ENCODE_CONV_EXT_STRING
/// @brief Macros used to convert EXT string
///
/// Is used to convert EXT strings represented as special dictionaries. Never
/// actually used if #TYPVAL_ENCODE_ALLOW_SPECIALS is false, but still must be
/// defined.
///
/// @param buf String to convert. Is a char[] buffer, not NUL-terminated.
/// @param len String length.
/// @param type EXT type.
/// @def TYPVAL_ENCODE_CONV_FUNC
/// @brief Macros used to convert a function reference
///
/// @param fun Function name.
/// @def TYPVAL_ENCODE_CONV_FUNC_START
/// @brief Macros used when starting to convert a funcref or a partial
///
/// @param fun Function name.
/// @param is_partial True if converted function is a partial.
/// @param pt Pointer to partial or NULL.
/// @def TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
/// @brief Macros used before starting to convert partial arguments
///
/// @param len Number of arguments. Zero for absent arguments or when
/// converting a funcref.
/// @def TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
/// @brief Macros used before starting to convert self dictionary
///
/// @param len Number of arguments. May be zero for empty dictionary or -1 for
/// missing self dictionary, also when converting function
/// reference.
/// @def TYPVAL_ENCODE_CONV_FUNC_END
/// @brief Macros used after converting a funcref or a partial
///
/// Accepts no arguments, but still must be a function-like macros.
/// @def TYPVAL_ENCODE_CONV_EMPTY_LIST
/// @brief Macros used to convert an empty list
///
/// Accepts no arguments, but still must be a function-like macros.
/// @def TYPVAL_ENCODE_CONV_EMPTY_DICT
/// @brief Macros used to convert an empty dictionary
///
/// Accepts no arguments, but still must be a function-like macros.
/// @def TYPVAL_ENCODE_CONV_LIST_START
/// @brief Macros used before starting to convert non-empty list
///
/// @param len List length. Is an expression which evaluates to an integer.
/// @def TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
/// @brief Macros used after finishing converting non-last list item
///
/// Accepts no arguments, but still must be a function-like macros.
/// @def TYPVAL_ENCODE_CONV_LIST_END
/// @brief Macros used after converting non-empty list
///
/// Accepts no arguments, but still must be a function-like macros.
/// @def TYPVAL_ENCODE_CONV_DICT_START
/// @brief Macros used before starting to convert non-empty dictionary
///
/// @param len Dictionary length. Is an expression which evaluates to an
/// integer.
/// @def TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK
/// @brief Macros used to check special dictionary key
///
/// @param label Label for goto in case check was not successfull.
/// @param key typval_T key to check.
/// @def TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
/// @brief Macros used after finishing converting dictionary key
///
/// Accepts no arguments, but still must be a function-like macros.
/// @def TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
/// @brief Macros used after finishing converting non-last dictionary value
///
/// Accepts no arguments, but still must be a function-like macros.
/// @def TYPVAL_ENCODE_CONV_DICT_END
/// @brief Macros used after converting non-empty dictionary
///
/// Accepts no arguments, but still must be a function-like macros.
/// @def TYPVAL_ENCODE_CONV_RECURSE
/// @brief Macros used when self-containing container is detected
///
/// @param val Container for which this situation was detected.
/// @param conv_type Type of the stack entry, @see MPConvStackValType.
/// @def TYPVAL_ENCODE_ALLOW_SPECIALS
/// @brief Macros that specifies whether special dictionaries are special
///
/// Must be something that evaluates to boolean, most likely `true` or `false`.
/// If it is false then special dictionaries are not treated specially.
/// @def TYPVAL_ENCODE_SCOPE
/// @brief Scope of the main function: either nothing or `static`
/// @def TYPVAL_ENCODE_NAME
/// @brief Name of the target converter
///
/// After including this file it will define function
/// `encode_vim_to_{TYPVAL_ENCODE_NAME}` with scope #TYPVAL_ENCODE_SCOPE and
/// static function `_{TYPVAL_ENCODE_NAME}_convert_one_value`.
/// @def TYPVAL_ENCODE_FIRST_ARG_TYPE
/// @brief Type of the first argument, which will be used to return the results
///
/// Is expected to be a pointer type.
/// @def TYPVAL_ENCODE_FIRST_ARG_NAME
/// @brief Name of the first argument
///
/// This name will only be used by one of the above macros which are defined by
/// the caller. Functions defined here do not use first argument directly.
#ifndef NVIM_EVAL_TYPVAL_ENCODE_C_H
#define NVIM_EVAL_TYPVAL_ENCODE_C_H
#undef NVIM_EVAL_TYPVAL_ENCODE_C_H
#include <stddef.h>
#include <inttypes.h>
#include <assert.h>
#include "nvim/lib/kvec.h"
#include "nvim/eval_defs.h"
#include "nvim/eval/encode.h"
#include "nvim/func_attr.h"
#include "nvim/eval/typval_encode.h"
static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
MPConvStack *const mpstack, typval_T *const tv, const int copyID,
const char *const objname)
REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT;
/// Convert single value
///
/// Only scalar values are converted immediately, everything else is pushed onto
/// the stack.
///
/// @param TYPVAL_ENCODE_FIRST_ARG_NAME First argument, defined by the
/// includer. Only meaningful to macros
/// defined by the includer.
/// @param[out] mpstack Stack with values to convert. Values which are not
/// converted completely by this function (i.e.
/// non-scalars) are pushed here.
/// @param tv Converted value.
/// @param[in] copyID CopyID.
/// @param[in] objname Object name, used for error reporting.
///
/// @return OK in case of success, FAIL in case of failure.
static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
MPConvStack *const mpstack, typval_T *const tv, const int copyID,
const char *const objname)
{
switch (tv->v_type) {
case VAR_STRING: {
TYPVAL_ENCODE_CONV_STRING(tv->vval.v_string, tv_strlen(tv));
break;
}
case VAR_NUMBER: {
TYPVAL_ENCODE_CONV_NUMBER(tv->vval.v_number);
break;
}
case VAR_FLOAT: {
TYPVAL_ENCODE_CONV_FLOAT(tv->vval.v_float);
break;
}
case VAR_FUNC: {
TYPVAL_ENCODE_CONV_FUNC_START(tv->vval.v_string, false, NULL);
TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(0);
TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(-1);
TYPVAL_ENCODE_CONV_FUNC_END();
break;
}
case VAR_PARTIAL: {
partial_T *const pt = tv->vval.v_partial;
(void)pt;
TYPVAL_ENCODE_CONV_FUNC_START(pt->pt_name, true, pt);
_mp_push(*mpstack, ((MPConvStackVal) {
.type = kMPConvPartial,
.tv = tv,
.data = {
.p = {
.stage = kMPConvPartialArgs,
.pt = tv->vval.v_partial,
},
},
}));
break;
}
case VAR_LIST: {
if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) {
TYPVAL_ENCODE_CONV_EMPTY_LIST();
break;
}
_TYPVAL_ENCODE_CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, copyID,
kMPConvList);
TYPVAL_ENCODE_CONV_LIST_START(tv->vval.v_list->lv_len);
_mp_push(*mpstack, ((MPConvStackVal) {
.type = kMPConvList,
.tv = tv,
.data = {
.l = {
.list = tv->vval.v_list,
.li = tv->vval.v_list->lv_first,
},
},
}));
break;
}
case VAR_SPECIAL: {
switch (tv->vval.v_special) {
case kSpecialVarNull: {
TYPVAL_ENCODE_CONV_NIL();
break;
}
case kSpecialVarTrue:
case kSpecialVarFalse: {
TYPVAL_ENCODE_CONV_BOOL(tv->vval.v_special == kSpecialVarTrue);
break;
}
}
break;
}
case VAR_DICT: {
if (tv->vval.v_dict == NULL
|| tv->vval.v_dict->dv_hashtab.ht_used == 0) {
TYPVAL_ENCODE_CONV_EMPTY_DICT();
break;
}
const dictitem_T *type_di;
const dictitem_T *val_di;
if (TYPVAL_ENCODE_ALLOW_SPECIALS
&& tv->vval.v_dict->dv_hashtab.ht_used == 2
&& (type_di = dict_find((dict_T *) tv->vval.v_dict,
(char_u *) "_TYPE", -1)) != NULL
&& type_di->di_tv.v_type == VAR_LIST
&& (val_di = dict_find((dict_T *) tv->vval.v_dict,
(char_u *) "_VAL", -1)) != NULL) {
size_t i;
for (i = 0; i < ARRAY_SIZE(eval_msgpack_type_lists); i++) {
if (type_di->di_tv.vval.v_list == eval_msgpack_type_lists[i]) {
break;
}
}
if (i == ARRAY_SIZE(eval_msgpack_type_lists)) {
goto _convert_one_value_regular_dict;
}
switch ((MessagePackType) i) {
case kMPNil: {
TYPVAL_ENCODE_CONV_NIL();
break;
}
case kMPBoolean: {
if (val_di->di_tv.v_type != VAR_NUMBER) {
goto _convert_one_value_regular_dict;
}
TYPVAL_ENCODE_CONV_BOOL(val_di->di_tv.vval.v_number);
break;
}
case kMPInteger: {
const list_T *val_list;
varnumber_T sign;
varnumber_T highest_bits;
varnumber_T high_bits;
varnumber_T low_bits;
/* List of 4 integers; first is signed (should be 1 or -1, but */
/* this is not checked), second is unsigned and have at most */
/* one (sign is -1) or two (sign is 1) non-zero bits (number of */
/* bits is not checked), other unsigned and have at most 31 */
/* non-zero bits (number of bits is not checked).*/
if (val_di->di_tv.v_type != VAR_LIST
|| (val_list = val_di->di_tv.vval.v_list) == NULL
|| val_list->lv_len != 4
|| val_list->lv_first->li_tv.v_type != VAR_NUMBER
|| (sign = val_list->lv_first->li_tv.vval.v_number) == 0
|| val_list->lv_first->li_next->li_tv.v_type != VAR_NUMBER
|| (highest_bits =
val_list->lv_first->li_next->li_tv.vval.v_number) < 0
|| val_list->lv_last->li_prev->li_tv.v_type != VAR_NUMBER
|| (high_bits =
val_list->lv_last->li_prev->li_tv.vval.v_number) < 0
|| val_list->lv_last->li_tv.v_type != VAR_NUMBER
|| (low_bits = val_list->lv_last->li_tv.vval.v_number) < 0) {
goto _convert_one_value_regular_dict;
}
uint64_t number = ((uint64_t) (((uint64_t) highest_bits) << 62)
| (uint64_t) (((uint64_t) high_bits) << 31)
| (uint64_t) low_bits);
if (sign > 0) {
TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(number);
} else {
TYPVAL_ENCODE_CONV_NUMBER(-number);
}
break;
}
case kMPFloat: {
if (val_di->di_tv.v_type != VAR_FLOAT) {
goto _convert_one_value_regular_dict;
}
TYPVAL_ENCODE_CONV_FLOAT(val_di->di_tv.vval.v_float);
break;
}
case kMPString:
case kMPBinary: {
const bool is_string = ((MessagePackType) i == kMPString);
if (val_di->di_tv.v_type != VAR_LIST) {
goto _convert_one_value_regular_dict;
}
size_t len;
char *buf;
if (!encode_vim_list_to_buf(val_di->di_tv.vval.v_list, &len,
&buf)) {
goto _convert_one_value_regular_dict;
}
if (is_string) {
TYPVAL_ENCODE_CONV_STR_STRING(buf, len);
} else {
TYPVAL_ENCODE_CONV_STRING(buf, len);
}
xfree(buf);
break;
}
case kMPArray: {
if (val_di->di_tv.v_type != VAR_LIST) {
goto _convert_one_value_regular_dict;
}
_TYPVAL_ENCODE_CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list,
lv_copyID, copyID, kMPConvList);
TYPVAL_ENCODE_CONV_LIST_START(val_di->di_tv.vval.v_list->lv_len);
_mp_push(*mpstack, ((MPConvStackVal) {
.tv = tv,
.type = kMPConvList,
.data = {
.l = {
.list = val_di->di_tv.vval.v_list,
.li = val_di->di_tv.vval.v_list->lv_first,
},
},
}));
break;
}
case kMPMap: {
if (val_di->di_tv.v_type != VAR_LIST) {
goto _convert_one_value_regular_dict;
}
list_T *const val_list = val_di->di_tv.vval.v_list;
if (val_list == NULL || val_list->lv_len == 0) {
TYPVAL_ENCODE_CONV_EMPTY_DICT();
break;
}
for (const listitem_T *li = val_list->lv_first; li != NULL;
li = li->li_next) {
if (li->li_tv.v_type != VAR_LIST
|| li->li_tv.vval.v_list->lv_len != 2) {
goto _convert_one_value_regular_dict;
}
}
_TYPVAL_ENCODE_CHECK_SELF_REFERENCE(val_list, lv_copyID, copyID,
kMPConvPairs);
TYPVAL_ENCODE_CONV_DICT_START(val_list->lv_len);
_mp_push(*mpstack, ((MPConvStackVal) {
.tv = tv,
.type = kMPConvPairs,
.data = {
.l = {
.list = val_list,
.li = val_list->lv_first,
},
},
}));
break;
}
case kMPExt: {
const list_T *val_list;
varnumber_T type;
if (val_di->di_tv.v_type != VAR_LIST
|| (val_list = val_di->di_tv.vval.v_list) == NULL
|| val_list->lv_len != 2
|| (val_list->lv_first->li_tv.v_type != VAR_NUMBER)
|| (type = val_list->lv_first->li_tv.vval.v_number) > INT8_MAX
|| type < INT8_MIN
|| (val_list->lv_last->li_tv.v_type != VAR_LIST)) {
goto _convert_one_value_regular_dict;
}
size_t len;
char *buf;
if (!encode_vim_list_to_buf(val_list->lv_last->li_tv.vval.v_list,
&len, &buf)) {
goto _convert_one_value_regular_dict;
}
TYPVAL_ENCODE_CONV_EXT_STRING(buf, len, type);
xfree(buf);
break;
}
}
break;
}
_convert_one_value_regular_dict:
_TYPVAL_ENCODE_CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, copyID,
kMPConvDict);
TYPVAL_ENCODE_CONV_DICT_START(tv->vval.v_dict->dv_hashtab.ht_used);
_mp_push(*mpstack, ((MPConvStackVal) {
.tv = tv,
.type = kMPConvDict,
.data = {
.d = {
.dict = tv->vval.v_dict,
.hi = tv->vval.v_dict->dv_hashtab.ht_array,
.todo = tv->vval.v_dict->dv_hashtab.ht_used,
},
},
}));
break;
}
case VAR_UNKNOWN: {
EMSG2(_(e_intern2), STR(_TYPVAL_ENCODE_CONVERT_ONE_VALUE) "()");
return FAIL;
}
}
return OK;
}
TYPVAL_ENCODE_SCOPE int _TYPVAL_ENCODE_ENCODE(
TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
typval_T *const tv, const char *const objname)
REAL_FATTR_WARN_UNUSED_RESULT;
/// Convert the whole typval
///
/// @param TYPVAL_ENCODE_FIRST_ARG_NAME First argument, defined by the
/// includer. Only meaningful to macros
/// defined by the includer.
/// @param tv Converted value.
/// @param[in] objname Object name, used for error reporting.
///
/// @return OK in case of success, FAIL in case of failure.
TYPVAL_ENCODE_SCOPE int _TYPVAL_ENCODE_ENCODE(
TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
typval_T *const tv, const char *const objname)
{
const int copyID = get_copyID();
MPConvStack mpstack;
_mp_init(mpstack);
if (_TYPVAL_ENCODE_CONVERT_ONE_VALUE(TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack,
tv, copyID, objname)
== FAIL) {
goto encode_vim_to__error_ret;
}
while (_mp_size(mpstack)) {
MPConvStackVal *cur_mpsv = &_mp_last(mpstack);
typval_T *cur_tv = NULL;
switch (cur_mpsv->type) {
case kMPConvDict: {
if (!cur_mpsv->data.d.todo) {
(void) _mp_pop(mpstack);
cur_mpsv->data.d.dict->dv_copyID = copyID - 1;
TYPVAL_ENCODE_CONV_DICT_END();
continue;
} else if (cur_mpsv->data.d.todo
!= cur_mpsv->data.d.dict->dv_hashtab.ht_used) {
TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS();
}
while (HASHITEM_EMPTY(cur_mpsv->data.d.hi)) {
cur_mpsv->data.d.hi++;
}
dictitem_T *const di = HI2DI(cur_mpsv->data.d.hi);
cur_mpsv->data.d.todo--;
cur_mpsv->data.d.hi++;
TYPVAL_ENCODE_CONV_STR_STRING(&di->di_key[0],
strlen((char *) &di->di_key[0]));
TYPVAL_ENCODE_CONV_DICT_AFTER_KEY();
cur_tv = &di->di_tv;
break;
}
case kMPConvList: {
if (cur_mpsv->data.l.li == NULL) {
(void) _mp_pop(mpstack);
cur_mpsv->data.l.list->lv_copyID = copyID - 1;
TYPVAL_ENCODE_CONV_LIST_END();
continue;
} else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) {
TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS();
}
cur_tv = &cur_mpsv->data.l.li->li_tv;
cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next;
break;
}
case kMPConvPairs: {
if (cur_mpsv->data.l.li == NULL) {
(void) _mp_pop(mpstack);
cur_mpsv->data.l.list->lv_copyID = copyID - 1;
TYPVAL_ENCODE_CONV_DICT_END();
continue;
} else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) {
TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS();
}
const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list;
TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(
encode_vim_to__error_ret, kv_pair->lv_first->li_tv);
if (_TYPVAL_ENCODE_CONVERT_ONE_VALUE(
TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack,
&kv_pair->lv_first->li_tv, copyID, objname) == FAIL) {
goto encode_vim_to__error_ret;
}
TYPVAL_ENCODE_CONV_DICT_AFTER_KEY();
cur_tv = &kv_pair->lv_last->li_tv;
cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next;
break;
}
case kMPConvPartial: {
partial_T *const pt = cur_mpsv->data.p.pt;
switch (cur_mpsv->data.p.stage) {
case kMPConvPartialArgs: {
TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(pt->pt_argc);
cur_mpsv->data.p.stage = kMPConvPartialSelf;
if (pt->pt_argc > 0) {
TYPVAL_ENCODE_CONV_LIST_START(pt->pt_argc);
_mp_push(mpstack, ((MPConvStackVal) {
.type = kMPConvPartialList,
.tv = tv,
.data = {
.a = {
.arg = pt->pt_argv,
.argv = pt->pt_argv,
.todo = (size_t)pt->pt_argc,
},
},
}));
}
break;
}
case kMPConvPartialSelf: {
cur_mpsv->data.p.stage = kMPConvPartialEnd;
dict_T *const dict = pt->pt_dict;
if (dict != NULL) {
TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(dict->dv_hashtab.ht_used);
TYPVAL_ENCODE_CONV_DICT_START(dict->dv_hashtab.ht_used);
_mp_push(mpstack, ((MPConvStackVal) {
.type = kMPConvDict,
.tv = tv,
.data = {
.d = {
.dict = dict,
.hi = dict->dv_hashtab.ht_array,
.todo = dict->dv_hashtab.ht_used,
},
},
}));
} else {
TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(-1);
}
break;
}
case kMPConvPartialEnd: {
TYPVAL_ENCODE_CONV_FUNC_END();
(void) _mp_pop(mpstack);
break;
}
}
continue;
}
case kMPConvPartialList: {
if (!cur_mpsv->data.a.todo) {
(void) _mp_pop(mpstack);
TYPVAL_ENCODE_CONV_LIST_END();
continue;
} else if (cur_mpsv->data.a.argv != cur_mpsv->data.a.arg) {
TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS();
}
cur_tv = cur_mpsv->data.a.arg++;
cur_mpsv->data.a.todo--;
break;
}
}
assert(cur_tv != NULL);
if (_TYPVAL_ENCODE_CONVERT_ONE_VALUE(
TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack, cur_tv, copyID, objname)
== FAIL) {
goto encode_vim_to__error_ret;
}
}
_mp_destroy(mpstack);
return OK;
encode_vim_to__error_ret:
_mp_destroy(mpstack);
return FAIL;
}
#endif // NVIM_EVAL_TYPVAL_ENCODE_C_H