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 }