diff options
author | Thiago de Arruda <tpadilha84@gmail.com> | 2014-05-08 11:12:03 -0300 |
---|---|---|
committer | Thiago de Arruda <tpadilha84@gmail.com> | 2014-05-12 21:26:56 -0300 |
commit | ba11128077845d286d9f1a8282e1404d2c80b2a5 (patch) | |
tree | 967af0e44e7e248b462bbc8df686ec0fc0186d2c /src/api/vim.c | |
parent | d98ca3ea98aee41354a45124cfd9f8f6dfb3017a (diff) | |
download | rneovim-ba11128077845d286d9f1a8282e1404d2c80b2a5.tar.gz rneovim-ba11128077845d286d9f1a8282e1404d2c80b2a5.tar.bz2 rneovim-ba11128077845d286d9f1a8282e1404d2c80b2a5.zip |
API: Implement vim_eval
The vimscript object conversion function was adapted from the version found in
'if_py_both.h' in the upstream source. It was also required to re-add the
`dict_lookup` function that was lost during the initial import.
Diffstat (limited to 'src/api/vim.c')
-rw-r--r-- | src/api/vim.c | 154 |
1 files changed, 153 insertions, 1 deletions
diff --git a/src/api/vim.c b/src/api/vim.c index 31bdbecd68..ee3dca30a4 100644 --- a/src/api/vim.c +++ b/src/api/vim.c @@ -1,6 +1,7 @@ #include <stdint.h> #include <stdbool.h> #include <stdlib.h> +#include <string.h> #include "api/vim.h" #include "api/defs.h" @@ -9,8 +10,13 @@ #include "ascii.h" #include "ex_docmd.h" #include "screen.h" +#include "eval.h" #include "memory.h" +#include "lib/khash.h" + +KHASH_SET_INIT_INT64(Lookup) + /// Start block that may cause vimscript exceptions static void try_start(void); @@ -21,6 +27,21 @@ static void try_start(void); /// @return true if an error occurred static bool try_end(Error *err); +/// Convert a vim object to an `Object` instance, recursively expanding +/// Arrays/Dictionaries. +/// +/// @param obj The source object +/// @return The converted value +static Object vim_to_object(typval_T *vim_obj); + +/// Recursion helper for the `vim_to_object`. This uses a pointer table +/// to avoid infinite recursion due to cyclic references +/// +/// @param obj The source object +/// @param lookup Lookup table containing pointers to all processed objects +/// @return The converted value +static Object vim_to_object_rec(typval_T *obj, khash_t(Lookup) *lookup); + void vim_push_keys(String str) { abort(); @@ -41,7 +62,23 @@ void vim_command(String str, Error *err) Object vim_eval(String str, Error *err) { - abort(); + Object rv; + + char expr_str[str.size + 1]; + memcpy(expr_str, str.data, str.size); + expr_str[str.size] = NUL; + // Evaluate the expression + try_start(); + typval_T *expr_result = eval_expr((char_u *)expr_str, NULL); + + if (!try_end(err)) { + // No errors, convert the result + rv = vim_to_object(expr_result); + } + + // Free the vim object + free_tv(expr_result); + return rv; } int64_t vim_strwidth(String str) @@ -209,3 +246,118 @@ static bool try_end(Error *err) return err->set; } + +static Object vim_to_object(typval_T *obj) +{ + Object rv; + // We use a lookup table to break out of cyclic references + khash_t(Lookup) *lookup = kh_init(Lookup); + rv = vim_to_object_rec(obj, lookup); + // Free the table + kh_destroy(Lookup, lookup); + return rv; +} + +static Object vim_to_object_rec(typval_T *obj, khash_t(Lookup) *lookup) +{ + Object rv = {.type = kObjectTypeNil}; + + if (obj->v_type == VAR_LIST || obj->v_type == VAR_DICT) { + int ret; + // Container object, add it to the lookup table + kh_put(Lookup, lookup, (uint64_t)obj, &ret); + if (!ret) { + // It's already present, meaning we alredy processed it so just return + // nil instead. + return rv; + } + } + + switch (obj->v_type) { + case VAR_STRING: + if (obj->vval.v_string != NULL) { + rv.type = kObjectTypeString; + rv.data.string.data = xstrdup((char *)obj->vval.v_string); + rv.data.string.size = strlen(rv.data.string.data); + } + break; + + case VAR_NUMBER: + rv.type = kObjectTypeInt; + rv.data.integer = obj->vval.v_number; + break; + + case VAR_FLOAT: + rv.type = kObjectTypeFloat; + rv.data.floating_point = obj->vval.v_float; + break; + + case VAR_LIST: + { + list_T *list = obj->vval.v_list; + listitem_T *item; + + if (list != NULL) { + rv.type = kObjectTypeArray; + rv.data.array.size = list->lv_len; + rv.data.array.items = xmalloc(list->lv_len * sizeof(Object)); + + uint32_t i = 0; + for (item = list->lv_first; item != NULL; item = item->li_next) { + rv.data.array.items[i] = vim_to_object_rec(&item->li_tv, lookup); + i++; + } + } + } + break; + + case VAR_DICT: + { + dict_T *dict = obj->vval.v_dict; + hashtab_T *ht; + uint64_t todo; + hashitem_T *hi; + dictitem_T *di; + + if (dict != NULL) { + ht = &obj->vval.v_dict->dv_hashtab; + todo = ht->ht_used; + rv.type = kObjectTypeDictionary; + + // Count items + rv.data.dictionary.size = 0; + for (hi = ht->ht_array; todo > 0; ++hi) { + if (!HASHITEM_EMPTY(hi)) { + todo--; + rv.data.dictionary.size++; + } + } + + rv.data.dictionary.items = + xmalloc(rv.data.dictionary.size * sizeof(KeyValuePair)); + todo = ht->ht_used; + uint32_t i = 0; + + // Convert all + for (hi = ht->ht_array; todo > 0; ++hi) { + if (!HASHITEM_EMPTY(hi)) { + di = dict_lookup(hi); + // Convert key + rv.data.dictionary.items[i].key.data = + xstrdup((char *)hi->hi_key); + rv.data.dictionary.items[i].key.size = + strlen((char *)hi->hi_key); + // Convert value + rv.data.dictionary.items[i].value = + vim_to_object_rec(&di->di_tv, lookup); + todo--; + i++; + } + } + } + } + break; + } + + return rv; +} |