diff options
author | James McCoy <jamessan@jamessan.com> | 2017-01-06 21:11:33 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-01-06 21:11:33 -0500 |
commit | b4c0c61f5caa22962ba94981dece4be8bf8a2c26 (patch) | |
tree | 6cd369e366182d25388377136e04370846573473 /src/nvim/eval/typval_encode.c.h | |
parent | 1fee9a7151b30435947475fb410029397725f2ac (diff) | |
parent | dd48d7b94b8a0f5b4612a3821330d508d5b4241c (diff) | |
download | rneovim-b4c0c61f5caa22962ba94981dece4be8bf8a2c26.tar.gz rneovim-b4c0c61f5caa22962ba94981dece4be8bf8a2c26.tar.bz2 rneovim-b4c0c61f5caa22962ba94981dece4be8bf8a2c26.zip |
Merge pull request #5826 from ZyX-I/fix-typval_encode
Refactor eval/typval_encode.h
Diffstat (limited to 'src/nvim/eval/typval_encode.c.h')
-rw-r--r-- | src/nvim/eval/typval_encode.c.h | 776 |
1 files changed, 776 insertions, 0 deletions
diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h new file mode 100644 index 0000000000..3e1170b8fa --- /dev/null +++ b/src/nvim/eval/typval_encode.c.h @@ -0,0 +1,776 @@ +/// @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`. +/// +/// @param tv Pointer to typval where value is stored. May not be NULL. May +/// point to special dictionary. + +/// @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 tv Pointer to typval where value is stored. May not be NULL. May +/// point to a special dictionary. +/// @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 tv Pointer to typval where value is stored. May not be NULL. May +/// point to a special dictionary. +/// @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 tv Pointer to typval where value is stored. May not be NULL. Points +/// to a special dictionary. +/// @param num Integer to convert, must accept uint64_t. + +/// @def TYPVAL_ENCODE_CONV_FLOAT +/// @brief Macros used to convert floating-point number +/// +/// @param tv Pointer to typval where value is stored. May not be NULL. May +/// point to a special dictionary. +/// @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 tv Pointer to typval where value is stored. May not be NULL. May +/// point to a 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. +/// +/// @param tv Pointer to typval where value is stored. May be NULL. May +/// point to a special dictionary. +/// @param buf String to convert. Is a char[] buffer, not NUL-terminated. +/// @param len String length. + +/// @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 tv Pointer to typval where value is stored. May not be NULL. Points +/// to a special dictionary. +/// @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_START +/// @brief Macros used when starting to convert a funcref or a partial +/// +/// @param tv Pointer to typval where value is stored. May not be NULL. +/// @param fun Function name. May be NULL. + +/// @def TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS +/// @brief Macros used before starting to convert partial arguments +/// +/// @param tv Pointer to typval where value is stored. May not be NULL. +/// @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 tv Pointer to typval where value is stored. May not be NULL. +/// @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 +/// +/// @param tv Pointer to typval where value is stored. May not be NULL. + +/// @def TYPVAL_ENCODE_CONV_EMPTY_LIST +/// @brief Macros used to convert an empty list +/// +/// @param tv Pointer to typval where value is stored. May not be NULL. + +/// @def TYPVAL_ENCODE_CONV_EMPTY_DICT +/// @brief Macros used to convert an empty dictionary +/// +/// @param tv Pointer to typval where value is stored. May be NULL. May +/// point to a special dictionary. +/// @param dict Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR +/// (for dictionaries represented as special lists). + +/// @def TYPVAL_ENCODE_CONV_LIST_START +/// @brief Macros used before starting to convert non-empty list +/// +/// @param tv Pointer to typval where value is stored. May be NULL. May +/// point to a special dictionary. +/// @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 +/// +/// @param tv Pointer to typval where list is stored. May be NULL. + +/// @def TYPVAL_ENCODE_CONV_LIST_END +/// @brief Macros used after converting non-empty list +/// +/// @param tv Pointer to typval where list is stored. May be NULL. + +/// @def TYPVAL_ENCODE_CONV_DICT_START +/// @brief Macros used before starting to convert non-empty dictionary +/// +/// @param tv Pointer to typval where dictionary is stored. May be NULL. May +/// point to a special dictionary. +/// @param dict Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR +/// (for dictionaries represented as special lists). +/// @param len Dictionary length. Is an expression which evaluates to an +/// integer. + +/// @def TYPVAL_ENCODE_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 +/// +/// @param tv Pointer to typval where dictionary is stored. May be NULL. May +/// point to a special dictionary. +/// @param dict Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR +/// (for dictionaries represented as special lists). + +/// @def TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS +/// @brief Macros used after finishing converting non-last dictionary value +/// +/// @param tv Pointer to typval where dictionary is stored. May be NULL. May +/// point to a special dictionary. +/// @param dict Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR +/// (for dictionaries represented as special lists). + +/// @def TYPVAL_ENCODE_CONV_DICT_END +/// @brief Macros used after converting non-empty dictionary +/// +/// @param tv Pointer to typval where dictionary is stored. May be NULL. May +/// point to a special dictionary. +/// @param dict Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR +/// (for dictionaries represented as special lists). + +/// @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 functions `_typval_encode_{TYPVAL_ENCODE_NAME}_convert_one_value` and +/// `_typval_encode_{TYPVAL_ENCODE_NAME}_check_self_reference`. + +/// @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" + +/// Dummy variable used because some macros need lvalue +/// +/// Must not be written to, if needed one must check that address of the +/// macros argument is (not) equal to `&TYPVAL_ENCODE_NODICT_VAR`. +const dict_T *const TYPVAL_ENCODE_NODICT_VAR = NULL; + +static inline int _TYPVAL_ENCODE_CHECK_SELF_REFERENCE( + TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME, + void *const val, int *const val_copyID, + const MPConvStack *const mpstack, const int copyID, + const MPConvStackValType conv_type, + const char *const objname) + REAL_FATTR_NONNULL_ARG(2, 3, 4, 7) REAL_FATTR_WARN_UNUSED_RESULT + REAL_FATTR_ALWAYS_INLINE; + +/// Function for checking whether container references itself +/// +/// @param TYPVAL_ENCODE_FIRST_ARG_NAME First argument. +/// @param[in,out] val Container to check. +/// @param val_copyID Pointer to the container attribute that holds copyID. +/// After checking whether value of this attribute is +/// copyID (variable) it is set to copyID. +/// @param[in] mpstack Stack with values to convert. Read-only, used for error +/// reporting. +/// @param[in] copyID CopyID used by the caller. +/// @param[in] conv_type Type of the conversion, @see MPConvStackValType. +/// @param[in] objname Object name, used for error reporting. +/// +/// @return NOTDONE in case of success, what to return in case of failure. +static inline int _TYPVAL_ENCODE_CHECK_SELF_REFERENCE( + TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME, + void *const val, int *const val_copyID, + const MPConvStack *const mpstack, const int copyID, + const MPConvStackValType conv_type, + const char *const objname) +{ + if (*val_copyID == copyID) { + TYPVAL_ENCODE_CONV_RECURSE(val, conv_type); + return OK; + } + *val_copyID = copyID; + return NOTDONE; +} + +static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( + TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME, + MPConvStack *const mpstack, MPConvStackVal *const cur_mpsv, + typval_T *const tv, const int copyID, + const char *const objname) + REAL_FATTR_NONNULL_ARG(2, 4, 6) 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 mpstack Stack with values to convert. Values which are not +/// converted completely by this function (i.e. +/// non-scalars) are pushed here. +/// @param cur_mpsv Currently converted value from stack. +/// @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, MPConvStackVal *const cur_mpsv, + typval_T *const tv, const int copyID, + const char *const objname) +{ + switch (tv->v_type) { + case VAR_STRING: { + TYPVAL_ENCODE_CONV_STRING(tv, tv->vval.v_string, tv_strlen(tv)); + break; + } + case VAR_NUMBER: { + TYPVAL_ENCODE_CONV_NUMBER(tv, tv->vval.v_number); + break; + } + case VAR_FLOAT: { + TYPVAL_ENCODE_CONV_FLOAT(tv, tv->vval.v_float); + break; + } + case VAR_FUNC: { + TYPVAL_ENCODE_CONV_FUNC_START(tv, tv->vval.v_string); + TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, 0); + TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, -1); + TYPVAL_ENCODE_CONV_FUNC_END(tv); + break; + } + case VAR_PARTIAL: { + partial_T *const pt = tv->vval.v_partial; + (void)pt; + TYPVAL_ENCODE_CONV_FUNC_START(tv, (pt == NULL ? NULL : pt->pt_name)); + _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(tv); + break; + } + _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, copyID, + kMPConvList); + TYPVAL_ENCODE_CONV_LIST_START(tv, 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(tv); + break; + } + case kSpecialVarTrue: + case kSpecialVarFalse: { + TYPVAL_ENCODE_CONV_BOOL(tv, 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(tv, tv->vval.v_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(tv); + break; + } + case kMPBoolean: { + if (val_di->di_tv.v_type != VAR_NUMBER) { + goto _convert_one_value_regular_dict; + } + TYPVAL_ENCODE_CONV_BOOL(tv, 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(tv, number); + } else { + TYPVAL_ENCODE_CONV_NUMBER(tv, -number); + } + break; + } + case kMPFloat: { + if (val_di->di_tv.v_type != VAR_FLOAT) { + goto _convert_one_value_regular_dict; + } + TYPVAL_ENCODE_CONV_FLOAT(tv, 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(tv, buf, len); + } else { + TYPVAL_ENCODE_CONV_STRING(tv, buf, len); + } + xfree(buf); + break; + } + case kMPArray: { + if (val_di->di_tv.v_type != VAR_LIST) { + goto _convert_one_value_regular_dict; + } + _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list, + lv_copyID, copyID, + kMPConvList); + TYPVAL_ENCODE_CONV_LIST_START(tv, + 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(tv, TYPVAL_ENCODE_NODICT_VAR); + 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_DO_CHECK_SELF_REFERENCE(val_list, lv_copyID, copyID, + kMPConvPairs); + TYPVAL_ENCODE_CONV_DICT_START(tv, TYPVAL_ENCODE_NODICT_VAR, + 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(tv, buf, len, type); + xfree(buf); + break; + } + } + break; + } +_convert_one_value_regular_dict: + _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, copyID, + kMPConvDict); + TYPVAL_ENCODE_CONV_DICT_START(tv, tv->vval.v_dict, + tv->vval.v_dict->dv_hashtab.ht_used); + _mp_push(*mpstack, ((MPConvStackVal) { + .tv = tv, + .type = kMPConvDict, + .data = { + .d = { + .dict = tv->vval.v_dict, + .dictp = &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; + } + } +typval_encode_stop_converting_one_item: + return OK; + // Prevent “unused label” warnings. + goto typval_encode_stop_converting_one_item; +} + +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_NONNULL_ARG(2, 3) 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 top_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 top_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, + NULL, + top_tv, copyID, objname) + == FAIL) { + goto encode_vim_to__error_ret; + } +/// Label common for this and convert_one_value functions, used for escaping +/// from macros like TYPVAL_ENCODE_CONV_DICT_START. +typval_encode_stop_converting_one_item: + while (_mp_size(mpstack)) { + MPConvStackVal *cur_mpsv = &_mp_last(mpstack); + typval_T *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(cur_mpsv->tv, *cur_mpsv->data.d.dictp); + continue; + } else if (cur_mpsv->data.d.todo + != cur_mpsv->data.d.dict->dv_hashtab.ht_used) { + TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(cur_mpsv->tv, + *cur_mpsv->data.d.dictp); + } + 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(NULL, &di->di_key[0], + strlen((char *)&di->di_key[0])); + TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(cur_mpsv->tv, + *cur_mpsv->data.d.dictp); + 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(cur_mpsv->tv); + continue; + } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { + TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(cur_mpsv->tv); + } + 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(cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR); + continue; + } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { + TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS( + cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR); + } + const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list; + TYPVAL_ENCODE_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, cur_mpsv, + &kv_pair->lv_first->li_tv, + copyID, + objname) == FAIL) { + goto encode_vim_to__error_ret; + } + TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(cur_mpsv->tv, + TYPVAL_ENCODE_NODICT_VAR); + 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; + tv = cur_mpsv->tv; + switch (cur_mpsv->data.p.stage) { + case kMPConvPartialArgs: { + TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, + pt == NULL ? 0 : pt->pt_argc); + cur_mpsv->data.p.stage = kMPConvPartialSelf; + if (pt != NULL && pt->pt_argc > 0) { + TYPVAL_ENCODE_CONV_LIST_START(NULL, pt->pt_argc); + _mp_push(mpstack, ((MPConvStackVal) { + .type = kMPConvPartialList, + .tv = NULL, + .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 == NULL ? NULL : pt->pt_dict; + if (dict != NULL) { + TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, dict->dv_hashtab.ht_used); + if (dict->dv_hashtab.ht_used == 0) { + TYPVAL_ENCODE_CONV_EMPTY_DICT(NULL, pt->pt_dict); + continue; + } + const int te_csr_ret = _TYPVAL_ENCODE_CHECK_SELF_REFERENCE( + TYPVAL_ENCODE_FIRST_ARG_NAME, + dict, &dict->dv_copyID, &mpstack, copyID, kMPConvDict, + objname); + if (te_csr_ret != NOTDONE) { + if (te_csr_ret == FAIL) { + goto encode_vim_to__error_ret; + } else { + continue; + } + } + TYPVAL_ENCODE_CONV_DICT_START(NULL, pt->pt_dict, + dict->dv_hashtab.ht_used); + _mp_push(mpstack, ((MPConvStackVal) { + .type = kMPConvDict, + .tv = NULL, + .data = { + .d = { + .dict = dict, + .dictp = &pt->pt_dict, + .hi = dict->dv_hashtab.ht_array, + .todo = dict->dv_hashtab.ht_used, + }, + }, + })); + } else { + TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, -1); + } + break; + } + case kMPConvPartialEnd: { + TYPVAL_ENCODE_CONV_FUNC_END(tv); + (void)_mp_pop(mpstack); + break; + } + } + continue; + } + case kMPConvPartialList: { + if (!cur_mpsv->data.a.todo) { + (void)_mp_pop(mpstack); + TYPVAL_ENCODE_CONV_LIST_END(NULL); + continue; + } else if (cur_mpsv->data.a.argv != cur_mpsv->data.a.arg) { + TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(NULL); + } + tv = cur_mpsv->data.a.arg++; + cur_mpsv->data.a.todo--; + break; + } + } + assert(tv != NULL); + if (_TYPVAL_ENCODE_CONVERT_ONE_VALUE(TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack, + cur_mpsv, 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; + // Prevent “unused label” warnings. + goto typval_encode_stop_converting_one_item; +} +#endif // NVIM_EVAL_TYPVAL_ENCODE_C_H |