mirror of
https://github.com/neovim/neovim.git
synced 2025-09-25 12:38:33 +00:00
eval/encode: Move main macros from encode.c to typval_encode.h
This commit is contained in:
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include <msgpack.h>
|
#include <msgpack.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
#include <stddef.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@
|
|||||||
#include "nvim/ascii.h"
|
#include "nvim/ascii.h"
|
||||||
#include "nvim/vim.h" // For _()
|
#include "nvim/vim.h" // For _()
|
||||||
#include "nvim/lib/kvec.h"
|
#include "nvim/lib/kvec.h"
|
||||||
|
#include "nvim/eval/typval_encode.h"
|
||||||
|
|
||||||
#define ga_concat(a, b) ga_concat(a, (char_u *)b)
|
#define ga_concat(a, b) ga_concat(a, (char_u *)b)
|
||||||
#define utf_ptr2char(b) utf_ptr2char((char_u *)b)
|
#define utf_ptr2char(b) utf_ptr2char((char_u *)b)
|
||||||
@@ -32,29 +34,6 @@
|
|||||||
#define convert_setup(vcp, from, to) \
|
#define convert_setup(vcp, from, to) \
|
||||||
(convert_setup(vcp, (char_u *)from, (char_u *)to))
|
(convert_setup(vcp, (char_u *)from, (char_u *)to))
|
||||||
|
|
||||||
/// Structure representing current VimL to messagepack conversion state
|
|
||||||
typedef struct {
|
|
||||||
enum {
|
|
||||||
kMPConvDict, ///< Convert dict_T *dictionary.
|
|
||||||
kMPConvList, ///< Convert list_T *list.
|
|
||||||
kMPConvPairs, ///< Convert mapping represented as a list_T* of pairs.
|
|
||||||
} type;
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
dict_T *dict; ///< Currently converted dictionary.
|
|
||||||
hashitem_T *hi; ///< Currently converted dictionary item.
|
|
||||||
size_t todo; ///< Amount of items left to process.
|
|
||||||
} d; ///< State of dictionary conversion.
|
|
||||||
struct {
|
|
||||||
list_T *list; ///< Currently converted list.
|
|
||||||
listitem_T *li; ///< Currently converted list item.
|
|
||||||
} l; ///< State of list or generic mapping conversion.
|
|
||||||
} data; ///< Data to convert.
|
|
||||||
} MPConvStackVal;
|
|
||||||
|
|
||||||
/// Stack used to convert VimL values to messagepack.
|
|
||||||
typedef kvec_t(MPConvStackVal) MPConvStack;
|
|
||||||
|
|
||||||
const char *const encode_special_var_names[] = {
|
const char *const encode_special_var_names[] = {
|
||||||
[kSpecialVarNull] = "null",
|
[kSpecialVarNull] = "null",
|
||||||
[kSpecialVarTrue] = "true",
|
[kSpecialVarTrue] = "true",
|
||||||
@@ -275,368 +254,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
|
|||||||
: OK);
|
: OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Code for checking whether container references itself
|
#define TYPVAL_ENCODE_CONV_STRING(buf, len) \
|
||||||
///
|
|
||||||
/// @param[in,out] val Container to check.
|
|
||||||
/// @param copyID_attr Name of the container attribute that holds copyID.
|
|
||||||
/// After checking whether value of this attribute is
|
|
||||||
/// copyID (variable) it is set to copyID.
|
|
||||||
#define CHECK_SELF_REFERENCE(val, copyID_attr, conv_type) \
|
|
||||||
do { \
|
|
||||||
if ((val)->copyID_attr == copyID) { \
|
|
||||||
CONV_RECURSE((val), conv_type); \
|
|
||||||
} \
|
|
||||||
(val)->copyID_attr = copyID; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define TV_STRLEN(tv) \
|
|
||||||
(tv->vval.v_string == NULL ? 0 : STRLEN(tv->vval.v_string))
|
|
||||||
|
|
||||||
/// Define functions which convert VimL value to something else
|
|
||||||
///
|
|
||||||
/// Creates function `vim_to_{name}(firstargtype firstargname, typval_T *const
|
|
||||||
/// tv)` which returns OK or FAIL and helper functions.
|
|
||||||
///
|
|
||||||
/// @param firstargtype Type of the first argument. It will be used to return
|
|
||||||
/// the results.
|
|
||||||
/// @param firstargname Name of the first argument.
|
|
||||||
/// @param name Name of the target converter.
|
|
||||||
#define DEFINE_VIML_CONV_FUNCTIONS(scope, name, firstargtype, firstargname) \
|
|
||||||
static int name##_convert_one_value(firstargtype firstargname, \
|
|
||||||
MPConvStack *const mpstack, \
|
|
||||||
typval_T *const tv, \
|
|
||||||
const int copyID, \
|
|
||||||
const char *const objname) \
|
|
||||||
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \
|
|
||||||
{ \
|
|
||||||
switch (tv->v_type) { \
|
|
||||||
case VAR_STRING: { \
|
|
||||||
CONV_STRING(tv->vval.v_string, TV_STRLEN(tv)); \
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
case VAR_NUMBER: { \
|
|
||||||
CONV_NUMBER(tv->vval.v_number); \
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
case VAR_FLOAT: { \
|
|
||||||
CONV_FLOAT(tv->vval.v_float); \
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
case VAR_FUNC: { \
|
|
||||||
CONV_FUNC(tv->vval.v_string); \
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
case VAR_LIST: { \
|
|
||||||
if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) { \
|
|
||||||
CONV_EMPTY_LIST(); \
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, kMPConvList); \
|
|
||||||
CONV_LIST_START(tv->vval.v_list); \
|
|
||||||
kv_push(*mpstack, ((MPConvStackVal) { \
|
|
||||||
.type = kMPConvList, \
|
|
||||||
.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: { \
|
|
||||||
CONV_NIL(); \
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
case kSpecialVarTrue: \
|
|
||||||
case kSpecialVarFalse: { \
|
|
||||||
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) { \
|
|
||||||
CONV_EMPTY_DICT(); \
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
const dictitem_T *type_di; \
|
|
||||||
const dictitem_T *val_di; \
|
|
||||||
if (CONV_ALLOW_SPECIAL \
|
|
||||||
&& 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 name##_convert_one_value_regular_dict; \
|
|
||||||
} \
|
|
||||||
switch ((MessagePackType) i) { \
|
|
||||||
case kMPNil: { \
|
|
||||||
CONV_NIL(); \
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
case kMPBoolean: { \
|
|
||||||
if (val_di->di_tv.v_type != VAR_NUMBER) { \
|
|
||||||
goto name##_convert_one_value_regular_dict; \
|
|
||||||
} \
|
|
||||||
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 name##_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) { \
|
|
||||||
CONV_UNSIGNED_NUMBER(number); \
|
|
||||||
} else { \
|
|
||||||
CONV_NUMBER(-number); \
|
|
||||||
} \
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
case kMPFloat: { \
|
|
||||||
if (val_di->di_tv.v_type != VAR_FLOAT) { \
|
|
||||||
goto name##_convert_one_value_regular_dict; \
|
|
||||||
} \
|
|
||||||
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 name##_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 name##_convert_one_value_regular_dict; \
|
|
||||||
} \
|
|
||||||
if (is_string) { \
|
|
||||||
CONV_STR_STRING(buf, len); \
|
|
||||||
} else { \
|
|
||||||
CONV_STRING(buf, len); \
|
|
||||||
} \
|
|
||||||
xfree(buf); \
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
case kMPArray: { \
|
|
||||||
if (val_di->di_tv.v_type != VAR_LIST) { \
|
|
||||||
goto name##_convert_one_value_regular_dict; \
|
|
||||||
} \
|
|
||||||
CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list, lv_copyID, \
|
|
||||||
kMPConvList); \
|
|
||||||
CONV_LIST_START(val_di->di_tv.vval.v_list); \
|
|
||||||
kv_push(*mpstack, ((MPConvStackVal) { \
|
|
||||||
.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 name##_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) { \
|
|
||||||
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 name##_convert_one_value_regular_dict; \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
CHECK_SELF_REFERENCE(val_list, lv_copyID, kMPConvPairs); \
|
|
||||||
CONV_DICT_START(val_list->lv_len); \
|
|
||||||
kv_push(*mpstack, ((MPConvStackVal) { \
|
|
||||||
.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 name##_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 name##_convert_one_value_regular_dict; \
|
|
||||||
} \
|
|
||||||
CONV_EXT_STRING(buf, len, type); \
|
|
||||||
xfree(buf); \
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
name##_convert_one_value_regular_dict: \
|
|
||||||
CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, kMPConvDict); \
|
|
||||||
CONV_DICT_START(tv->vval.v_dict->dv_hashtab.ht_used); \
|
|
||||||
kv_push(*mpstack, ((MPConvStackVal) { \
|
|
||||||
.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), #name "_convert_one_value()"); \
|
|
||||||
return FAIL; \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
return OK; \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
scope int encode_vim_to_##name(firstargtype firstargname, typval_T *const tv, \
|
|
||||||
const char *const objname) \
|
|
||||||
FUNC_ATTR_WARN_UNUSED_RESULT \
|
|
||||||
{ \
|
|
||||||
const int copyID = get_copyID(); \
|
|
||||||
MPConvStack mpstack; \
|
|
||||||
kv_init(mpstack); \
|
|
||||||
if (name##_convert_one_value(firstargname, &mpstack, tv, copyID, objname) \
|
|
||||||
== FAIL) { \
|
|
||||||
goto encode_vim_to_##name##_error_ret; \
|
|
||||||
} \
|
|
||||||
while (kv_size(mpstack)) { \
|
|
||||||
MPConvStackVal *cur_mpsv = &kv_A(mpstack, kv_size(mpstack) - 1); \
|
|
||||||
typval_T *cur_tv = NULL; \
|
|
||||||
switch (cur_mpsv->type) { \
|
|
||||||
case kMPConvDict: { \
|
|
||||||
if (!cur_mpsv->data.d.todo) { \
|
|
||||||
(void) kv_pop(mpstack); \
|
|
||||||
cur_mpsv->data.d.dict->dv_copyID = copyID - 1; \
|
|
||||||
CONV_DICT_END(); \
|
|
||||||
continue; \
|
|
||||||
} else if (cur_mpsv->data.d.todo \
|
|
||||||
!= cur_mpsv->data.d.dict->dv_hashtab.ht_used) { \
|
|
||||||
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++; \
|
|
||||||
CONV_STR_STRING(&di->di_key[0], STRLEN(&di->di_key[0])); \
|
|
||||||
CONV_DICT_AFTER_KEY(); \
|
|
||||||
cur_tv = &di->di_tv; \
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
case kMPConvList: { \
|
|
||||||
if (cur_mpsv->data.l.li == NULL) { \
|
|
||||||
(void) kv_pop(mpstack); \
|
|
||||||
cur_mpsv->data.l.list->lv_copyID = copyID - 1; \
|
|
||||||
CONV_LIST_END(cur_mpsv->data.l.list); \
|
|
||||||
continue; \
|
|
||||||
} else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { \
|
|
||||||
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) kv_pop(mpstack); \
|
|
||||||
cur_mpsv->data.l.list->lv_copyID = copyID - 1; \
|
|
||||||
CONV_DICT_END(); \
|
|
||||||
continue; \
|
|
||||||
} else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { \
|
|
||||||
CONV_DICT_BETWEEN_ITEMS(); \
|
|
||||||
} \
|
|
||||||
const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list; \
|
|
||||||
CONV_SPECIAL_DICT_KEY_CHECK(name, kv_pair); \
|
|
||||||
if (name##_convert_one_value(firstargname, &mpstack, \
|
|
||||||
&kv_pair->lv_first->li_tv, copyID, \
|
|
||||||
objname) == FAIL) { \
|
|
||||||
goto encode_vim_to_##name##_error_ret; \
|
|
||||||
} \
|
|
||||||
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; \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
assert(cur_tv != NULL); \
|
|
||||||
if (name##_convert_one_value(firstargname, &mpstack, cur_tv, copyID, \
|
|
||||||
objname) == FAIL) { \
|
|
||||||
goto encode_vim_to_##name##_error_ret; \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
kv_destroy(mpstack); \
|
|
||||||
return OK; \
|
|
||||||
encode_vim_to_##name##_error_ret: \
|
|
||||||
kv_destroy(mpstack); \
|
|
||||||
return FAIL; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define CONV_STRING(buf, len) \
|
|
||||||
do { \
|
do { \
|
||||||
const char *const buf_ = (const char *) buf; \
|
const char *const buf_ = (const char *) buf; \
|
||||||
if (buf == NULL) { \
|
if (buf == NULL) { \
|
||||||
@@ -655,19 +273,19 @@ encode_vim_to_##name##_error_ret: \
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define CONV_STR_STRING(buf, len) \
|
#define TYPVAL_ENCODE_CONV_STR_STRING(buf, len) \
|
||||||
CONV_STRING(buf, len)
|
TYPVAL_ENCODE_CONV_STRING(buf, len)
|
||||||
|
|
||||||
#define CONV_EXT_STRING(buf, len, type)
|
#define TYPVAL_ENCODE_CONV_EXT_STRING(buf, len, type)
|
||||||
|
|
||||||
#define CONV_NUMBER(num) \
|
#define TYPVAL_ENCODE_CONV_NUMBER(num) \
|
||||||
do { \
|
do { \
|
||||||
char numbuf[NUMBUFLEN]; \
|
char numbuf[NUMBUFLEN]; \
|
||||||
vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRId64, (int64_t) (num)); \
|
vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRId64, (int64_t) (num)); \
|
||||||
ga_concat(gap, numbuf); \
|
ga_concat(gap, numbuf); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define CONV_FLOAT(flt) \
|
#define TYPVAL_ENCODE_CONV_FLOAT(flt) \
|
||||||
do { \
|
do { \
|
||||||
const float_T flt_ = (flt); \
|
const float_T flt_ = (flt); \
|
||||||
switch (fpclassify(flt_)) { \
|
switch (fpclassify(flt_)) { \
|
||||||
@@ -690,51 +308,51 @@ encode_vim_to_##name##_error_ret: \
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define CONV_FUNC(fun) \
|
#define TYPVAL_ENCODE_CONV_FUNC(fun) \
|
||||||
do { \
|
do { \
|
||||||
ga_concat(gap, "function("); \
|
ga_concat(gap, "function("); \
|
||||||
CONV_STRING(fun, STRLEN(fun)); \
|
TYPVAL_ENCODE_CONV_STRING(fun, STRLEN(fun)); \
|
||||||
ga_append(gap, ')'); \
|
ga_append(gap, ')'); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define CONV_EMPTY_LIST() \
|
#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
|
||||||
ga_concat(gap, "[]")
|
ga_concat(gap, "[]")
|
||||||
|
|
||||||
#define CONV_LIST_START(lst) \
|
#define TYPVAL_ENCODE_CONV_LIST_START(len) \
|
||||||
ga_append(gap, '[')
|
ga_append(gap, '[')
|
||||||
|
|
||||||
#define CONV_EMPTY_DICT() \
|
#define TYPVAL_ENCODE_CONV_EMPTY_DICT() \
|
||||||
ga_concat(gap, "{}")
|
ga_concat(gap, "{}")
|
||||||
|
|
||||||
#define CONV_NIL() \
|
#define TYPVAL_ENCODE_CONV_NIL() \
|
||||||
ga_concat(gap, "v:null")
|
ga_concat(gap, "v:null")
|
||||||
|
|
||||||
#define CONV_BOOL(num) \
|
#define TYPVAL_ENCODE_CONV_BOOL(num) \
|
||||||
ga_concat(gap, ((num)? "v:true": "v:false"))
|
ga_concat(gap, ((num)? "v:true": "v:false"))
|
||||||
|
|
||||||
#define CONV_UNSIGNED_NUMBER(num)
|
#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(num)
|
||||||
|
|
||||||
#define CONV_DICT_START(len) \
|
#define TYPVAL_ENCODE_CONV_DICT_START(len) \
|
||||||
ga_append(gap, '{')
|
ga_append(gap, '{')
|
||||||
|
|
||||||
#define CONV_DICT_END() \
|
#define TYPVAL_ENCODE_CONV_DICT_END() \
|
||||||
ga_append(gap, '}')
|
ga_append(gap, '}')
|
||||||
|
|
||||||
#define CONV_DICT_AFTER_KEY() \
|
#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY() \
|
||||||
ga_concat(gap, ": ")
|
ga_concat(gap, ": ")
|
||||||
|
|
||||||
#define CONV_DICT_BETWEEN_ITEMS() \
|
#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS() \
|
||||||
ga_concat(gap, ", ")
|
ga_concat(gap, ", ")
|
||||||
|
|
||||||
#define CONV_SPECIAL_DICT_KEY_CHECK(name, kv_pair)
|
#define TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(label, kv_pair)
|
||||||
|
|
||||||
#define CONV_LIST_END(lst) \
|
#define TYPVAL_ENCODE_CONV_LIST_END() \
|
||||||
ga_append(gap, ']')
|
ga_append(gap, ']')
|
||||||
|
|
||||||
#define CONV_LIST_BETWEEN_ITEMS() \
|
#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS() \
|
||||||
CONV_DICT_BETWEEN_ITEMS()
|
TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS()
|
||||||
|
|
||||||
#define CONV_RECURSE(val, conv_type) \
|
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
|
||||||
do { \
|
do { \
|
||||||
if (!did_echo_string_emsg) { \
|
if (!did_echo_string_emsg) { \
|
||||||
/* Only give this message once for a recursive call to avoid */ \
|
/* Only give this message once for a recursive call to avoid */ \
|
||||||
@@ -764,12 +382,12 @@ encode_vim_to_##name##_error_ret: \
|
|||||||
return OK; \
|
return OK; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define CONV_ALLOW_SPECIAL false
|
#define TYPVAL_ENCODE_ALLOW_SPECIALS false
|
||||||
|
|
||||||
DEFINE_VIML_CONV_FUNCTIONS(static, string, garray_T *const, gap)
|
TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, string, garray_T *const, gap)
|
||||||
|
|
||||||
#undef CONV_RECURSE
|
#undef TYPVAL_ENCODE_CONV_RECURSE
|
||||||
#define CONV_RECURSE(val, conv_type) \
|
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
|
||||||
do { \
|
do { \
|
||||||
char ebuf[NUMBUFLEN + 7]; \
|
char ebuf[NUMBUFLEN + 7]; \
|
||||||
size_t backref = 0; \
|
size_t backref = 0; \
|
||||||
@@ -796,10 +414,10 @@ DEFINE_VIML_CONV_FUNCTIONS(static, string, garray_T *const, gap)
|
|||||||
return OK; \
|
return OK; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
DEFINE_VIML_CONV_FUNCTIONS(, echo, garray_T *const, gap)
|
TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(, echo, garray_T *const, gap)
|
||||||
|
|
||||||
#undef CONV_RECURSE
|
#undef TYPVAL_ENCODE_CONV_RECURSE
|
||||||
#define CONV_RECURSE(val, conv_type) \
|
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
|
||||||
do { \
|
do { \
|
||||||
if (!did_echo_string_emsg) { \
|
if (!did_echo_string_emsg) { \
|
||||||
/* Only give this message once for a recursive call to avoid */ \
|
/* Only give this message once for a recursive call to avoid */ \
|
||||||
@@ -811,27 +429,27 @@ DEFINE_VIML_CONV_FUNCTIONS(, echo, garray_T *const, gap)
|
|||||||
return OK; \
|
return OK; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#undef CONV_ALLOW_SPECIAL
|
#undef TYPVAL_ENCODE_ALLOW_SPECIALS
|
||||||
#define CONV_ALLOW_SPECIAL true
|
#define TYPVAL_ENCODE_ALLOW_SPECIALS true
|
||||||
|
|
||||||
#undef CONV_NIL
|
#undef TYPVAL_ENCODE_CONV_NIL
|
||||||
#define CONV_NIL() \
|
#define TYPVAL_ENCODE_CONV_NIL() \
|
||||||
ga_concat(gap, "null")
|
ga_concat(gap, "null")
|
||||||
|
|
||||||
#undef CONV_BOOL
|
#undef TYPVAL_ENCODE_CONV_BOOL
|
||||||
#define CONV_BOOL(num) \
|
#define TYPVAL_ENCODE_CONV_BOOL(num) \
|
||||||
ga_concat(gap, ((num)? "true": "false"))
|
ga_concat(gap, ((num)? "true": "false"))
|
||||||
|
|
||||||
#undef CONV_UNSIGNED_NUMBER
|
#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
|
||||||
#define CONV_UNSIGNED_NUMBER(num) \
|
#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(num) \
|
||||||
do { \
|
do { \
|
||||||
char numbuf[NUMBUFLEN]; \
|
char numbuf[NUMBUFLEN]; \
|
||||||
vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRIu64, (num)); \
|
vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRIu64, (num)); \
|
||||||
ga_concat(gap, numbuf); \
|
ga_concat(gap, numbuf); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#undef CONV_FLOAT
|
#undef TYPVAL_ENCODE_CONV_FLOAT
|
||||||
#define CONV_FLOAT(flt) \
|
#define TYPVAL_ENCODE_CONV_FLOAT(flt) \
|
||||||
do { \
|
do { \
|
||||||
const float_T flt_ = (flt); \
|
const float_T flt_ = (flt); \
|
||||||
switch (fpclassify(flt_)) { \
|
switch (fpclassify(flt_)) { \
|
||||||
@@ -1019,24 +637,24 @@ static inline int convert_to_json_string(garray_T *const gap,
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef CONV_STRING
|
#undef TYPVAL_ENCODE_CONV_STRING
|
||||||
#define CONV_STRING(buf, len) \
|
#define TYPVAL_ENCODE_CONV_STRING(buf, len) \
|
||||||
do { \
|
do { \
|
||||||
if (convert_to_json_string(gap, (const char *) (buf), (len)) != OK) { \
|
if (convert_to_json_string(gap, (const char *) (buf), (len)) != OK) { \
|
||||||
return FAIL; \
|
return FAIL; \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#undef CONV_EXT_STRING
|
#undef TYPVAL_ENCODE_CONV_EXT_STRING
|
||||||
#define CONV_EXT_STRING(buf, len, type) \
|
#define TYPVAL_ENCODE_CONV_EXT_STRING(buf, len, type) \
|
||||||
do { \
|
do { \
|
||||||
xfree(buf); \
|
xfree(buf); \
|
||||||
EMSG(_("E474: Unable to convert EXT string to JSON")); \
|
EMSG(_("E474: Unable to convert EXT string to JSON")); \
|
||||||
return FAIL; \
|
return FAIL; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#undef CONV_FUNC
|
#undef TYPVAL_ENCODE_CONV_FUNC
|
||||||
#define CONV_FUNC(fun) \
|
#define TYPVAL_ENCODE_CONV_FUNC(fun) \
|
||||||
return conv_error(_("E474: Error while dumping %s, %s: " \
|
return conv_error(_("E474: Error while dumping %s, %s: " \
|
||||||
"attempt to dump function reference"), \
|
"attempt to dump function reference"), \
|
||||||
mpstack, objname)
|
mpstack, objname)
|
||||||
@@ -1080,38 +698,38 @@ static inline bool check_json_key(const typval_T *const tv)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef CONV_SPECIAL_DICT_KEY_CHECK
|
#undef TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK
|
||||||
#define CONV_SPECIAL_DICT_KEY_CHECK(name, kv_pair) \
|
#define TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(label, kv_pair) \
|
||||||
do { \
|
do { \
|
||||||
if (!check_json_key(&kv_pair->lv_first->li_tv)) { \
|
if (!check_json_key(&kv_pair->lv_first->li_tv)) { \
|
||||||
EMSG(_("E474: Invalid key in special dictionary")); \
|
EMSG(_("E474: Invalid key in special dictionary")); \
|
||||||
goto encode_vim_to_##name##_error_ret; \
|
goto label; \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
DEFINE_VIML_CONV_FUNCTIONS(static, json, garray_T *const, gap)
|
TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, json, garray_T *const, gap)
|
||||||
|
|
||||||
#undef CONV_STRING
|
#undef TYPVAL_ENCODE_CONV_STRING
|
||||||
#undef CONV_STR_STRING
|
#undef TYPVAL_ENCODE_CONV_STR_STRING
|
||||||
#undef CONV_EXT_STRING
|
#undef TYPVAL_ENCODE_CONV_EXT_STRING
|
||||||
#undef CONV_NUMBER
|
#undef TYPVAL_ENCODE_CONV_NUMBER
|
||||||
#undef CONV_FLOAT
|
#undef TYPVAL_ENCODE_CONV_FLOAT
|
||||||
#undef CONV_FUNC
|
#undef TYPVAL_ENCODE_CONV_FUNC
|
||||||
#undef CONV_EMPTY_LIST
|
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
|
||||||
#undef CONV_LIST_START
|
#undef TYPVAL_ENCODE_CONV_LIST_START
|
||||||
#undef CONV_EMPTY_DICT
|
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
|
||||||
#undef CONV_NIL
|
#undef TYPVAL_ENCODE_CONV_NIL
|
||||||
#undef CONV_BOOL
|
#undef TYPVAL_ENCODE_CONV_BOOL
|
||||||
#undef CONV_UNSIGNED_NUMBER
|
#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
|
||||||
#undef CONV_DICT_START
|
#undef TYPVAL_ENCODE_CONV_DICT_START
|
||||||
#undef CONV_DICT_END
|
#undef TYPVAL_ENCODE_CONV_DICT_END
|
||||||
#undef CONV_DICT_AFTER_KEY
|
#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
|
||||||
#undef CONV_DICT_BETWEEN_ITEMS
|
#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
|
||||||
#undef CONV_SPECIAL_DICT_KEY_CHECK
|
#undef TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK
|
||||||
#undef CONV_LIST_END
|
#undef TYPVAL_ENCODE_CONV_LIST_END
|
||||||
#undef CONV_LIST_BETWEEN_ITEMS
|
#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
|
||||||
#undef CONV_RECURSE
|
#undef TYPVAL_ENCODE_CONV_RECURSE
|
||||||
#undef CONV_ALLOW_SPECIAL
|
#undef TYPVAL_ENCODE_ALLOW_SPECIALS
|
||||||
|
|
||||||
/// Return a string with the string representation of a variable.
|
/// Return a string with the string representation of a variable.
|
||||||
/// Puts quotes around strings, so that they can be parsed back by eval().
|
/// Puts quotes around strings, so that they can be parsed back by eval().
|
||||||
@@ -1181,7 +799,7 @@ char *encode_tv2json(typval_T *tv, size_t *len)
|
|||||||
return (char *) ga.ga_data;
|
return (char *) ga.ga_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CONV_STRING(buf, len) \
|
#define TYPVAL_ENCODE_CONV_STRING(buf, len) \
|
||||||
do { \
|
do { \
|
||||||
if (buf == NULL) { \
|
if (buf == NULL) { \
|
||||||
msgpack_pack_bin(packer, 0); \
|
msgpack_pack_bin(packer, 0); \
|
||||||
@@ -1192,7 +810,7 @@ char *encode_tv2json(typval_T *tv, size_t *len)
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define CONV_STR_STRING(buf, len) \
|
#define TYPVAL_ENCODE_CONV_STR_STRING(buf, len) \
|
||||||
do { \
|
do { \
|
||||||
if (buf == NULL) { \
|
if (buf == NULL) { \
|
||||||
msgpack_pack_str(packer, 0); \
|
msgpack_pack_str(packer, 0); \
|
||||||
@@ -1203,7 +821,7 @@ char *encode_tv2json(typval_T *tv, size_t *len)
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define CONV_EXT_STRING(buf, len, type) \
|
#define TYPVAL_ENCODE_CONV_EXT_STRING(buf, len, type) \
|
||||||
do { \
|
do { \
|
||||||
if (buf == NULL) { \
|
if (buf == NULL) { \
|
||||||
msgpack_pack_ext(packer, 0, (int8_t) type); \
|
msgpack_pack_ext(packer, 0, (int8_t) type); \
|
||||||
@@ -1214,30 +832,30 @@ char *encode_tv2json(typval_T *tv, size_t *len)
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define CONV_NUMBER(num) \
|
#define TYPVAL_ENCODE_CONV_NUMBER(num) \
|
||||||
msgpack_pack_int64(packer, (int64_t) (num))
|
msgpack_pack_int64(packer, (int64_t) (num))
|
||||||
|
|
||||||
#define CONV_FLOAT(flt) \
|
#define TYPVAL_ENCODE_CONV_FLOAT(flt) \
|
||||||
msgpack_pack_double(packer, (double) (flt))
|
msgpack_pack_double(packer, (double) (flt))
|
||||||
|
|
||||||
#define CONV_FUNC(fun) \
|
#define TYPVAL_ENCODE_CONV_FUNC(fun) \
|
||||||
return conv_error(_("E951: Error while dumping %s, %s: " \
|
return conv_error(_("E951: Error while dumping %s, %s: " \
|
||||||
"attempt to dump function reference"), \
|
"attempt to dump function reference"), \
|
||||||
mpstack, objname)
|
mpstack, objname)
|
||||||
|
|
||||||
#define CONV_EMPTY_LIST() \
|
#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
|
||||||
msgpack_pack_array(packer, 0)
|
msgpack_pack_array(packer, 0)
|
||||||
|
|
||||||
#define CONV_LIST_START(lst) \
|
#define TYPVAL_ENCODE_CONV_LIST_START(len) \
|
||||||
msgpack_pack_array(packer, (size_t) (lst)->lv_len)
|
msgpack_pack_array(packer, (size_t) (len))
|
||||||
|
|
||||||
#define CONV_EMPTY_DICT() \
|
#define TYPVAL_ENCODE_CONV_EMPTY_DICT() \
|
||||||
msgpack_pack_map(packer, 0)
|
msgpack_pack_map(packer, 0)
|
||||||
|
|
||||||
#define CONV_NIL() \
|
#define TYPVAL_ENCODE_CONV_NIL() \
|
||||||
msgpack_pack_nil(packer)
|
msgpack_pack_nil(packer)
|
||||||
|
|
||||||
#define CONV_BOOL(num) \
|
#define TYPVAL_ENCODE_CONV_BOOL(num) \
|
||||||
do { \
|
do { \
|
||||||
if ((num)) { \
|
if ((num)) { \
|
||||||
msgpack_pack_true(packer); \
|
msgpack_pack_true(packer); \
|
||||||
@@ -1246,51 +864,51 @@ char *encode_tv2json(typval_T *tv, size_t *len)
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define CONV_UNSIGNED_NUMBER(num) \
|
#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(num) \
|
||||||
msgpack_pack_uint64(packer, (num))
|
msgpack_pack_uint64(packer, (num))
|
||||||
|
|
||||||
#define CONV_DICT_START(len) \
|
#define TYPVAL_ENCODE_CONV_DICT_START(len) \
|
||||||
msgpack_pack_map(packer, (size_t) (len))
|
msgpack_pack_map(packer, (size_t) (len))
|
||||||
|
|
||||||
#define CONV_DICT_END()
|
#define TYPVAL_ENCODE_CONV_DICT_END()
|
||||||
|
|
||||||
#define CONV_DICT_AFTER_KEY()
|
#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY()
|
||||||
|
|
||||||
#define CONV_DICT_BETWEEN_ITEMS()
|
#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS()
|
||||||
|
|
||||||
#define CONV_SPECIAL_DICT_KEY_CHECK(name, kv_pair)
|
#define TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(label, kv_pair)
|
||||||
|
|
||||||
#define CONV_LIST_END(lst)
|
#define TYPVAL_ENCODE_CONV_LIST_END()
|
||||||
|
|
||||||
#define CONV_LIST_BETWEEN_ITEMS()
|
#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS()
|
||||||
|
|
||||||
#define CONV_RECURSE(val, conv_type) \
|
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
|
||||||
return conv_error(_("E952: Unable to dump %s: " \
|
return conv_error(_("E952: Unable to dump %s: " \
|
||||||
"container references itself in %s"), \
|
"container references itself in %s"), \
|
||||||
mpstack, objname)
|
mpstack, objname)
|
||||||
|
|
||||||
#define CONV_ALLOW_SPECIAL true
|
#define TYPVAL_ENCODE_ALLOW_SPECIALS true
|
||||||
|
|
||||||
DEFINE_VIML_CONV_FUNCTIONS(, msgpack, msgpack_packer *const, packer)
|
TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(, msgpack, msgpack_packer *const, packer)
|
||||||
|
|
||||||
#undef CONV_STRING
|
#undef TYPVAL_ENCODE_CONV_STRING
|
||||||
#undef CONV_STR_STRING
|
#undef TYPVAL_ENCODE_CONV_STR_STRING
|
||||||
#undef CONV_EXT_STRING
|
#undef TYPVAL_ENCODE_CONV_EXT_STRING
|
||||||
#undef CONV_NUMBER
|
#undef TYPVAL_ENCODE_CONV_NUMBER
|
||||||
#undef CONV_FLOAT
|
#undef TYPVAL_ENCODE_CONV_FLOAT
|
||||||
#undef CONV_FUNC
|
#undef TYPVAL_ENCODE_CONV_FUNC
|
||||||
#undef CONV_EMPTY_LIST
|
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
|
||||||
#undef CONV_LIST_START
|
#undef TYPVAL_ENCODE_CONV_LIST_START
|
||||||
#undef CONV_EMPTY_DICT
|
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
|
||||||
#undef CONV_NIL
|
#undef TYPVAL_ENCODE_CONV_NIL
|
||||||
#undef CONV_BOOL
|
#undef TYPVAL_ENCODE_CONV_BOOL
|
||||||
#undef CONV_UNSIGNED_NUMBER
|
#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
|
||||||
#undef CONV_DICT_START
|
#undef TYPVAL_ENCODE_CONV_DICT_START
|
||||||
#undef CONV_DICT_END
|
#undef TYPVAL_ENCODE_CONV_DICT_END
|
||||||
#undef CONV_DICT_AFTER_KEY
|
#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
|
||||||
#undef CONV_DICT_BETWEEN_ITEMS
|
#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
|
||||||
#undef CONV_SPECIAL_DICT_KEY_CHECK
|
#undef TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK
|
||||||
#undef CONV_LIST_END
|
#undef TYPVAL_ENCODE_CONV_LIST_END
|
||||||
#undef CONV_LIST_BETWEEN_ITEMS
|
#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
|
||||||
#undef CONV_RECURSE
|
#undef TYPVAL_ENCODE_CONV_RECURSE
|
||||||
#undef CONV_ALLOW_SPECIAL
|
#undef TYPVAL_ENCODE_ALLOW_SPECIALS
|
||||||
|
555
src/nvim/eval/typval_encode.h
Normal file
555
src/nvim/eval/typval_encode.h
Normal file
@@ -0,0 +1,555 @@
|
|||||||
|
/// @file eval/typval_convert.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_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 kv_pair List with two elements: key and value.
|
||||||
|
|
||||||
|
/// @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.
|
||||||
|
#ifndef NVIM_EVAL_TYPVAL_ENCODE_H
|
||||||
|
#define NVIM_EVAL_TYPVAL_ENCODE_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"
|
||||||
|
|
||||||
|
/// Type of the stack entry
|
||||||
|
typedef enum {
|
||||||
|
kMPConvDict, ///< Convert dict_T *dictionary.
|
||||||
|
kMPConvList, ///< Convert list_T *list.
|
||||||
|
kMPConvPairs, ///< Convert mapping represented as a list_T* of pairs.
|
||||||
|
} MPConvStackValType;
|
||||||
|
|
||||||
|
/// Structure representing current VimL to messagepack conversion state
|
||||||
|
typedef struct {
|
||||||
|
MPConvStackValType type; ///< Type of the stack entry.
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
dict_T *dict; ///< Currently converted dictionary.
|
||||||
|
hashitem_T *hi; ///< Currently converted dictionary item.
|
||||||
|
size_t todo; ///< Amount of items left to process.
|
||||||
|
} d; ///< State of dictionary conversion.
|
||||||
|
struct {
|
||||||
|
list_T *list; ///< Currently converted list.
|
||||||
|
listitem_T *li; ///< Currently converted list item.
|
||||||
|
} l; ///< State of list or generic mapping conversion.
|
||||||
|
} data; ///< Data to convert.
|
||||||
|
} MPConvStackVal;
|
||||||
|
|
||||||
|
/// Stack used to convert VimL values to messagepack.
|
||||||
|
typedef kvec_t(MPConvStackVal) MPConvStack;
|
||||||
|
|
||||||
|
/// Code for checking whether container references itself
|
||||||
|
///
|
||||||
|
/// @param[in,out] val Container to check.
|
||||||
|
/// @param copyID_attr Name of the container attribute that holds copyID.
|
||||||
|
/// After checking whether value of this attribute is
|
||||||
|
/// copyID (variable) it is set to copyID.
|
||||||
|
#define _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(val, copyID_attr, conv_type) \
|
||||||
|
do { \
|
||||||
|
if ((val)->copyID_attr == copyID) { \
|
||||||
|
TYPVAL_ENCODE_CONV_RECURSE((val), conv_type); \
|
||||||
|
} \
|
||||||
|
(val)->copyID_attr = copyID; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/// Length of the string stored in typval_T
|
||||||
|
///
|
||||||
|
/// @param[in] tv String for which to compute length for. Must be typval_T
|
||||||
|
/// with VAR_STRING.
|
||||||
|
///
|
||||||
|
/// @return Length of the string stored in typval_T, including 0 for NULL
|
||||||
|
/// string.
|
||||||
|
static inline size_t tv_strlen(const typval_T *const tv)
|
||||||
|
FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
|
{
|
||||||
|
assert(tv->v_type == VAR_STRING);
|
||||||
|
return (tv->vval.v_string == NULL
|
||||||
|
? 0
|
||||||
|
: strlen((char *) tv->vval.v_string));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Define functions which convert VimL value to something else
|
||||||
|
///
|
||||||
|
/// Creates function `vim_to_{name}(firstargtype firstargname, typval_T *const
|
||||||
|
/// tv)` which returns OK or FAIL and helper functions.
|
||||||
|
///
|
||||||
|
/// @param scope Scope of the main function: either nothing or `static`.
|
||||||
|
/// @param firstargtype Type of the first argument. It will be used to return
|
||||||
|
/// the results.
|
||||||
|
/// @param firstargname Name of the first argument.
|
||||||
|
/// @param name Name of the target converter.
|
||||||
|
#define TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(scope, name, firstargtype, \
|
||||||
|
firstargname) \
|
||||||
|
static int name##_convert_one_value(firstargtype firstargname, \
|
||||||
|
MPConvStack *const mpstack, \
|
||||||
|
typval_T *const tv, \
|
||||||
|
const int copyID, \
|
||||||
|
const char *const objname) \
|
||||||
|
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \
|
||||||
|
{ \
|
||||||
|
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(tv->vval.v_string); \
|
||||||
|
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, \
|
||||||
|
kMPConvList); \
|
||||||
|
TYPVAL_ENCODE_CONV_LIST_START(tv->vval.v_list->lv_len); \
|
||||||
|
kv_push(*mpstack, ((MPConvStackVal) { \
|
||||||
|
.type = kMPConvList, \
|
||||||
|
.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 name##_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 name##_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 name##_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 name##_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 name##_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 name##_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 name##_convert_one_value_regular_dict; \
|
||||||
|
} \
|
||||||
|
_TYPVAL_ENCODE_CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list, \
|
||||||
|
lv_copyID, kMPConvList); \
|
||||||
|
TYPVAL_ENCODE_CONV_LIST_START(val_di->di_tv.vval.v_list->lv_len); \
|
||||||
|
kv_push(*mpstack, ((MPConvStackVal) { \
|
||||||
|
.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 name##_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 name##_convert_one_value_regular_dict; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
_TYPVAL_ENCODE_CHECK_SELF_REFERENCE(val_list, lv_copyID, \
|
||||||
|
kMPConvPairs); \
|
||||||
|
TYPVAL_ENCODE_CONV_DICT_START(val_list->lv_len); \
|
||||||
|
kv_push(*mpstack, ((MPConvStackVal) { \
|
||||||
|
.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 name##_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 name##_convert_one_value_regular_dict; \
|
||||||
|
} \
|
||||||
|
TYPVAL_ENCODE_CONV_EXT_STRING(buf, len, type); \
|
||||||
|
xfree(buf); \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
name##_convert_one_value_regular_dict: \
|
||||||
|
_TYPVAL_ENCODE_CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, \
|
||||||
|
kMPConvDict); \
|
||||||
|
TYPVAL_ENCODE_CONV_DICT_START(tv->vval.v_dict->dv_hashtab.ht_used); \
|
||||||
|
kv_push(*mpstack, ((MPConvStackVal) { \
|
||||||
|
.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), #name "_convert_one_value()"); \
|
||||||
|
return FAIL; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
return OK; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
scope int encode_vim_to_##name(firstargtype firstargname, typval_T *const tv, \
|
||||||
|
const char *const objname) \
|
||||||
|
FUNC_ATTR_WARN_UNUSED_RESULT \
|
||||||
|
{ \
|
||||||
|
const int copyID = get_copyID(); \
|
||||||
|
MPConvStack mpstack; \
|
||||||
|
kv_init(mpstack); \
|
||||||
|
if (name##_convert_one_value(firstargname, &mpstack, tv, copyID, objname) \
|
||||||
|
== FAIL) { \
|
||||||
|
goto encode_vim_to_##name##_error_ret; \
|
||||||
|
} \
|
||||||
|
while (kv_size(mpstack)) { \
|
||||||
|
MPConvStackVal *cur_mpsv = &kv_A(mpstack, kv_size(mpstack) - 1); \
|
||||||
|
typval_T *cur_tv = NULL; \
|
||||||
|
switch (cur_mpsv->type) { \
|
||||||
|
case kMPConvDict: { \
|
||||||
|
if (!cur_mpsv->data.d.todo) { \
|
||||||
|
(void) kv_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) kv_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) kv_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_##name##_error_ret, kv_pair); \
|
||||||
|
if (name##_convert_one_value(firstargname, &mpstack, \
|
||||||
|
&kv_pair->lv_first->li_tv, copyID, \
|
||||||
|
objname) == FAIL) { \
|
||||||
|
goto encode_vim_to_##name##_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; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
assert(cur_tv != NULL); \
|
||||||
|
if (name##_convert_one_value(firstargname, &mpstack, cur_tv, copyID, \
|
||||||
|
objname) == FAIL) { \
|
||||||
|
goto encode_vim_to_##name##_error_ret; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
kv_destroy(mpstack); \
|
||||||
|
return OK; \
|
||||||
|
encode_vim_to_##name##_error_ret: \
|
||||||
|
kv_destroy(mpstack); \
|
||||||
|
return FAIL; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // NVIM_EVAL_TYPVAL_ENCODE_H
|
Reference in New Issue
Block a user