neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

vimscript.c (24575B)


      1 #include <assert.h>
      2 #include <stdbool.h>
      3 #include <stddef.h>
      4 #include <stdint.h>
      5 #include <stdlib.h>
      6 #include <string.h>
      7 
      8 #include "klib/kvec.h"
      9 #include "nvim/api/keysets_defs.h"
     10 #include "nvim/api/private/converter.h"
     11 #include "nvim/api/private/defs.h"
     12 #include "nvim/api/private/helpers.h"
     13 #include "nvim/api/vimscript.h"
     14 #include "nvim/ascii_defs.h"
     15 #include "nvim/buffer_defs.h"
     16 #include "nvim/eval.h"
     17 #include "nvim/eval/typval.h"
     18 #include "nvim/eval/typval_defs.h"
     19 #include "nvim/eval/userfunc.h"
     20 #include "nvim/ex_docmd.h"
     21 #include "nvim/garray.h"
     22 #include "nvim/garray_defs.h"
     23 #include "nvim/globals.h"
     24 #include "nvim/memory.h"
     25 #include "nvim/memory_defs.h"
     26 #include "nvim/runtime.h"
     27 #include "nvim/vim_defs.h"
     28 #include "nvim/viml/parser/expressions.h"
     29 #include "nvim/viml/parser/parser.h"
     30 #include "nvim/viml/parser/parser_defs.h"
     31 
     32 #include "api/vimscript.c.generated.h"
     33 
     34 /// Executes Vimscript (multiline block of Ex commands), like anonymous
     35 /// |:source|.
     36 ///
     37 /// Unlike |nvim_command()| this function supports heredocs, script-scope (s:),
     38 /// etc.
     39 ///
     40 /// On execution error: fails with Vimscript error, updates v:errmsg.
     41 ///
     42 /// @see |execute()|
     43 /// @see |nvim_command()|
     44 /// @see |nvim_cmd()|
     45 ///
     46 /// @param src      Vimscript code
     47 /// @param opts  Optional parameters.
     48 ///           - output: (boolean, default false) Whether to capture and return
     49 ///                     all (non-error, non-shell |:!|) output.
     50 /// @param[out] err Error details (Vim error), if any
     51 /// @return Dict containing information about execution, with these keys:
     52 ///       - output: (string|nil) Output if `opts.output` is true.
     53 Dict nvim_exec2(uint64_t channel_id, String src, Dict(exec_opts) *opts, Error *err)
     54  FUNC_API_SINCE(11) FUNC_API_RET_ALLOC
     55 {
     56  Dict result = ARRAY_DICT_INIT;
     57 
     58  String output = exec_impl(channel_id, src, opts, err);
     59  if (ERROR_SET(err)) {
     60    return result;
     61  }
     62 
     63  if (opts->output) {
     64    PUT(result, "output", STRING_OBJ(output));
     65  }
     66 
     67  return result;
     68 }
     69 
     70 String exec_impl(uint64_t channel_id, String src, Dict(exec_opts) *opts, Error *err)
     71 {
     72  const int save_msg_silent = msg_silent;
     73  const bool save_redir_off = redir_off;
     74  garray_T *const save_capture_ga = capture_ga;
     75  const int save_msg_col = msg_col;
     76  garray_T capture_local;
     77  if (opts->output) {
     78    ga_init(&capture_local, 1, 80);
     79    capture_ga = &capture_local;
     80  }
     81 
     82  TRY_WRAP(err, {
     83    if (opts->output) {
     84      msg_silent++;
     85      redir_off = false;
     86      msg_col = 0;  // prevent leading spaces
     87    }
     88 
     89    const sctx_T save_current_sctx = api_set_sctx(channel_id);
     90 
     91    do_source_str(src.data, "nvim_exec2()");
     92    if (opts->output) {
     93      capture_ga = save_capture_ga;
     94      msg_silent = save_msg_silent;
     95      redir_off = save_redir_off;
     96      // Put msg_col back where it was, since nothing should have been written.
     97      msg_col = save_msg_col;
     98    }
     99 
    100    current_sctx = save_current_sctx;
    101  });
    102 
    103  if (ERROR_SET(err)) {
    104    goto theend;
    105  }
    106 
    107  if (opts->output && capture_local.ga_len > 1) {
    108    String s = (String){
    109      .data = capture_local.ga_data,
    110      .size = (size_t)capture_local.ga_len,
    111    };
    112    // redir usually (except :echon) prepends a newline.
    113    if (s.data[0] == '\n') {
    114      memmove(s.data, s.data + 1, s.size - 1);
    115      s.data[s.size - 1] = NUL;
    116      s.size = s.size - 1;
    117    }
    118    return s;  // Caller will free the memory.
    119  }
    120 theend:
    121  if (opts->output) {
    122    ga_clear(&capture_local);
    123  }
    124  return (String)STRING_INIT;
    125 }
    126 
    127 /// Executes an Ex command.
    128 ///
    129 /// On execution error: fails with Vimscript error, updates v:errmsg.
    130 ///
    131 /// Prefer |nvim_cmd()| or |nvim_exec2()| instead. To modify an Ex command in a structured way
    132 /// before executing it, modify the result of |nvim_parse_cmd()| then pass it to |nvim_cmd()|.
    133 ///
    134 /// @param command  Ex command string
    135 /// @param[out] err Error details (Vim error), if any
    136 void nvim_command(String command, Error *err)
    137  FUNC_API_SINCE(1)
    138 {
    139  TRY_WRAP(err, {
    140    do_cmdline_cmd(command.data);
    141  });
    142 }
    143 
    144 /// Evaluates a Vimscript |expression|. Dicts and Lists are recursively expanded.
    145 ///
    146 /// On execution error: fails with Vimscript error, updates v:errmsg.
    147 ///
    148 /// @param expr     Vimscript expression string
    149 /// @param[out] err Error details, if any
    150 /// @return         Evaluation result or expanded object
    151 Object nvim_eval(String expr, Arena *arena, Error *err)
    152  FUNC_API_SINCE(1)
    153 {
    154  static int recursive = 0;  // recursion depth
    155  Object rv = OBJECT_INIT;
    156 
    157  // Initialize `force_abort`  and `suppress_errthrow` at the top level.
    158  if (!recursive) {
    159    force_abort = false;
    160    suppress_errthrow = false;
    161    did_throw = false;
    162    // `did_emsg` is set by emsg(), which cancels execution.
    163    did_emsg = false;
    164  }
    165 
    166  recursive++;
    167 
    168  typval_T rettv;
    169  int ok;
    170 
    171  TRY_WRAP(err, {
    172    ok = eval0(expr.data, &rettv, NULL, &EVALARG_EVALUATE);
    173    clear_evalarg(&EVALARG_EVALUATE, NULL);
    174  });
    175 
    176  if (!ERROR_SET(err)) {
    177    if (ok == FAIL) {
    178      // Should never happen, try_end() (in TRY_WRAP) should get the error. #8371
    179      api_set_error(err, kErrorTypeException,
    180                    "Failed to evaluate expression: '%.*s'", 256, expr.data);
    181    } else {
    182      rv = vim_to_object(&rettv, arena, false);
    183    }
    184  }
    185 
    186  tv_clear(&rettv);
    187  recursive--;
    188 
    189  return rv;
    190 }
    191 
    192 /// Calls a Vimscript function.
    193 ///
    194 /// @param fn Function name
    195 /// @param args Function arguments
    196 /// @param self `self` dict, or NULL for non-dict functions
    197 /// @param[out] err Error details, if any
    198 /// @return Result of the function call
    199 static Object _call_function(String fn, Array args, dict_T *self, Arena *arena, Error *err)
    200 {
    201  static int recursive = 0;  // recursion depth
    202  Object rv = OBJECT_INIT;
    203 
    204  if (args.size > MAX_FUNC_ARGS) {
    205    api_set_error(err, kErrorTypeValidation,
    206                  "Function called with too many arguments");
    207    return rv;
    208  }
    209 
    210  // Convert the arguments in args from Object to typval_T values
    211  typval_T vim_args[MAX_FUNC_ARGS + 1];
    212  size_t i = 0;  // also used for freeing the variables
    213  for (; i < args.size; i++) {
    214    object_to_vim(args.items[i], &vim_args[i], err);
    215  }
    216 
    217  // Initialize `force_abort`  and `suppress_errthrow` at the top level.
    218  if (!recursive) {
    219    force_abort = false;
    220    suppress_errthrow = false;
    221    did_throw = false;
    222    // `did_emsg` is set by emsg(), which cancels execution.
    223    did_emsg = false;
    224  }
    225  recursive++;
    226 
    227  typval_T rettv;
    228  funcexe_T funcexe = FUNCEXE_INIT;
    229  funcexe.fe_firstline = curwin->w_cursor.lnum;
    230  funcexe.fe_lastline = curwin->w_cursor.lnum;
    231  funcexe.fe_evaluate = true;
    232  funcexe.fe_selfdict = self;
    233 
    234  TRY_WRAP(err, {
    235    // call_func() retval is deceptive, ignore it.  Instead TRY_WRAP sets `msg_list` to capture
    236    // abort-causing non-exception errors.
    237    (void)call_func(fn.data, (int)fn.size, &rettv, (int)args.size, vim_args, &funcexe);
    238  });
    239 
    240  if (!ERROR_SET(err)) {
    241    rv = vim_to_object(&rettv, arena, false);
    242  }
    243 
    244  tv_clear(&rettv);
    245  recursive--;
    246 
    247  while (i > 0) {
    248    tv_clear(&vim_args[--i]);
    249  }
    250 
    251  return rv;
    252 }
    253 
    254 /// Calls a Vimscript function with the given arguments.
    255 ///
    256 /// On execution error: fails with Vimscript error, updates v:errmsg.
    257 ///
    258 /// @param fn       Function to call
    259 /// @param args     Function arguments packed in an Array
    260 /// @param[out] err Error details, if any
    261 /// @return Result of the function call
    262 Object nvim_call_function(String fn, Array args, Arena *arena, Error *err)
    263  FUNC_API_SINCE(1)
    264 {
    265  return _call_function(fn, args, NULL, arena, err);
    266 }
    267 
    268 /// Calls a Vimscript |Dictionary-function| with the given arguments.
    269 ///
    270 /// On execution error: fails with Vimscript error, updates v:errmsg.
    271 ///
    272 /// @param dict Dict, or String evaluating to a Vimscript |self| dict
    273 /// @param fn Name of the function defined on the Vimscript dict
    274 /// @param args Function arguments packed in an Array
    275 /// @param[out] err Error details, if any
    276 /// @return Result of the function call
    277 Object nvim_call_dict_function(Object dict, String fn, Array args, Arena *arena, Error *err)
    278  FUNC_API_SINCE(4)
    279 {
    280  Object rv = OBJECT_INIT;
    281 
    282  typval_T rettv;
    283  bool mustfree = false;
    284  switch (dict.type) {
    285  case kObjectTypeString: {
    286    int eval_ret;
    287    TRY_WRAP(err, {
    288        eval_ret = eval0(dict.data.string.data, &rettv, NULL, &EVALARG_EVALUATE);
    289        clear_evalarg(&EVALARG_EVALUATE, NULL);
    290      });
    291    if (ERROR_SET(err)) {
    292      return rv;
    293    }
    294    if (eval_ret != OK) {
    295      abort();  // Should not happen.
    296    }
    297    // Evaluation of the string arg created a new dict or increased the
    298    // refcount of a dict. Not necessary for a RPC dict.
    299    mustfree = true;
    300    break;
    301  }
    302  case kObjectTypeDict:
    303    object_to_vim(dict, &rettv, err);
    304    break;
    305  default:
    306    api_set_error(err, kErrorTypeValidation, "dict argument type must be String or Dict");
    307    return rv;
    308  }
    309  dict_T *self_dict = rettv.vval.v_dict;
    310  if (rettv.v_type != VAR_DICT || !self_dict) {
    311    api_set_error(err, kErrorTypeValidation, "dict not found");
    312    goto end;
    313  }
    314 
    315  if (fn.data && fn.size > 0 && dict.type != kObjectTypeDict) {
    316    dictitem_T *const di = tv_dict_find(self_dict, fn.data, (ptrdiff_t)fn.size);
    317    if (di == NULL) {
    318      api_set_error(err, kErrorTypeValidation, "Not found: %s", fn.data);
    319      goto end;
    320    }
    321    if (di->di_tv.v_type == VAR_PARTIAL) {
    322      api_set_error(err, kErrorTypeValidation,
    323                    "partial function not supported");
    324      goto end;
    325    }
    326    if (di->di_tv.v_type != VAR_FUNC) {
    327      api_set_error(err, kErrorTypeValidation, "Not a function: %s", fn.data);
    328      goto end;
    329    }
    330    fn = (String) {
    331      .data = di->di_tv.vval.v_string,
    332      .size = strlen(di->di_tv.vval.v_string),
    333    };
    334  }
    335 
    336  if (!fn.data || fn.size < 1) {
    337    api_set_error(err, kErrorTypeValidation, "Invalid (empty) function name");
    338    goto end;
    339  }
    340 
    341  rv = _call_function(fn, args, self_dict, arena, err);
    342 end:
    343  if (mustfree) {
    344    tv_clear(&rettv);
    345  }
    346 
    347  return rv;
    348 }
    349 
    350 typedef struct {
    351  ExprASTNode **node_p;
    352  Object *ret_node_p;
    353 } ExprASTConvStackItem;
    354 
    355 typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack;
    356 
    357 /// Parse a Vimscript expression.
    358 ///
    359 /// @param[in]  expr  Expression to parse. Always treated as a single line.
    360 /// @param[in]  flags Flags:
    361 ///                    - "m" if multiple expressions in a row are allowed (only
    362 ///                      the first one will be parsed),
    363 ///                    - "E" if EOC tokens are not allowed (determines whether
    364 ///                      they will stop parsing process or be recognized as an
    365 ///                      operator/space, though also yielding an error).
    366 ///                    - "l" when needing to start parsing with lvalues for
    367 ///                      ":let" or ":for".
    368 ///                    Common flag sets:
    369 ///                    - "m" to parse like for `":echo"`.
    370 ///                    - "E" to parse like for `"<C-r>="`.
    371 ///                    - empty string for ":call".
    372 ///                    - "lm" to parse for ":let".
    373 /// @param[in]  highlight  If true, return value will also include "highlight"
    374 ///                        key containing array of 4-tuples (arrays) (Integer,
    375 ///                        Integer, Integer, String), where first three numbers
    376 ///                        define the highlighted region and represent line,
    377 ///                        starting column and ending column (latter exclusive:
    378 ///                        one should highlight region [start_col, end_col)).
    379 ///
    380 /// @return
    381 ///      - AST: top-level dict with these keys:
    382 ///        - "error": Dict with error, present only if parser saw some
    383 ///                 error. Contains the following keys:
    384 ///          - "message": String, error message in printf format, translated.
    385 ///                       Must contain exactly one `%.*s`.
    386 ///          - "arg": String, error message argument.
    387 ///        - "len": Amount of bytes successfully parsed. With flags equal to ""
    388 ///                 that should be equal to the length of expr string.
    389 ///                 ("Successfully parsed" here means "participated in AST
    390 ///                  creation", not "till the first error".)
    391 ///        - "ast": AST, either nil or a dict with these keys:
    392 ///          - "type": node type, one of the value names from ExprASTNodeType
    393 ///                    stringified without "kExprNode" prefix.
    394 ///          - "start": a pair `[line, column]` describing where node is "started"
    395 ///                     where "line" is always 0 (will not be 0 if you will be
    396 ///                     using this API on e.g. ":let", but that is not
    397 ///                     present yet). Both elements are Integers.
    398 ///          - "len": “length” of the node. This and "start" are there for
    399 ///                   debugging purposes primary (debugging parser and providing
    400 ///                   debug information).
    401 ///          - "children": a list of nodes described in top/"ast". There always
    402 ///                        is zero, one or two children, key will not be present
    403 ///                        if node has no children. Maximum number of children
    404 ///                        may be found in node_maxchildren array.
    405 ///      - Local values (present only for certain nodes):
    406 ///        - "scope": a single Integer, specifies scope for "Option" and
    407 ///                   "PlainIdentifier" nodes. For "Option" it is one of
    408 ///                   ExprOptScope values, for "PlainIdentifier" it is one of
    409 ///                   ExprVarScope values.
    410 ///        - "ident": identifier (without scope, if any), present for "Option",
    411 ///                   "PlainIdentifier", "PlainKey" and "Environment" nodes.
    412 ///        - "name": Integer, register name (one character) or -1. Only present
    413 ///                for "Register" nodes.
    414 ///        - "cmp_type": String, comparison type, one of the value names from
    415 ///                      ExprComparisonType, stringified without "kExprCmp"
    416 ///                      prefix. Only present for "Comparison" nodes.
    417 ///        - "ccs_strategy": String, case comparison strategy, one of the
    418 ///                          value names from ExprCaseCompareStrategy,
    419 ///                          stringified without "kCCStrategy" prefix. Only
    420 ///                          present for "Comparison" nodes.
    421 ///        - "augmentation": String, augmentation type for "Assignment" nodes.
    422 ///                          Is either an empty string, "Add", "Subtract" or
    423 ///                          "Concat" for "=", "+=", "-=" or ".=" respectively.
    424 ///        - "invert": Boolean, true if result of comparison needs to be
    425 ///                    inverted. Only present for "Comparison" nodes.
    426 ///        - "ivalue": Integer, integer value for "Integer" nodes.
    427 ///        - "fvalue": Float, floating-point value for "Float" nodes.
    428 ///        - "svalue": String, value for "SingleQuotedString" and
    429 ///                    "DoubleQuotedString" nodes.
    430 /// @param[out] err Error details, if any
    431 Dict nvim_parse_expression(String expr, String flags, Boolean highlight, Arena *arena, Error *err)
    432  FUNC_API_SINCE(4) FUNC_API_FAST
    433 {
    434  int pflags = 0;
    435  for (size_t i = 0; i < flags.size; i++) {
    436    switch (flags.data[i]) {
    437    case 'm':
    438      pflags |= kExprFlagsMulti; break;
    439    case 'E':
    440      pflags |= kExprFlagsDisallowEOC; break;
    441    case 'l':
    442      pflags |= kExprFlagsParseLet; break;
    443    case NUL:
    444      api_set_error(err, kErrorTypeValidation, "Invalid flag: '\\0' (%u)",
    445                    (unsigned)flags.data[i]);
    446      return (Dict)ARRAY_DICT_INIT;
    447    default:
    448      api_set_error(err, kErrorTypeValidation, "Invalid flag: '%c' (%u)",
    449                    flags.data[i], (unsigned)flags.data[i]);
    450      return (Dict)ARRAY_DICT_INIT;
    451    }
    452  }
    453  ParserLine parser_lines[] = {
    454    {
    455      .data = expr.data,
    456      .size = expr.size,
    457      .allocated = false,
    458    },
    459    { NULL, 0, false },
    460  };
    461  ParserLine *plines_p = parser_lines;
    462  ParserHighlight colors;
    463  kvi_init(colors);
    464  ParserHighlight *const colors_p = (highlight ? &colors : NULL);
    465  ParserState pstate;
    466  viml_parser_init(&pstate, parser_simple_get_line, &plines_p, colors_p);
    467  ExprAST east = viml_pexpr_parse(&pstate, pflags);
    468 
    469  const size_t ret_size = (2  // "ast", "len"
    470                           + (size_t)(east.err.msg != NULL)  // "error"
    471                           + (size_t)highlight  // "highlight"
    472                           + 0);
    473 
    474  Dict ret = arena_dict(arena, ret_size);
    475  PUT_C(ret, "len", INTEGER_OBJ((Integer)(pstate.pos.line == 1
    476                                          ? parser_lines[0].size
    477                                          : pstate.pos.col)));
    478  if (east.err.msg != NULL) {
    479    Dict err_dict = arena_dict(arena, 2);
    480    PUT_C(err_dict, "message", CSTR_TO_ARENA_OBJ(arena, east.err.msg));
    481    PUT_C(err_dict, "arg", CBUF_TO_ARENA_OBJ(arena, east.err.arg, (size_t)east.err.arg_len));
    482    PUT_C(ret, "error", DICT_OBJ(err_dict));
    483  }
    484  if (highlight) {
    485    Array hl = arena_array(arena, kv_size(colors));
    486    for (size_t i = 0; i < kv_size(colors); i++) {
    487      const ParserHighlightChunk chunk = kv_A(colors, i);
    488      Array chunk_arr = arena_array(arena, 4);
    489      ADD_C(chunk_arr, INTEGER_OBJ((Integer)chunk.start.line));
    490      ADD_C(chunk_arr, INTEGER_OBJ((Integer)chunk.start.col));
    491      ADD_C(chunk_arr, INTEGER_OBJ((Integer)chunk.end_col));
    492      ADD_C(chunk_arr, CSTR_AS_OBJ(chunk.group));
    493 
    494      ADD_C(hl, ARRAY_OBJ(chunk_arr));
    495    }
    496    PUT_C(ret, "highlight", ARRAY_OBJ(hl));
    497  }
    498  kvi_destroy(colors);
    499 
    500  // Walk over the AST, freeing nodes in process.
    501  ExprASTConvStack ast_conv_stack;
    502  kvi_init(ast_conv_stack);
    503  Object ast = NIL;
    504  kvi_push(ast_conv_stack, ((ExprASTConvStackItem) {
    505    .node_p = &east.root,
    506    .ret_node_p = &ast,
    507  }));
    508  while (kv_size(ast_conv_stack)) {
    509    ExprASTConvStackItem cur_item = kv_last(ast_conv_stack);
    510    ExprASTNode *const node = *cur_item.node_p;
    511    if (node == NULL) {
    512      assert(kv_size(ast_conv_stack) == 1);
    513      kv_drop(ast_conv_stack, 1);
    514    } else {
    515      if (cur_item.ret_node_p->type == kObjectTypeNil) {
    516        size_t items_size = (size_t)(3  // "type", "start" and "len"  // NOLINT(bugprone-misplaced-widening-cast)
    517                                     + (node->children != NULL)  // "children"
    518                                     + (node->type == kExprNodeOption
    519                                        || node->type == kExprNodePlainIdentifier)  // "scope"
    520                                     + (node->type == kExprNodeOption
    521                                        || node->type == kExprNodePlainIdentifier
    522                                        || node->type == kExprNodePlainKey
    523                                        || node->type == kExprNodeEnvironment)  // "ident"
    524                                     + (node->type == kExprNodeRegister)  // "name"
    525                                     + (3  // "cmp_type", "ccs_strategy", "invert"
    526                                        * (node->type == kExprNodeComparison))
    527                                     + (node->type == kExprNodeInteger)  // "ivalue"
    528                                     + (node->type == kExprNodeFloat)  // "fvalue"
    529                                     + (node->type == kExprNodeDoubleQuotedString
    530                                        || node->type == kExprNodeSingleQuotedString)  // "svalue"
    531                                     + (node->type == kExprNodeAssignment)  // "augmentation"
    532                                     + 0);
    533        Dict ret_node = arena_dict(arena, items_size);
    534        *cur_item.ret_node_p = DICT_OBJ(ret_node);
    535      }
    536      Dict *ret_node = &cur_item.ret_node_p->data.dict;
    537      if (node->children != NULL) {
    538        const size_t num_children = 1 + (node->children->next != NULL);
    539        Array children_array = arena_array(arena, num_children);
    540        for (size_t i = 0; i < num_children; i++) {
    541          ADD_C(children_array, NIL);
    542        }
    543        PUT_C(*ret_node, "children", ARRAY_OBJ(children_array));
    544        kvi_push(ast_conv_stack, ((ExprASTConvStackItem) {
    545          .node_p = &node->children,
    546          .ret_node_p = &children_array.items[0],
    547        }));
    548      } else if (node->next != NULL) {
    549        kvi_push(ast_conv_stack, ((ExprASTConvStackItem) {
    550          .node_p = &node->next,
    551          .ret_node_p = cur_item.ret_node_p + 1,
    552        }));
    553      } else {
    554        kv_drop(ast_conv_stack, 1);
    555        PUT_C(*ret_node, "type", CSTR_AS_OBJ(east_node_type_tab[node->type]));
    556        Array start_array = arena_array(arena, 2);
    557        ADD_C(start_array, INTEGER_OBJ((Integer)node->start.line));
    558        ADD_C(start_array, INTEGER_OBJ((Integer)node->start.col));
    559        PUT_C(*ret_node, "start", ARRAY_OBJ(start_array));
    560 
    561        PUT_C(*ret_node, "len", INTEGER_OBJ((Integer)node->len));
    562        switch (node->type) {
    563        case kExprNodeDoubleQuotedString:
    564        case kExprNodeSingleQuotedString: {
    565          Object str = CBUF_TO_ARENA_OBJ(arena, node->data.str.value, node->data.str.size);
    566          PUT_C(*ret_node, "svalue", str);
    567          xfree(node->data.str.value);
    568          break;
    569        }
    570        case kExprNodeOption:
    571          PUT_C(*ret_node, "scope", INTEGER_OBJ(node->data.opt.scope));
    572          PUT_C(*ret_node, "ident", CBUF_TO_ARENA_OBJ(arena, node->data.opt.ident,
    573                                                      node->data.opt.ident_len));
    574          break;
    575        case kExprNodePlainIdentifier:
    576          PUT_C(*ret_node, "scope", INTEGER_OBJ(node->data.var.scope));
    577          PUT_C(*ret_node, "ident", CBUF_TO_ARENA_OBJ(arena, node->data.var.ident,
    578                                                      node->data.var.ident_len));
    579          break;
    580        case kExprNodePlainKey:
    581          PUT_C(*ret_node, "ident", CBUF_TO_ARENA_OBJ(arena, node->data.var.ident,
    582                                                      node->data.var.ident_len));
    583          break;
    584        case kExprNodeEnvironment:
    585          PUT_C(*ret_node, "ident", CBUF_TO_ARENA_OBJ(arena, node->data.env.ident,
    586                                                      node->data.env.ident_len));
    587          break;
    588        case kExprNodeRegister:
    589          PUT_C(*ret_node, "name", INTEGER_OBJ(node->data.reg.name));
    590          break;
    591        case kExprNodeComparison:
    592          PUT_C(*ret_node, "cmp_type", CSTR_AS_OBJ(eltkn_cmp_type_tab[node->data.cmp.type]));
    593          PUT_C(*ret_node, "ccs_strategy", CSTR_AS_OBJ(ccs_tab[node->data.cmp.ccs]));
    594          PUT_C(*ret_node, "invert", BOOLEAN_OBJ(node->data.cmp.inv));
    595          break;
    596        case kExprNodeFloat:
    597          PUT_C(*ret_node, "fvalue", FLOAT_OBJ(node->data.flt.value));
    598          break;
    599        case kExprNodeInteger:
    600          PUT_C(*ret_node, "ivalue", INTEGER_OBJ((Integer)(node->data.num.value > API_INTEGER_MAX
    601                                                           ? API_INTEGER_MAX
    602                                                           : (Integer)node->data.num.value)));
    603          break;
    604        case kExprNodeAssignment: {
    605          const ExprAssignmentType asgn_type = node->data.ass.type;
    606          String str = (asgn_type == kExprAsgnPlain
    607                        ? (String)STRING_INIT : cstr_as_string(expr_asgn_type_tab[asgn_type]));
    608          PUT_C(*ret_node, "augmentation", STRING_OBJ(str));
    609          break;
    610        }
    611        case kExprNodeMissing:
    612        case kExprNodeOpMissing:
    613        case kExprNodeTernary:
    614        case kExprNodeTernaryValue:
    615        case kExprNodeSubscript:
    616        case kExprNodeListLiteral:
    617        case kExprNodeUnaryPlus:
    618        case kExprNodeBinaryPlus:
    619        case kExprNodeNested:
    620        case kExprNodeCall:
    621        case kExprNodeComplexIdentifier:
    622        case kExprNodeUnknownFigure:
    623        case kExprNodeLambda:
    624        case kExprNodeDictLiteral:
    625        case kExprNodeCurlyBracesIdentifier:
    626        case kExprNodeComma:
    627        case kExprNodeColon:
    628        case kExprNodeArrow:
    629        case kExprNodeConcat:
    630        case kExprNodeConcatOrSubscript:
    631        case kExprNodeOr:
    632        case kExprNodeAnd:
    633        case kExprNodeUnaryMinus:
    634        case kExprNodeBinaryMinus:
    635        case kExprNodeNot:
    636        case kExprNodeMultiplication:
    637        case kExprNodeDivision:
    638        case kExprNodeMod:
    639          break;
    640        }
    641        assert(cur_item.ret_node_p->data.dict.size == cur_item.ret_node_p->data.dict.capacity);
    642        xfree(*cur_item.node_p);
    643        *cur_item.node_p = NULL;
    644      }
    645    }
    646  }
    647  kvi_destroy(ast_conv_stack);
    648  PUT_C(ret, "ast", ast);
    649 
    650  assert(ret.size == ret.capacity);
    651  // Should be a no-op actually, leaving it in case non-nodes will need to be
    652  // freed later.
    653  viml_pexpr_free_ast(east);
    654  viml_parser_destroy(&pstate);
    655  return ret;
    656 }