mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 01:34:25 +00:00 
			
		
		
		
	refactor(api): break out Vim script functions to its own file
This commit is contained in:
		@@ -89,7 +89,9 @@ CONFIG = {
 | 
				
			|||||||
        # Section ordering.
 | 
					        # Section ordering.
 | 
				
			||||||
        'section_order': [
 | 
					        'section_order': [
 | 
				
			||||||
            'vim.c',
 | 
					            'vim.c',
 | 
				
			||||||
 | 
					            'vimscript.c',
 | 
				
			||||||
            'buffer.c',
 | 
					            'buffer.c',
 | 
				
			||||||
 | 
					            'extmark.c',
 | 
				
			||||||
            'window.c',
 | 
					            'window.c',
 | 
				
			||||||
            'win_config.c',
 | 
					            'win_config.c',
 | 
				
			||||||
            'tabpage.c',
 | 
					            'tabpage.c',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,6 +12,7 @@
 | 
				
			|||||||
#include "nvim/api/private/defs.h"
 | 
					#include "nvim/api/private/defs.h"
 | 
				
			||||||
#include "nvim/api/private/helpers.h"
 | 
					#include "nvim/api/private/helpers.h"
 | 
				
			||||||
#include "nvim/api/vim.h"
 | 
					#include "nvim/api/vim.h"
 | 
				
			||||||
 | 
					#include "nvim/api/vimscript.h"
 | 
				
			||||||
#include "nvim/extmark.h"
 | 
					#include "nvim/extmark.h"
 | 
				
			||||||
#include "nvim/lua/executor.h"
 | 
					#include "nvim/lua/executor.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,6 +15,7 @@
 | 
				
			|||||||
#include "nvim/api/tabpage.h"
 | 
					#include "nvim/api/tabpage.h"
 | 
				
			||||||
#include "nvim/api/ui.h"
 | 
					#include "nvim/api/ui.h"
 | 
				
			||||||
#include "nvim/api/vim.h"
 | 
					#include "nvim/api/vim.h"
 | 
				
			||||||
 | 
					#include "nvim/api/vimscript.h"
 | 
				
			||||||
#include "nvim/api/win_config.h"
 | 
					#include "nvim/api/win_config.h"
 | 
				
			||||||
#include "nvim/api/window.h"
 | 
					#include "nvim/api/window.h"
 | 
				
			||||||
#include "nvim/log.h"
 | 
					#include "nvim/log.h"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -60,84 +60,6 @@
 | 
				
			|||||||
# include "api/vim.c.generated.h"
 | 
					# include "api/vim.c.generated.h"
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Executes Vimscript (multiline block of Ex-commands), like anonymous
 | 
					 | 
				
			||||||
/// |:source|.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// Unlike |nvim_command()| this function supports heredocs, script-scope (s:),
 | 
					 | 
				
			||||||
/// etc.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// On execution error: fails with VimL error, does not update v:errmsg.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// @see |execute()|
 | 
					 | 
				
			||||||
/// @see |nvim_command()|
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// @param src      Vimscript code
 | 
					 | 
				
			||||||
/// @param output   Capture and return all (non-error, non-shell |:!|) output
 | 
					 | 
				
			||||||
/// @param[out] err Error details (Vim error), if any
 | 
					 | 
				
			||||||
/// @return Output (non-error, non-shell |:!|) if `output` is true,
 | 
					 | 
				
			||||||
///         else empty string.
 | 
					 | 
				
			||||||
String nvim_exec(String src, Boolean output, Error *err)
 | 
					 | 
				
			||||||
  FUNC_API_SINCE(7)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  const int save_msg_silent = msg_silent;
 | 
					 | 
				
			||||||
  garray_T *const save_capture_ga = capture_ga;
 | 
					 | 
				
			||||||
  garray_T capture_local;
 | 
					 | 
				
			||||||
  if (output) {
 | 
					 | 
				
			||||||
    ga_init(&capture_local, 1, 80);
 | 
					 | 
				
			||||||
    capture_ga = &capture_local;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  try_start();
 | 
					 | 
				
			||||||
  if (output) {
 | 
					 | 
				
			||||||
    msg_silent++;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  do_source_str(src.data, "nvim_exec()");
 | 
					 | 
				
			||||||
  if (output) {
 | 
					 | 
				
			||||||
    capture_ga = save_capture_ga;
 | 
					 | 
				
			||||||
    msg_silent = save_msg_silent;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  try_end(err);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (ERROR_SET(err)) {
 | 
					 | 
				
			||||||
    goto theend;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (output && capture_local.ga_len > 1) {
 | 
					 | 
				
			||||||
    String s = (String){
 | 
					 | 
				
			||||||
      .data = capture_local.ga_data,
 | 
					 | 
				
			||||||
      .size = (size_t)capture_local.ga_len,
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    // redir usually (except :echon) prepends a newline.
 | 
					 | 
				
			||||||
    if (s.data[0] == '\n') {
 | 
					 | 
				
			||||||
      memmove(s.data, s.data + 1, s.size - 1);
 | 
					 | 
				
			||||||
      s.data[s.size - 1] = '\0';
 | 
					 | 
				
			||||||
      s.size = s.size - 1;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return s;  // Caller will free the memory.
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
theend:
 | 
					 | 
				
			||||||
  if (output) {
 | 
					 | 
				
			||||||
    ga_clear(&capture_local);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  return (String)STRING_INIT;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Executes an ex-command.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// On execution error: fails with VimL error, does not update v:errmsg.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// @see |nvim_exec()|
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// @param command  Ex-command string
 | 
					 | 
				
			||||||
/// @param[out] err Error details (Vim error), if any
 | 
					 | 
				
			||||||
void nvim_command(String command, Error *err)
 | 
					 | 
				
			||||||
  FUNC_API_SINCE(1)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  try_start();
 | 
					 | 
				
			||||||
  do_cmdline_cmd(command.data);
 | 
					 | 
				
			||||||
  try_end(err);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Gets a highlight definition by name.
 | 
					/// Gets a highlight definition by name.
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// @param name Highlight group name
 | 
					/// @param name Highlight group name
 | 
				
			||||||
@@ -478,51 +400,6 @@ String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, Bool
 | 
				
			|||||||
  return cstr_as_string(ptr);
 | 
					  return cstr_as_string(ptr);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Evaluates a VimL |expression|.
 | 
					 | 
				
			||||||
/// Dictionaries and Lists are recursively expanded.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// On execution error: fails with VimL error, does not update v:errmsg.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// @param expr     VimL expression string
 | 
					 | 
				
			||||||
/// @param[out] err Error details, if any
 | 
					 | 
				
			||||||
/// @return         Evaluation result or expanded object
 | 
					 | 
				
			||||||
Object nvim_eval(String expr, Error *err)
 | 
					 | 
				
			||||||
  FUNC_API_SINCE(1)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  static int recursive = 0;  // recursion depth
 | 
					 | 
				
			||||||
  Object rv = OBJECT_INIT;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  TRY_WRAP({
 | 
					 | 
				
			||||||
    // Initialize `force_abort`  and `suppress_errthrow` at the top level.
 | 
					 | 
				
			||||||
    if (!recursive) {
 | 
					 | 
				
			||||||
      force_abort = false;
 | 
					 | 
				
			||||||
      suppress_errthrow = false;
 | 
					 | 
				
			||||||
      current_exception = NULL;
 | 
					 | 
				
			||||||
      // `did_emsg` is set by emsg(), which cancels execution.
 | 
					 | 
				
			||||||
      did_emsg = false;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    recursive++;
 | 
					 | 
				
			||||||
    try_start();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    typval_T rettv;
 | 
					 | 
				
			||||||
    int ok = eval0((char_u *)expr.data, &rettv, NULL, true);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!try_end(err)) {
 | 
					 | 
				
			||||||
      if (ok == FAIL) {
 | 
					 | 
				
			||||||
        // Should never happen, try_end() should get the error. #8371
 | 
					 | 
				
			||||||
        api_set_error(err, kErrorTypeException,
 | 
					 | 
				
			||||||
                      "Failed to evaluate expression: '%.*s'", 256, expr.data);
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        rv = vim_to_object(&rettv);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    tv_clear(&rettv);
 | 
					 | 
				
			||||||
    recursive--;
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return rv;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Execute Lua code. Parameters (if any) are available as `...` inside the
 | 
					/// Execute Lua code. Parameters (if any) are available as `...` inside the
 | 
				
			||||||
/// chunk. The chunk can return a value.
 | 
					/// chunk. The chunk can return a value.
 | 
				
			||||||
@@ -563,164 +440,6 @@ Object nvim_notify(String msg, Integer log_level, Dictionary opts, Error *err)
 | 
				
			|||||||
  return nlua_exec(STATIC_CSTR_AS_STRING("return vim.notify(...)"), args, err);
 | 
					  return nlua_exec(STATIC_CSTR_AS_STRING("return vim.notify(...)"), args, err);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Calls a VimL function.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// @param fn Function name
 | 
					 | 
				
			||||||
/// @param args Function arguments
 | 
					 | 
				
			||||||
/// @param self `self` dict, or NULL for non-dict functions
 | 
					 | 
				
			||||||
/// @param[out] err Error details, if any
 | 
					 | 
				
			||||||
/// @return Result of the function call
 | 
					 | 
				
			||||||
static Object _call_function(String fn, Array args, dict_T *self, Error *err)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  static int recursive = 0;  // recursion depth
 | 
					 | 
				
			||||||
  Object rv = OBJECT_INIT;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (args.size > MAX_FUNC_ARGS) {
 | 
					 | 
				
			||||||
    api_set_error(err, kErrorTypeValidation,
 | 
					 | 
				
			||||||
                  "Function called with too many arguments");
 | 
					 | 
				
			||||||
    return rv;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Convert the arguments in args from Object to typval_T values
 | 
					 | 
				
			||||||
  typval_T vim_args[MAX_FUNC_ARGS + 1];
 | 
					 | 
				
			||||||
  size_t i = 0;  // also used for freeing the variables
 | 
					 | 
				
			||||||
  for (; i < args.size; i++) {
 | 
					 | 
				
			||||||
    if (!object_to_vim(args.items[i], &vim_args[i], err)) {
 | 
					 | 
				
			||||||
      goto free_vim_args;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  TRY_WRAP({
 | 
					 | 
				
			||||||
    // Initialize `force_abort`  and `suppress_errthrow` at the top level.
 | 
					 | 
				
			||||||
    if (!recursive) {
 | 
					 | 
				
			||||||
      force_abort = false;
 | 
					 | 
				
			||||||
      suppress_errthrow = false;
 | 
					 | 
				
			||||||
      current_exception = NULL;
 | 
					 | 
				
			||||||
      // `did_emsg` is set by emsg(), which cancels execution.
 | 
					 | 
				
			||||||
      did_emsg = false;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    recursive++;
 | 
					 | 
				
			||||||
    try_start();
 | 
					 | 
				
			||||||
    typval_T rettv;
 | 
					 | 
				
			||||||
    funcexe_T funcexe = FUNCEXE_INIT;
 | 
					 | 
				
			||||||
    funcexe.firstline = curwin->w_cursor.lnum;
 | 
					 | 
				
			||||||
    funcexe.lastline = curwin->w_cursor.lnum;
 | 
					 | 
				
			||||||
    funcexe.evaluate = true;
 | 
					 | 
				
			||||||
    funcexe.selfdict = self;
 | 
					 | 
				
			||||||
    // call_func() retval is deceptive, ignore it.  Instead we set `msg_list`
 | 
					 | 
				
			||||||
    // (see above) to capture abort-causing non-exception errors.
 | 
					 | 
				
			||||||
    (void)call_func((char_u *)fn.data, (int)fn.size, &rettv, (int)args.size,
 | 
					 | 
				
			||||||
                    vim_args, &funcexe);
 | 
					 | 
				
			||||||
    if (!try_end(err)) {
 | 
					 | 
				
			||||||
      rv = vim_to_object(&rettv);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    tv_clear(&rettv);
 | 
					 | 
				
			||||||
    recursive--;
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
free_vim_args:
 | 
					 | 
				
			||||||
  while (i > 0) {
 | 
					 | 
				
			||||||
    tv_clear(&vim_args[--i]);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return rv;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Calls a VimL function with the given arguments.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// On execution error: fails with VimL error, does not update v:errmsg.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// @param fn       Function to call
 | 
					 | 
				
			||||||
/// @param args     Function arguments packed in an Array
 | 
					 | 
				
			||||||
/// @param[out] err Error details, if any
 | 
					 | 
				
			||||||
/// @return Result of the function call
 | 
					 | 
				
			||||||
Object nvim_call_function(String fn, Array args, Error *err)
 | 
					 | 
				
			||||||
  FUNC_API_SINCE(1)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  return _call_function(fn, args, NULL, err);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Calls a VimL |Dictionary-function| with the given arguments.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// On execution error: fails with VimL error, does not update v:errmsg.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// @param dict Dictionary, or String evaluating to a VimL |self| dict
 | 
					 | 
				
			||||||
/// @param fn Name of the function defined on the VimL dict
 | 
					 | 
				
			||||||
/// @param args Function arguments packed in an Array
 | 
					 | 
				
			||||||
/// @param[out] err Error details, if any
 | 
					 | 
				
			||||||
/// @return Result of the function call
 | 
					 | 
				
			||||||
Object nvim_call_dict_function(Object dict, String fn, Array args, Error *err)
 | 
					 | 
				
			||||||
  FUNC_API_SINCE(4)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  Object rv = OBJECT_INIT;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  typval_T rettv;
 | 
					 | 
				
			||||||
  bool mustfree = false;
 | 
					 | 
				
			||||||
  switch (dict.type) {
 | 
					 | 
				
			||||||
  case kObjectTypeString:
 | 
					 | 
				
			||||||
    try_start();
 | 
					 | 
				
			||||||
    if (eval0((char_u *)dict.data.string.data, &rettv, NULL, true) == FAIL) {
 | 
					 | 
				
			||||||
      api_set_error(err, kErrorTypeException,
 | 
					 | 
				
			||||||
                    "Failed to evaluate dict expression");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (try_end(err)) {
 | 
					 | 
				
			||||||
      return rv;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    // Evaluation of the string arg created a new dict or increased the
 | 
					 | 
				
			||||||
    // refcount of a dict. Not necessary for a RPC dict.
 | 
					 | 
				
			||||||
    mustfree = true;
 | 
					 | 
				
			||||||
    break;
 | 
					 | 
				
			||||||
  case kObjectTypeDictionary:
 | 
					 | 
				
			||||||
    if (!object_to_vim(dict, &rettv, err)) {
 | 
					 | 
				
			||||||
      goto end;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    break;
 | 
					 | 
				
			||||||
  default:
 | 
					 | 
				
			||||||
    api_set_error(err, kErrorTypeValidation,
 | 
					 | 
				
			||||||
                  "dict argument type must be String or Dictionary");
 | 
					 | 
				
			||||||
    return rv;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  dict_T *self_dict = rettv.vval.v_dict;
 | 
					 | 
				
			||||||
  if (rettv.v_type != VAR_DICT || !self_dict) {
 | 
					 | 
				
			||||||
    api_set_error(err, kErrorTypeValidation, "dict not found");
 | 
					 | 
				
			||||||
    goto end;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (fn.data && fn.size > 0 && dict.type != kObjectTypeDictionary) {
 | 
					 | 
				
			||||||
    dictitem_T *const di = tv_dict_find(self_dict, fn.data, (ptrdiff_t)fn.size);
 | 
					 | 
				
			||||||
    if (di == NULL) {
 | 
					 | 
				
			||||||
      api_set_error(err, kErrorTypeValidation, "Not found: %s", fn.data);
 | 
					 | 
				
			||||||
      goto end;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (di->di_tv.v_type == VAR_PARTIAL) {
 | 
					 | 
				
			||||||
      api_set_error(err, kErrorTypeValidation,
 | 
					 | 
				
			||||||
                    "partial function not supported");
 | 
					 | 
				
			||||||
      goto end;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (di->di_tv.v_type != VAR_FUNC) {
 | 
					 | 
				
			||||||
      api_set_error(err, kErrorTypeValidation, "Not a function: %s", fn.data);
 | 
					 | 
				
			||||||
      goto end;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    fn = (String) {
 | 
					 | 
				
			||||||
      .data = (char *)di->di_tv.vval.v_string,
 | 
					 | 
				
			||||||
      .size = STRLEN(di->di_tv.vval.v_string),
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (!fn.data || fn.size < 1) {
 | 
					 | 
				
			||||||
    api_set_error(err, kErrorTypeValidation, "Invalid (empty) function name");
 | 
					 | 
				
			||||||
    goto end;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  rv = _call_function(fn, args, self_dict, err);
 | 
					 | 
				
			||||||
end:
 | 
					 | 
				
			||||||
  if (mustfree) {
 | 
					 | 
				
			||||||
    tv_clear(&rettv);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return rv;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Calculates the number of display cells occupied by `text`.
 | 
					/// Calculates the number of display cells occupied by `text`.
 | 
				
			||||||
/// <Tab> counts as one cell.
 | 
					/// <Tab> counts as one cell.
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
@@ -1991,439 +1710,6 @@ theend:
 | 
				
			|||||||
  return rv;
 | 
					  return rv;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
  ExprASTNode **node_p;
 | 
					 | 
				
			||||||
  Object *ret_node_p;
 | 
					 | 
				
			||||||
} ExprASTConvStackItem;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// @cond DOXYGEN_NOT_A_FUNCTION
 | 
					 | 
				
			||||||
typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack;
 | 
					 | 
				
			||||||
/// @endcond
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Parse a VimL expression.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// @param[in]  expr  Expression to parse. Always treated as a single line.
 | 
					 | 
				
			||||||
/// @param[in]  flags Flags:
 | 
					 | 
				
			||||||
///                    - "m" if multiple expressions in a row are allowed (only
 | 
					 | 
				
			||||||
///                      the first one will be parsed),
 | 
					 | 
				
			||||||
///                    - "E" if EOC tokens are not allowed (determines whether
 | 
					 | 
				
			||||||
///                      they will stop parsing process or be recognized as an
 | 
					 | 
				
			||||||
///                      operator/space, though also yielding an error).
 | 
					 | 
				
			||||||
///                    - "l" when needing to start parsing with lvalues for
 | 
					 | 
				
			||||||
///                      ":let" or ":for".
 | 
					 | 
				
			||||||
///                    Common flag sets:
 | 
					 | 
				
			||||||
///                    - "m" to parse like for ":echo".
 | 
					 | 
				
			||||||
///                    - "E" to parse like for "<C-r>=".
 | 
					 | 
				
			||||||
///                    - empty string for ":call".
 | 
					 | 
				
			||||||
///                    - "lm" to parse for ":let".
 | 
					 | 
				
			||||||
/// @param[in]  highlight  If true, return value will also include "highlight"
 | 
					 | 
				
			||||||
///                        key containing array of 4-tuples (arrays) (Integer,
 | 
					 | 
				
			||||||
///                        Integer, Integer, String), where first three numbers
 | 
					 | 
				
			||||||
///                        define the highlighted region and represent line,
 | 
					 | 
				
			||||||
///                        starting column and ending column (latter exclusive:
 | 
					 | 
				
			||||||
///                        one should highlight region [start_col, end_col)).
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// @return
 | 
					 | 
				
			||||||
///      - AST: top-level dictionary with these keys:
 | 
					 | 
				
			||||||
///        - "error": Dictionary with error, present only if parser saw some
 | 
					 | 
				
			||||||
///                 error. Contains the following keys:
 | 
					 | 
				
			||||||
///          - "message": String, error message in printf format, translated.
 | 
					 | 
				
			||||||
///                       Must contain exactly one "%.*s".
 | 
					 | 
				
			||||||
///          - "arg": String, error message argument.
 | 
					 | 
				
			||||||
///        - "len": Amount of bytes successfully parsed. With flags equal to ""
 | 
					 | 
				
			||||||
///                 that should be equal to the length of expr string.
 | 
					 | 
				
			||||||
///                 (“Successfully parsed” here means “participated in AST
 | 
					 | 
				
			||||||
///                  creation”, not “till the first error”.)
 | 
					 | 
				
			||||||
///        - "ast": AST, either nil or a dictionary with these keys:
 | 
					 | 
				
			||||||
///          - "type": node type, one of the value names from ExprASTNodeType
 | 
					 | 
				
			||||||
///                    stringified without "kExprNode" prefix.
 | 
					 | 
				
			||||||
///          - "start": a pair [line, column] describing where node is "started"
 | 
					 | 
				
			||||||
///                     where "line" is always 0 (will not be 0 if you will be
 | 
					 | 
				
			||||||
///                     using nvim_parse_viml() on e.g. ":let", but that is not
 | 
					 | 
				
			||||||
///                     present yet). Both elements are Integers.
 | 
					 | 
				
			||||||
///          - "len": “length” of the node. This and "start" are there for
 | 
					 | 
				
			||||||
///                   debugging purposes primary (debugging parser and providing
 | 
					 | 
				
			||||||
///                   debug information).
 | 
					 | 
				
			||||||
///          - "children": a list of nodes described in top/"ast". There always
 | 
					 | 
				
			||||||
///                        is zero, one or two children, key will not be present
 | 
					 | 
				
			||||||
///                        if node has no children. Maximum number of children
 | 
					 | 
				
			||||||
///                        may be found in node_maxchildren array.
 | 
					 | 
				
			||||||
///      - Local values (present only for certain nodes):
 | 
					 | 
				
			||||||
///        - "scope": a single Integer, specifies scope for "Option" and
 | 
					 | 
				
			||||||
///                   "PlainIdentifier" nodes. For "Option" it is one of
 | 
					 | 
				
			||||||
///                   ExprOptScope values, for "PlainIdentifier" it is one of
 | 
					 | 
				
			||||||
///                   ExprVarScope values.
 | 
					 | 
				
			||||||
///        - "ident": identifier (without scope, if any), present for "Option",
 | 
					 | 
				
			||||||
///                   "PlainIdentifier", "PlainKey" and "Environment" nodes.
 | 
					 | 
				
			||||||
///        - "name": Integer, register name (one character) or -1. Only present
 | 
					 | 
				
			||||||
///                for "Register" nodes.
 | 
					 | 
				
			||||||
///        - "cmp_type": String, comparison type, one of the value names from
 | 
					 | 
				
			||||||
///                      ExprComparisonType, stringified without "kExprCmp"
 | 
					 | 
				
			||||||
///                      prefix. Only present for "Comparison" nodes.
 | 
					 | 
				
			||||||
///        - "ccs_strategy": String, case comparison strategy, one of the
 | 
					 | 
				
			||||||
///                          value names from ExprCaseCompareStrategy,
 | 
					 | 
				
			||||||
///                          stringified without "kCCStrategy" prefix. Only
 | 
					 | 
				
			||||||
///                          present for "Comparison" nodes.
 | 
					 | 
				
			||||||
///        - "augmentation": String, augmentation type for "Assignment" nodes.
 | 
					 | 
				
			||||||
///                          Is either an empty string, "Add", "Subtract" or
 | 
					 | 
				
			||||||
///                          "Concat" for "=", "+=", "-=" or ".=" respectively.
 | 
					 | 
				
			||||||
///        - "invert": Boolean, true if result of comparison needs to be
 | 
					 | 
				
			||||||
///                    inverted. Only present for "Comparison" nodes.
 | 
					 | 
				
			||||||
///        - "ivalue": Integer, integer value for "Integer" nodes.
 | 
					 | 
				
			||||||
///        - "fvalue": Float, floating-point value for "Float" nodes.
 | 
					 | 
				
			||||||
///        - "svalue": String, value for "SingleQuotedString" and
 | 
					 | 
				
			||||||
///                    "DoubleQuotedString" nodes.
 | 
					 | 
				
			||||||
/// @param[out] err Error details, if any
 | 
					 | 
				
			||||||
Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, Error *err)
 | 
					 | 
				
			||||||
  FUNC_API_SINCE(4) FUNC_API_FAST
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  int pflags = 0;
 | 
					 | 
				
			||||||
  for (size_t i = 0 ; i < flags.size ; i++) {
 | 
					 | 
				
			||||||
    switch (flags.data[i]) {
 | 
					 | 
				
			||||||
    case 'm':
 | 
					 | 
				
			||||||
      pflags |= kExprFlagsMulti; break;
 | 
					 | 
				
			||||||
    case 'E':
 | 
					 | 
				
			||||||
      pflags |= kExprFlagsDisallowEOC; break;
 | 
					 | 
				
			||||||
    case 'l':
 | 
					 | 
				
			||||||
      pflags |= kExprFlagsParseLet; break;
 | 
					 | 
				
			||||||
    case NUL:
 | 
					 | 
				
			||||||
      api_set_error(err, kErrorTypeValidation, "Invalid flag: '\\0' (%u)",
 | 
					 | 
				
			||||||
                    (unsigned)flags.data[i]);
 | 
					 | 
				
			||||||
      return (Dictionary)ARRAY_DICT_INIT;
 | 
					 | 
				
			||||||
    default:
 | 
					 | 
				
			||||||
      api_set_error(err, kErrorTypeValidation, "Invalid flag: '%c' (%u)",
 | 
					 | 
				
			||||||
                    flags.data[i], (unsigned)flags.data[i]);
 | 
					 | 
				
			||||||
      return (Dictionary)ARRAY_DICT_INIT;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  ParserLine parser_lines[] = {
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      .data = expr.data,
 | 
					 | 
				
			||||||
      .size = expr.size,
 | 
					 | 
				
			||||||
      .allocated = false,
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    { NULL, 0, false },
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  ParserLine *plines_p = parser_lines;
 | 
					 | 
				
			||||||
  ParserHighlight colors;
 | 
					 | 
				
			||||||
  kvi_init(colors);
 | 
					 | 
				
			||||||
  ParserHighlight *const colors_p = (highlight ? &colors : NULL);
 | 
					 | 
				
			||||||
  ParserState pstate;
 | 
					 | 
				
			||||||
  viml_parser_init(&pstate, parser_simple_get_line, &plines_p, colors_p);
 | 
					 | 
				
			||||||
  ExprAST east = viml_pexpr_parse(&pstate, pflags);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const size_t ret_size = (
 | 
					 | 
				
			||||||
                           2  // "ast", "len"
 | 
					 | 
				
			||||||
                           + (size_t)(east.err.msg != NULL)  // "error"
 | 
					 | 
				
			||||||
                           + (size_t)highlight  // "highlight"
 | 
					 | 
				
			||||||
                           + 0);
 | 
					 | 
				
			||||||
  Dictionary ret = {
 | 
					 | 
				
			||||||
    .items = xmalloc(ret_size * sizeof(ret.items[0])),
 | 
					 | 
				
			||||||
    .size = 0,
 | 
					 | 
				
			||||||
    .capacity = ret_size,
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  ret.items[ret.size++] = (KeyValuePair) {
 | 
					 | 
				
			||||||
    .key = STATIC_CSTR_TO_STRING("ast"),
 | 
					 | 
				
			||||||
    .value = NIL,
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  ret.items[ret.size++] = (KeyValuePair) {
 | 
					 | 
				
			||||||
    .key = STATIC_CSTR_TO_STRING("len"),
 | 
					 | 
				
			||||||
    .value = INTEGER_OBJ((Integer)(pstate.pos.line == 1
 | 
					 | 
				
			||||||
                                   ? parser_lines[0].size
 | 
					 | 
				
			||||||
                                   : pstate.pos.col)),
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  if (east.err.msg != NULL) {
 | 
					 | 
				
			||||||
    Dictionary err_dict = {
 | 
					 | 
				
			||||||
      .items = xmalloc(2 * sizeof(err_dict.items[0])),
 | 
					 | 
				
			||||||
      .size = 2,
 | 
					 | 
				
			||||||
      .capacity = 2,
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    err_dict.items[0] = (KeyValuePair) {
 | 
					 | 
				
			||||||
      .key = STATIC_CSTR_TO_STRING("message"),
 | 
					 | 
				
			||||||
      .value = STRING_OBJ(cstr_to_string(east.err.msg)),
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    if (east.err.arg == NULL) {
 | 
					 | 
				
			||||||
      err_dict.items[1] = (KeyValuePair) {
 | 
					 | 
				
			||||||
        .key = STATIC_CSTR_TO_STRING("arg"),
 | 
					 | 
				
			||||||
        .value = STRING_OBJ(STRING_INIT),
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      err_dict.items[1] = (KeyValuePair) {
 | 
					 | 
				
			||||||
        .key = STATIC_CSTR_TO_STRING("arg"),
 | 
					 | 
				
			||||||
        .value = STRING_OBJ(((String) {
 | 
					 | 
				
			||||||
          .data = xmemdupz(east.err.arg, (size_t)east.err.arg_len),
 | 
					 | 
				
			||||||
          .size = (size_t)east.err.arg_len,
 | 
					 | 
				
			||||||
        })),
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    ret.items[ret.size++] = (KeyValuePair) {
 | 
					 | 
				
			||||||
      .key = STATIC_CSTR_TO_STRING("error"),
 | 
					 | 
				
			||||||
      .value = DICTIONARY_OBJ(err_dict),
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  if (highlight) {
 | 
					 | 
				
			||||||
    Array hl = (Array) {
 | 
					 | 
				
			||||||
      .items = xmalloc(kv_size(colors) * sizeof(hl.items[0])),
 | 
					 | 
				
			||||||
      .capacity = kv_size(colors),
 | 
					 | 
				
			||||||
      .size = kv_size(colors),
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    for (size_t i = 0 ; i < kv_size(colors) ; i++) {
 | 
					 | 
				
			||||||
      const ParserHighlightChunk chunk = kv_A(colors, i);
 | 
					 | 
				
			||||||
      Array chunk_arr = (Array) {
 | 
					 | 
				
			||||||
        .items = xmalloc(4 * sizeof(chunk_arr.items[0])),
 | 
					 | 
				
			||||||
        .capacity = 4,
 | 
					 | 
				
			||||||
        .size = 4,
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
      chunk_arr.items[0] = INTEGER_OBJ((Integer)chunk.start.line);
 | 
					 | 
				
			||||||
      chunk_arr.items[1] = INTEGER_OBJ((Integer)chunk.start.col);
 | 
					 | 
				
			||||||
      chunk_arr.items[2] = INTEGER_OBJ((Integer)chunk.end_col);
 | 
					 | 
				
			||||||
      chunk_arr.items[3] = STRING_OBJ(cstr_to_string(chunk.group));
 | 
					 | 
				
			||||||
      hl.items[i] = ARRAY_OBJ(chunk_arr);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    ret.items[ret.size++] = (KeyValuePair) {
 | 
					 | 
				
			||||||
      .key = STATIC_CSTR_TO_STRING("highlight"),
 | 
					 | 
				
			||||||
      .value = ARRAY_OBJ(hl),
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  kvi_destroy(colors);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Walk over the AST, freeing nodes in process.
 | 
					 | 
				
			||||||
  ExprASTConvStack ast_conv_stack;
 | 
					 | 
				
			||||||
  kvi_init(ast_conv_stack);
 | 
					 | 
				
			||||||
  kvi_push(ast_conv_stack, ((ExprASTConvStackItem) {
 | 
					 | 
				
			||||||
    .node_p = &east.root,
 | 
					 | 
				
			||||||
    .ret_node_p = &ret.items[0].value,
 | 
					 | 
				
			||||||
  }));
 | 
					 | 
				
			||||||
  while (kv_size(ast_conv_stack)) {
 | 
					 | 
				
			||||||
    ExprASTConvStackItem cur_item = kv_last(ast_conv_stack);
 | 
					 | 
				
			||||||
    ExprASTNode *const node = *cur_item.node_p;
 | 
					 | 
				
			||||||
    if (node == NULL) {
 | 
					 | 
				
			||||||
      assert(kv_size(ast_conv_stack) == 1);
 | 
					 | 
				
			||||||
      kv_drop(ast_conv_stack, 1);
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      if (cur_item.ret_node_p->type == kObjectTypeNil) {
 | 
					 | 
				
			||||||
        const size_t ret_node_items_size = (size_t)(
 | 
					 | 
				
			||||||
                                                    3  // "type", "start" and "len"
 | 
					 | 
				
			||||||
                                                    + (node->children != NULL)  // "children"
 | 
					 | 
				
			||||||
                                                    + (node->type == kExprNodeOption
 | 
					 | 
				
			||||||
                                                       || node->type == kExprNodePlainIdentifier)  // "scope"
 | 
					 | 
				
			||||||
                                                    + (node->type == kExprNodeOption
 | 
					 | 
				
			||||||
                                                       || node->type == kExprNodePlainIdentifier
 | 
					 | 
				
			||||||
                                                       || node->type == kExprNodePlainKey
 | 
					 | 
				
			||||||
                                                       || node->type == kExprNodeEnvironment)  // "ident"
 | 
					 | 
				
			||||||
                                                    + (node->type == kExprNodeRegister)  // "name"
 | 
					 | 
				
			||||||
                                                    + (3  // "cmp_type", "ccs_strategy", "invert"
 | 
					 | 
				
			||||||
                                                       * (node->type == kExprNodeComparison))
 | 
					 | 
				
			||||||
                                                    + (node->type == kExprNodeInteger)  // "ivalue"
 | 
					 | 
				
			||||||
                                                    + (node->type == kExprNodeFloat)  // "fvalue"
 | 
					 | 
				
			||||||
                                                    + (node->type == kExprNodeDoubleQuotedString
 | 
					 | 
				
			||||||
                                                       || node->type == kExprNodeSingleQuotedString)  // "svalue"
 | 
					 | 
				
			||||||
                                                    + (node->type == kExprNodeAssignment)  // "augmentation"
 | 
					 | 
				
			||||||
                                                    + 0);
 | 
					 | 
				
			||||||
        Dictionary ret_node = {
 | 
					 | 
				
			||||||
          .items = xmalloc(ret_node_items_size * sizeof(ret_node.items[0])),
 | 
					 | 
				
			||||||
          .capacity = ret_node_items_size,
 | 
					 | 
				
			||||||
          .size = 0,
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        *cur_item.ret_node_p = DICTIONARY_OBJ(ret_node);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      Dictionary *ret_node = &cur_item.ret_node_p->data.dictionary;
 | 
					 | 
				
			||||||
      if (node->children != NULL) {
 | 
					 | 
				
			||||||
        const size_t num_children = 1 + (node->children->next != NULL);
 | 
					 | 
				
			||||||
        Array children_array = {
 | 
					 | 
				
			||||||
          .items = xmalloc(num_children * sizeof(children_array.items[0])),
 | 
					 | 
				
			||||||
          .capacity = num_children,
 | 
					 | 
				
			||||||
          .size = num_children,
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        for (size_t i = 0; i < num_children; i++) {
 | 
					 | 
				
			||||||
          children_array.items[i] = NIL;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
					 | 
				
			||||||
          .key = STATIC_CSTR_TO_STRING("children"),
 | 
					 | 
				
			||||||
          .value = ARRAY_OBJ(children_array),
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        kvi_push(ast_conv_stack, ((ExprASTConvStackItem) {
 | 
					 | 
				
			||||||
          .node_p = &node->children,
 | 
					 | 
				
			||||||
          .ret_node_p = &children_array.items[0],
 | 
					 | 
				
			||||||
        }));
 | 
					 | 
				
			||||||
      } else if (node->next != NULL) {
 | 
					 | 
				
			||||||
        kvi_push(ast_conv_stack, ((ExprASTConvStackItem) {
 | 
					 | 
				
			||||||
          .node_p = &node->next,
 | 
					 | 
				
			||||||
          .ret_node_p = cur_item.ret_node_p + 1,
 | 
					 | 
				
			||||||
        }));
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        kv_drop(ast_conv_stack, 1);
 | 
					 | 
				
			||||||
        ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
					 | 
				
			||||||
          .key = STATIC_CSTR_TO_STRING("type"),
 | 
					 | 
				
			||||||
          .value = STRING_OBJ(cstr_to_string(east_node_type_tab[node->type])),
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        Array start_array = {
 | 
					 | 
				
			||||||
          .items = xmalloc(2 * sizeof(start_array.items[0])),
 | 
					 | 
				
			||||||
          .capacity = 2,
 | 
					 | 
				
			||||||
          .size = 2,
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        start_array.items[0] = INTEGER_OBJ((Integer)node->start.line);
 | 
					 | 
				
			||||||
        start_array.items[1] = INTEGER_OBJ((Integer)node->start.col);
 | 
					 | 
				
			||||||
        ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
					 | 
				
			||||||
          .key = STATIC_CSTR_TO_STRING("start"),
 | 
					 | 
				
			||||||
          .value = ARRAY_OBJ(start_array),
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
					 | 
				
			||||||
          .key = STATIC_CSTR_TO_STRING("len"),
 | 
					 | 
				
			||||||
          .value = INTEGER_OBJ((Integer)node->len),
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        switch (node->type) {
 | 
					 | 
				
			||||||
        case kExprNodeDoubleQuotedString:
 | 
					 | 
				
			||||||
        case kExprNodeSingleQuotedString:
 | 
					 | 
				
			||||||
          ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
					 | 
				
			||||||
            .key = STATIC_CSTR_TO_STRING("svalue"),
 | 
					 | 
				
			||||||
            .value = STRING_OBJ(((String) {
 | 
					 | 
				
			||||||
              .data = node->data.str.value,
 | 
					 | 
				
			||||||
              .size = node->data.str.size,
 | 
					 | 
				
			||||||
            })),
 | 
					 | 
				
			||||||
          };
 | 
					 | 
				
			||||||
          break;
 | 
					 | 
				
			||||||
        case kExprNodeOption:
 | 
					 | 
				
			||||||
          ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
					 | 
				
			||||||
            .key = STATIC_CSTR_TO_STRING("scope"),
 | 
					 | 
				
			||||||
            .value = INTEGER_OBJ(node->data.opt.scope),
 | 
					 | 
				
			||||||
          };
 | 
					 | 
				
			||||||
          ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
					 | 
				
			||||||
            .key = STATIC_CSTR_TO_STRING("ident"),
 | 
					 | 
				
			||||||
            .value = STRING_OBJ(((String) {
 | 
					 | 
				
			||||||
              .data = xmemdupz(node->data.opt.ident,
 | 
					 | 
				
			||||||
                               node->data.opt.ident_len),
 | 
					 | 
				
			||||||
              .size = node->data.opt.ident_len,
 | 
					 | 
				
			||||||
            })),
 | 
					 | 
				
			||||||
          };
 | 
					 | 
				
			||||||
          break;
 | 
					 | 
				
			||||||
        case kExprNodePlainIdentifier:
 | 
					 | 
				
			||||||
          ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
					 | 
				
			||||||
            .key = STATIC_CSTR_TO_STRING("scope"),
 | 
					 | 
				
			||||||
            .value = INTEGER_OBJ(node->data.var.scope),
 | 
					 | 
				
			||||||
          };
 | 
					 | 
				
			||||||
          ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
					 | 
				
			||||||
            .key = STATIC_CSTR_TO_STRING("ident"),
 | 
					 | 
				
			||||||
            .value = STRING_OBJ(((String) {
 | 
					 | 
				
			||||||
              .data = xmemdupz(node->data.var.ident,
 | 
					 | 
				
			||||||
                               node->data.var.ident_len),
 | 
					 | 
				
			||||||
              .size = node->data.var.ident_len,
 | 
					 | 
				
			||||||
            })),
 | 
					 | 
				
			||||||
          };
 | 
					 | 
				
			||||||
          break;
 | 
					 | 
				
			||||||
        case kExprNodePlainKey:
 | 
					 | 
				
			||||||
          ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
					 | 
				
			||||||
            .key = STATIC_CSTR_TO_STRING("ident"),
 | 
					 | 
				
			||||||
            .value = STRING_OBJ(((String) {
 | 
					 | 
				
			||||||
              .data = xmemdupz(node->data.var.ident,
 | 
					 | 
				
			||||||
                               node->data.var.ident_len),
 | 
					 | 
				
			||||||
              .size = node->data.var.ident_len,
 | 
					 | 
				
			||||||
            })),
 | 
					 | 
				
			||||||
          };
 | 
					 | 
				
			||||||
          break;
 | 
					 | 
				
			||||||
        case kExprNodeEnvironment:
 | 
					 | 
				
			||||||
          ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
					 | 
				
			||||||
            .key = STATIC_CSTR_TO_STRING("ident"),
 | 
					 | 
				
			||||||
            .value = STRING_OBJ(((String) {
 | 
					 | 
				
			||||||
              .data = xmemdupz(node->data.env.ident,
 | 
					 | 
				
			||||||
                               node->data.env.ident_len),
 | 
					 | 
				
			||||||
              .size = node->data.env.ident_len,
 | 
					 | 
				
			||||||
            })),
 | 
					 | 
				
			||||||
          };
 | 
					 | 
				
			||||||
          break;
 | 
					 | 
				
			||||||
        case kExprNodeRegister:
 | 
					 | 
				
			||||||
          ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
					 | 
				
			||||||
            .key = STATIC_CSTR_TO_STRING("name"),
 | 
					 | 
				
			||||||
            .value = INTEGER_OBJ(node->data.reg.name),
 | 
					 | 
				
			||||||
          };
 | 
					 | 
				
			||||||
          break;
 | 
					 | 
				
			||||||
        case kExprNodeComparison:
 | 
					 | 
				
			||||||
          ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
					 | 
				
			||||||
            .key = STATIC_CSTR_TO_STRING("cmp_type"),
 | 
					 | 
				
			||||||
            .value = STRING_OBJ(cstr_to_string(eltkn_cmp_type_tab[node->data.cmp.type])),
 | 
					 | 
				
			||||||
          };
 | 
					 | 
				
			||||||
          ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
					 | 
				
			||||||
            .key = STATIC_CSTR_TO_STRING("ccs_strategy"),
 | 
					 | 
				
			||||||
            .value = STRING_OBJ(cstr_to_string(ccs_tab[node->data.cmp.ccs])),
 | 
					 | 
				
			||||||
          };
 | 
					 | 
				
			||||||
          ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
					 | 
				
			||||||
            .key = STATIC_CSTR_TO_STRING("invert"),
 | 
					 | 
				
			||||||
            .value = BOOLEAN_OBJ(node->data.cmp.inv),
 | 
					 | 
				
			||||||
          };
 | 
					 | 
				
			||||||
          break;
 | 
					 | 
				
			||||||
        case kExprNodeFloat:
 | 
					 | 
				
			||||||
          ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
					 | 
				
			||||||
            .key = STATIC_CSTR_TO_STRING("fvalue"),
 | 
					 | 
				
			||||||
            .value = FLOAT_OBJ(node->data.flt.value),
 | 
					 | 
				
			||||||
          };
 | 
					 | 
				
			||||||
          break;
 | 
					 | 
				
			||||||
        case kExprNodeInteger:
 | 
					 | 
				
			||||||
          ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
					 | 
				
			||||||
            .key = STATIC_CSTR_TO_STRING("ivalue"),
 | 
					 | 
				
			||||||
            .value = INTEGER_OBJ((Integer)(
 | 
					 | 
				
			||||||
                                           node->data.num.value > API_INTEGER_MAX
 | 
					 | 
				
			||||||
                  ? API_INTEGER_MAX
 | 
					 | 
				
			||||||
                  : (Integer)node->data.num.value)),
 | 
					 | 
				
			||||||
          };
 | 
					 | 
				
			||||||
          break;
 | 
					 | 
				
			||||||
        case kExprNodeAssignment: {
 | 
					 | 
				
			||||||
          const ExprAssignmentType asgn_type = node->data.ass.type;
 | 
					 | 
				
			||||||
          ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
					 | 
				
			||||||
            .key = STATIC_CSTR_TO_STRING("augmentation"),
 | 
					 | 
				
			||||||
            .value = STRING_OBJ(asgn_type == kExprAsgnPlain
 | 
					 | 
				
			||||||
                  ? (String)STRING_INIT
 | 
					 | 
				
			||||||
                  : cstr_to_string(expr_asgn_type_tab[asgn_type])),
 | 
					 | 
				
			||||||
          };
 | 
					 | 
				
			||||||
          break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        case kExprNodeMissing:
 | 
					 | 
				
			||||||
        case kExprNodeOpMissing:
 | 
					 | 
				
			||||||
        case kExprNodeTernary:
 | 
					 | 
				
			||||||
        case kExprNodeTernaryValue:
 | 
					 | 
				
			||||||
        case kExprNodeSubscript:
 | 
					 | 
				
			||||||
        case kExprNodeListLiteral:
 | 
					 | 
				
			||||||
        case kExprNodeUnaryPlus:
 | 
					 | 
				
			||||||
        case kExprNodeBinaryPlus:
 | 
					 | 
				
			||||||
        case kExprNodeNested:
 | 
					 | 
				
			||||||
        case kExprNodeCall:
 | 
					 | 
				
			||||||
        case kExprNodeComplexIdentifier:
 | 
					 | 
				
			||||||
        case kExprNodeUnknownFigure:
 | 
					 | 
				
			||||||
        case kExprNodeLambda:
 | 
					 | 
				
			||||||
        case kExprNodeDictLiteral:
 | 
					 | 
				
			||||||
        case kExprNodeCurlyBracesIdentifier:
 | 
					 | 
				
			||||||
        case kExprNodeComma:
 | 
					 | 
				
			||||||
        case kExprNodeColon:
 | 
					 | 
				
			||||||
        case kExprNodeArrow:
 | 
					 | 
				
			||||||
        case kExprNodeConcat:
 | 
					 | 
				
			||||||
        case kExprNodeConcatOrSubscript:
 | 
					 | 
				
			||||||
        case kExprNodeOr:
 | 
					 | 
				
			||||||
        case kExprNodeAnd:
 | 
					 | 
				
			||||||
        case kExprNodeUnaryMinus:
 | 
					 | 
				
			||||||
        case kExprNodeBinaryMinus:
 | 
					 | 
				
			||||||
        case kExprNodeNot:
 | 
					 | 
				
			||||||
        case kExprNodeMultiplication:
 | 
					 | 
				
			||||||
        case kExprNodeDivision:
 | 
					 | 
				
			||||||
        case kExprNodeMod:
 | 
					 | 
				
			||||||
          break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        assert(cur_item.ret_node_p->data.dictionary.size
 | 
					 | 
				
			||||||
               == cur_item.ret_node_p->data.dictionary.capacity);
 | 
					 | 
				
			||||||
        xfree(*cur_item.node_p);
 | 
					 | 
				
			||||||
        *cur_item.node_p = NULL;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  kvi_destroy(ast_conv_stack);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  assert(ret.size == ret.capacity);
 | 
					 | 
				
			||||||
  // Should be a no-op actually, leaving it in case non-nodes will need to be
 | 
					 | 
				
			||||||
  // freed later.
 | 
					 | 
				
			||||||
  viml_pexpr_free_ast(east);
 | 
					 | 
				
			||||||
  viml_parser_destroy(&pstate);
 | 
					 | 
				
			||||||
  return ret;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Writes a message to vim output or error buffer. The string is split
 | 
					/// Writes a message to vim output or error buffer. The string is split
 | 
				
			||||||
/// and flushed after each newline. Incomplete lines are kept for writing
 | 
					/// and flushed after each newline. Incomplete lines are kept for writing
 | 
				
			||||||
/// later.
 | 
					/// later.
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										733
									
								
								src/nvim/api/vimscript.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										733
									
								
								src/nvim/api/vimscript.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,733 @@
 | 
				
			|||||||
 | 
					// This is an open source non-commercial project. Dear PVS-Studio, please check
 | 
				
			||||||
 | 
					// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <assert.h>
 | 
				
			||||||
 | 
					#include <limits.h>
 | 
				
			||||||
 | 
					#include <stdlib.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "nvim/ascii.h"
 | 
				
			||||||
 | 
					#include "nvim/api/vimscript.h"
 | 
				
			||||||
 | 
					#include "nvim/api/private/converter.h"
 | 
				
			||||||
 | 
					#include "nvim/api/private/defs.h"
 | 
				
			||||||
 | 
					#include "nvim/api/private/helpers.h"
 | 
				
			||||||
 | 
					#include "nvim/eval.h"
 | 
				
			||||||
 | 
					#include "nvim/eval/typval.h"
 | 
				
			||||||
 | 
					#include "nvim/eval/userfunc.h"
 | 
				
			||||||
 | 
					#include "nvim/ex_cmds2.h"
 | 
				
			||||||
 | 
					#include "nvim/viml/parser/expressions.h"
 | 
				
			||||||
 | 
					#include "nvim/viml/parser/parser.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef INCLUDE_GENERATED_DECLARATIONS
 | 
				
			||||||
 | 
					# include "api/vimscript.c.generated.h"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Executes Vimscript (multiline block of Ex-commands), like anonymous
 | 
				
			||||||
 | 
					/// |:source|.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// Unlike |nvim_command()| this function supports heredocs, script-scope (s:),
 | 
				
			||||||
 | 
					/// etc.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// On execution error: fails with VimL error, does not update v:errmsg.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @see |execute()|
 | 
				
			||||||
 | 
					/// @see |nvim_command()|
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @param src      Vimscript code
 | 
				
			||||||
 | 
					/// @param output   Capture and return all (non-error, non-shell |:!|) output
 | 
				
			||||||
 | 
					/// @param[out] err Error details (Vim error), if any
 | 
				
			||||||
 | 
					/// @return Output (non-error, non-shell |:!|) if `output` is true,
 | 
				
			||||||
 | 
					///         else empty string.
 | 
				
			||||||
 | 
					String nvim_exec(String src, Boolean output, Error *err)
 | 
				
			||||||
 | 
					  FUNC_API_SINCE(7)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  const int save_msg_silent = msg_silent;
 | 
				
			||||||
 | 
					  garray_T *const save_capture_ga = capture_ga;
 | 
				
			||||||
 | 
					  garray_T capture_local;
 | 
				
			||||||
 | 
					  if (output) {
 | 
				
			||||||
 | 
					    ga_init(&capture_local, 1, 80);
 | 
				
			||||||
 | 
					    capture_ga = &capture_local;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  try_start();
 | 
				
			||||||
 | 
					  if (output) {
 | 
				
			||||||
 | 
					    msg_silent++;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  do_source_str(src.data, "nvim_exec()");
 | 
				
			||||||
 | 
					  if (output) {
 | 
				
			||||||
 | 
					    capture_ga = save_capture_ga;
 | 
				
			||||||
 | 
					    msg_silent = save_msg_silent;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  try_end(err);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (ERROR_SET(err)) {
 | 
				
			||||||
 | 
					    goto theend;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (output && capture_local.ga_len > 1) {
 | 
				
			||||||
 | 
					    String s = (String){
 | 
				
			||||||
 | 
					      .data = capture_local.ga_data,
 | 
				
			||||||
 | 
					      .size = (size_t)capture_local.ga_len,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    // redir usually (except :echon) prepends a newline.
 | 
				
			||||||
 | 
					    if (s.data[0] == '\n') {
 | 
				
			||||||
 | 
					      memmove(s.data, s.data + 1, s.size - 1);
 | 
				
			||||||
 | 
					      s.data[s.size - 1] = '\0';
 | 
				
			||||||
 | 
					      s.size = s.size - 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return s;  // Caller will free the memory.
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					theend:
 | 
				
			||||||
 | 
					  if (output) {
 | 
				
			||||||
 | 
					    ga_clear(&capture_local);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return (String)STRING_INIT;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Executes an ex-command.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// On execution error: fails with VimL error, does not update v:errmsg.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @see |nvim_exec()|
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @param command  Ex-command string
 | 
				
			||||||
 | 
					/// @param[out] err Error details (Vim error), if any
 | 
				
			||||||
 | 
					void nvim_command(String command, Error *err)
 | 
				
			||||||
 | 
					  FUNC_API_SINCE(1)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  try_start();
 | 
				
			||||||
 | 
					  do_cmdline_cmd(command.data);
 | 
				
			||||||
 | 
					  try_end(err);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Evaluates a VimL |expression|.
 | 
				
			||||||
 | 
					/// Dictionaries and Lists are recursively expanded.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// On execution error: fails with VimL error, does not update v:errmsg.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @param expr     VimL expression string
 | 
				
			||||||
 | 
					/// @param[out] err Error details, if any
 | 
				
			||||||
 | 
					/// @return         Evaluation result or expanded object
 | 
				
			||||||
 | 
					Object nvim_eval(String expr, Error *err)
 | 
				
			||||||
 | 
					  FUNC_API_SINCE(1)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  static int recursive = 0;  // recursion depth
 | 
				
			||||||
 | 
					  Object rv = OBJECT_INIT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  TRY_WRAP({
 | 
				
			||||||
 | 
					    // Initialize `force_abort`  and `suppress_errthrow` at the top level.
 | 
				
			||||||
 | 
					    if (!recursive) {
 | 
				
			||||||
 | 
					      force_abort = false;
 | 
				
			||||||
 | 
					      suppress_errthrow = false;
 | 
				
			||||||
 | 
					      current_exception = NULL;
 | 
				
			||||||
 | 
					      // `did_emsg` is set by emsg(), which cancels execution.
 | 
				
			||||||
 | 
					      did_emsg = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    recursive++;
 | 
				
			||||||
 | 
					    try_start();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    typval_T rettv;
 | 
				
			||||||
 | 
					    int ok = eval0((char_u *)expr.data, &rettv, NULL, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!try_end(err)) {
 | 
				
			||||||
 | 
					      if (ok == FAIL) {
 | 
				
			||||||
 | 
					        // Should never happen, try_end() should get the error. #8371
 | 
				
			||||||
 | 
					        api_set_error(err, kErrorTypeException,
 | 
				
			||||||
 | 
					                      "Failed to evaluate expression: '%.*s'", 256, expr.data);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        rv = vim_to_object(&rettv);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tv_clear(&rettv);
 | 
				
			||||||
 | 
					    recursive--;
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return rv;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Calls a VimL function.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @param fn Function name
 | 
				
			||||||
 | 
					/// @param args Function arguments
 | 
				
			||||||
 | 
					/// @param self `self` dict, or NULL for non-dict functions
 | 
				
			||||||
 | 
					/// @param[out] err Error details, if any
 | 
				
			||||||
 | 
					/// @return Result of the function call
 | 
				
			||||||
 | 
					static Object _call_function(String fn, Array args, dict_T *self, Error *err)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  static int recursive = 0;  // recursion depth
 | 
				
			||||||
 | 
					  Object rv = OBJECT_INIT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (args.size > MAX_FUNC_ARGS) {
 | 
				
			||||||
 | 
					    api_set_error(err, kErrorTypeValidation,
 | 
				
			||||||
 | 
					                  "Function called with too many arguments");
 | 
				
			||||||
 | 
					    return rv;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Convert the arguments in args from Object to typval_T values
 | 
				
			||||||
 | 
					  typval_T vim_args[MAX_FUNC_ARGS + 1];
 | 
				
			||||||
 | 
					  size_t i = 0;  // also used for freeing the variables
 | 
				
			||||||
 | 
					  for (; i < args.size; i++) {
 | 
				
			||||||
 | 
					    if (!object_to_vim(args.items[i], &vim_args[i], err)) {
 | 
				
			||||||
 | 
					      goto free_vim_args;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  TRY_WRAP({
 | 
				
			||||||
 | 
					    // Initialize `force_abort`  and `suppress_errthrow` at the top level.
 | 
				
			||||||
 | 
					    if (!recursive) {
 | 
				
			||||||
 | 
					      force_abort = false;
 | 
				
			||||||
 | 
					      suppress_errthrow = false;
 | 
				
			||||||
 | 
					      current_exception = NULL;
 | 
				
			||||||
 | 
					      // `did_emsg` is set by emsg(), which cancels execution.
 | 
				
			||||||
 | 
					      did_emsg = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    recursive++;
 | 
				
			||||||
 | 
					    try_start();
 | 
				
			||||||
 | 
					    typval_T rettv;
 | 
				
			||||||
 | 
					    funcexe_T funcexe = FUNCEXE_INIT;
 | 
				
			||||||
 | 
					    funcexe.firstline = curwin->w_cursor.lnum;
 | 
				
			||||||
 | 
					    funcexe.lastline = curwin->w_cursor.lnum;
 | 
				
			||||||
 | 
					    funcexe.evaluate = true;
 | 
				
			||||||
 | 
					    funcexe.selfdict = self;
 | 
				
			||||||
 | 
					    // call_func() retval is deceptive, ignore it.  Instead we set `msg_list`
 | 
				
			||||||
 | 
					    // (see above) to capture abort-causing non-exception errors.
 | 
				
			||||||
 | 
					    (void)call_func((char_u *)fn.data, (int)fn.size, &rettv, (int)args.size,
 | 
				
			||||||
 | 
					                    vim_args, &funcexe);
 | 
				
			||||||
 | 
					    if (!try_end(err)) {
 | 
				
			||||||
 | 
					      rv = vim_to_object(&rettv);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    tv_clear(&rettv);
 | 
				
			||||||
 | 
					    recursive--;
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					free_vim_args:
 | 
				
			||||||
 | 
					  while (i > 0) {
 | 
				
			||||||
 | 
					    tv_clear(&vim_args[--i]);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return rv;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Calls a VimL function with the given arguments.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// On execution error: fails with VimL error, does not update v:errmsg.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @param fn       Function to call
 | 
				
			||||||
 | 
					/// @param args     Function arguments packed in an Array
 | 
				
			||||||
 | 
					/// @param[out] err Error details, if any
 | 
				
			||||||
 | 
					/// @return Result of the function call
 | 
				
			||||||
 | 
					Object nvim_call_function(String fn, Array args, Error *err)
 | 
				
			||||||
 | 
					  FUNC_API_SINCE(1)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  return _call_function(fn, args, NULL, err);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Calls a VimL |Dictionary-function| with the given arguments.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// On execution error: fails with VimL error, does not update v:errmsg.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @param dict Dictionary, or String evaluating to a VimL |self| dict
 | 
				
			||||||
 | 
					/// @param fn Name of the function defined on the VimL dict
 | 
				
			||||||
 | 
					/// @param args Function arguments packed in an Array
 | 
				
			||||||
 | 
					/// @param[out] err Error details, if any
 | 
				
			||||||
 | 
					/// @return Result of the function call
 | 
				
			||||||
 | 
					Object nvim_call_dict_function(Object dict, String fn, Array args, Error *err)
 | 
				
			||||||
 | 
					  FUNC_API_SINCE(4)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  Object rv = OBJECT_INIT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  typval_T rettv;
 | 
				
			||||||
 | 
					  bool mustfree = false;
 | 
				
			||||||
 | 
					  switch (dict.type) {
 | 
				
			||||||
 | 
					  case kObjectTypeString:
 | 
				
			||||||
 | 
					    try_start();
 | 
				
			||||||
 | 
					    if (eval0((char_u *)dict.data.string.data, &rettv, NULL, true) == FAIL) {
 | 
				
			||||||
 | 
					      api_set_error(err, kErrorTypeException,
 | 
				
			||||||
 | 
					                    "Failed to evaluate dict expression");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (try_end(err)) {
 | 
				
			||||||
 | 
					      return rv;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // Evaluation of the string arg created a new dict or increased the
 | 
				
			||||||
 | 
					    // refcount of a dict. Not necessary for a RPC dict.
 | 
				
			||||||
 | 
					    mustfree = true;
 | 
				
			||||||
 | 
					    break;
 | 
				
			||||||
 | 
					  case kObjectTypeDictionary:
 | 
				
			||||||
 | 
					    if (!object_to_vim(dict, &rettv, err)) {
 | 
				
			||||||
 | 
					      goto end;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    break;
 | 
				
			||||||
 | 
					  default:
 | 
				
			||||||
 | 
					    api_set_error(err, kErrorTypeValidation,
 | 
				
			||||||
 | 
					                  "dict argument type must be String or Dictionary");
 | 
				
			||||||
 | 
					    return rv;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  dict_T *self_dict = rettv.vval.v_dict;
 | 
				
			||||||
 | 
					  if (rettv.v_type != VAR_DICT || !self_dict) {
 | 
				
			||||||
 | 
					    api_set_error(err, kErrorTypeValidation, "dict not found");
 | 
				
			||||||
 | 
					    goto end;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (fn.data && fn.size > 0 && dict.type != kObjectTypeDictionary) {
 | 
				
			||||||
 | 
					    dictitem_T *const di = tv_dict_find(self_dict, fn.data, (ptrdiff_t)fn.size);
 | 
				
			||||||
 | 
					    if (di == NULL) {
 | 
				
			||||||
 | 
					      api_set_error(err, kErrorTypeValidation, "Not found: %s", fn.data);
 | 
				
			||||||
 | 
					      goto end;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (di->di_tv.v_type == VAR_PARTIAL) {
 | 
				
			||||||
 | 
					      api_set_error(err, kErrorTypeValidation,
 | 
				
			||||||
 | 
					                    "partial function not supported");
 | 
				
			||||||
 | 
					      goto end;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (di->di_tv.v_type != VAR_FUNC) {
 | 
				
			||||||
 | 
					      api_set_error(err, kErrorTypeValidation, "Not a function: %s", fn.data);
 | 
				
			||||||
 | 
					      goto end;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn = (String) {
 | 
				
			||||||
 | 
					      .data = (char *)di->di_tv.vval.v_string,
 | 
				
			||||||
 | 
					      .size = STRLEN(di->di_tv.vval.v_string),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!fn.data || fn.size < 1) {
 | 
				
			||||||
 | 
					    api_set_error(err, kErrorTypeValidation, "Invalid (empty) function name");
 | 
				
			||||||
 | 
					    goto end;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  rv = _call_function(fn, args, self_dict, err);
 | 
				
			||||||
 | 
					end:
 | 
				
			||||||
 | 
					  if (mustfree) {
 | 
				
			||||||
 | 
					    tv_clear(&rettv);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return rv;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					  ExprASTNode **node_p;
 | 
				
			||||||
 | 
					  Object *ret_node_p;
 | 
				
			||||||
 | 
					} ExprASTConvStackItem;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// @cond DOXYGEN_NOT_A_FUNCTION
 | 
				
			||||||
 | 
					typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack;
 | 
				
			||||||
 | 
					/// @endcond
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Parse a VimL expression.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @param[in]  expr  Expression to parse. Always treated as a single line.
 | 
				
			||||||
 | 
					/// @param[in]  flags Flags:
 | 
				
			||||||
 | 
					///                    - "m" if multiple expressions in a row are allowed (only
 | 
				
			||||||
 | 
					///                      the first one will be parsed),
 | 
				
			||||||
 | 
					///                    - "E" if EOC tokens are not allowed (determines whether
 | 
				
			||||||
 | 
					///                      they will stop parsing process or be recognized as an
 | 
				
			||||||
 | 
					///                      operator/space, though also yielding an error).
 | 
				
			||||||
 | 
					///                    - "l" when needing to start parsing with lvalues for
 | 
				
			||||||
 | 
					///                      ":let" or ":for".
 | 
				
			||||||
 | 
					///                    Common flag sets:
 | 
				
			||||||
 | 
					///                    - "m" to parse like for ":echo".
 | 
				
			||||||
 | 
					///                    - "E" to parse like for "<C-r>=".
 | 
				
			||||||
 | 
					///                    - empty string for ":call".
 | 
				
			||||||
 | 
					///                    - "lm" to parse for ":let".
 | 
				
			||||||
 | 
					/// @param[in]  highlight  If true, return value will also include "highlight"
 | 
				
			||||||
 | 
					///                        key containing array of 4-tuples (arrays) (Integer,
 | 
				
			||||||
 | 
					///                        Integer, Integer, String), where first three numbers
 | 
				
			||||||
 | 
					///                        define the highlighted region and represent line,
 | 
				
			||||||
 | 
					///                        starting column and ending column (latter exclusive:
 | 
				
			||||||
 | 
					///                        one should highlight region [start_col, end_col)).
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @return
 | 
				
			||||||
 | 
					///      - AST: top-level dictionary with these keys:
 | 
				
			||||||
 | 
					///        - "error": Dictionary with error, present only if parser saw some
 | 
				
			||||||
 | 
					///                 error. Contains the following keys:
 | 
				
			||||||
 | 
					///          - "message": String, error message in printf format, translated.
 | 
				
			||||||
 | 
					///                       Must contain exactly one "%.*s".
 | 
				
			||||||
 | 
					///          - "arg": String, error message argument.
 | 
				
			||||||
 | 
					///        - "len": Amount of bytes successfully parsed. With flags equal to ""
 | 
				
			||||||
 | 
					///                 that should be equal to the length of expr string.
 | 
				
			||||||
 | 
					///                 (“Successfully parsed” here means “participated in AST
 | 
				
			||||||
 | 
					///                  creation”, not “till the first error”.)
 | 
				
			||||||
 | 
					///        - "ast": AST, either nil or a dictionary with these keys:
 | 
				
			||||||
 | 
					///          - "type": node type, one of the value names from ExprASTNodeType
 | 
				
			||||||
 | 
					///                    stringified without "kExprNode" prefix.
 | 
				
			||||||
 | 
					///          - "start": a pair [line, column] describing where node is "started"
 | 
				
			||||||
 | 
					///                     where "line" is always 0 (will not be 0 if you will be
 | 
				
			||||||
 | 
					///                     using nvim_parse_viml() on e.g. ":let", but that is not
 | 
				
			||||||
 | 
					///                     present yet). Both elements are Integers.
 | 
				
			||||||
 | 
					///          - "len": “length” of the node. This and "start" are there for
 | 
				
			||||||
 | 
					///                   debugging purposes primary (debugging parser and providing
 | 
				
			||||||
 | 
					///                   debug information).
 | 
				
			||||||
 | 
					///          - "children": a list of nodes described in top/"ast". There always
 | 
				
			||||||
 | 
					///                        is zero, one or two children, key will not be present
 | 
				
			||||||
 | 
					///                        if node has no children. Maximum number of children
 | 
				
			||||||
 | 
					///                        may be found in node_maxchildren array.
 | 
				
			||||||
 | 
					///      - Local values (present only for certain nodes):
 | 
				
			||||||
 | 
					///        - "scope": a single Integer, specifies scope for "Option" and
 | 
				
			||||||
 | 
					///                   "PlainIdentifier" nodes. For "Option" it is one of
 | 
				
			||||||
 | 
					///                   ExprOptScope values, for "PlainIdentifier" it is one of
 | 
				
			||||||
 | 
					///                   ExprVarScope values.
 | 
				
			||||||
 | 
					///        - "ident": identifier (without scope, if any), present for "Option",
 | 
				
			||||||
 | 
					///                   "PlainIdentifier", "PlainKey" and "Environment" nodes.
 | 
				
			||||||
 | 
					///        - "name": Integer, register name (one character) or -1. Only present
 | 
				
			||||||
 | 
					///                for "Register" nodes.
 | 
				
			||||||
 | 
					///        - "cmp_type": String, comparison type, one of the value names from
 | 
				
			||||||
 | 
					///                      ExprComparisonType, stringified without "kExprCmp"
 | 
				
			||||||
 | 
					///                      prefix. Only present for "Comparison" nodes.
 | 
				
			||||||
 | 
					///        - "ccs_strategy": String, case comparison strategy, one of the
 | 
				
			||||||
 | 
					///                          value names from ExprCaseCompareStrategy,
 | 
				
			||||||
 | 
					///                          stringified without "kCCStrategy" prefix. Only
 | 
				
			||||||
 | 
					///                          present for "Comparison" nodes.
 | 
				
			||||||
 | 
					///        - "augmentation": String, augmentation type for "Assignment" nodes.
 | 
				
			||||||
 | 
					///                          Is either an empty string, "Add", "Subtract" or
 | 
				
			||||||
 | 
					///                          "Concat" for "=", "+=", "-=" or ".=" respectively.
 | 
				
			||||||
 | 
					///        - "invert": Boolean, true if result of comparison needs to be
 | 
				
			||||||
 | 
					///                    inverted. Only present for "Comparison" nodes.
 | 
				
			||||||
 | 
					///        - "ivalue": Integer, integer value for "Integer" nodes.
 | 
				
			||||||
 | 
					///        - "fvalue": Float, floating-point value for "Float" nodes.
 | 
				
			||||||
 | 
					///        - "svalue": String, value for "SingleQuotedString" and
 | 
				
			||||||
 | 
					///                    "DoubleQuotedString" nodes.
 | 
				
			||||||
 | 
					/// @param[out] err Error details, if any
 | 
				
			||||||
 | 
					Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, Error *err)
 | 
				
			||||||
 | 
					  FUNC_API_SINCE(4) FUNC_API_FAST
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  int pflags = 0;
 | 
				
			||||||
 | 
					  for (size_t i = 0 ; i < flags.size ; i++) {
 | 
				
			||||||
 | 
					    switch (flags.data[i]) {
 | 
				
			||||||
 | 
					    case 'm':
 | 
				
			||||||
 | 
					      pflags |= kExprFlagsMulti; break;
 | 
				
			||||||
 | 
					    case 'E':
 | 
				
			||||||
 | 
					      pflags |= kExprFlagsDisallowEOC; break;
 | 
				
			||||||
 | 
					    case 'l':
 | 
				
			||||||
 | 
					      pflags |= kExprFlagsParseLet; break;
 | 
				
			||||||
 | 
					    case NUL:
 | 
				
			||||||
 | 
					      api_set_error(err, kErrorTypeValidation, "Invalid flag: '\\0' (%u)",
 | 
				
			||||||
 | 
					                    (unsigned)flags.data[i]);
 | 
				
			||||||
 | 
					      return (Dictionary)ARRAY_DICT_INIT;
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      api_set_error(err, kErrorTypeValidation, "Invalid flag: '%c' (%u)",
 | 
				
			||||||
 | 
					                    flags.data[i], (unsigned)flags.data[i]);
 | 
				
			||||||
 | 
					      return (Dictionary)ARRAY_DICT_INIT;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  ParserLine parser_lines[] = {
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      .data = expr.data,
 | 
				
			||||||
 | 
					      .size = expr.size,
 | 
				
			||||||
 | 
					      .allocated = false,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    { NULL, 0, false },
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  ParserLine *plines_p = parser_lines;
 | 
				
			||||||
 | 
					  ParserHighlight colors;
 | 
				
			||||||
 | 
					  kvi_init(colors);
 | 
				
			||||||
 | 
					  ParserHighlight *const colors_p = (highlight ? &colors : NULL);
 | 
				
			||||||
 | 
					  ParserState pstate;
 | 
				
			||||||
 | 
					  viml_parser_init(&pstate, parser_simple_get_line, &plines_p, colors_p);
 | 
				
			||||||
 | 
					  ExprAST east = viml_pexpr_parse(&pstate, pflags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const size_t ret_size = (2  // "ast", "len"
 | 
				
			||||||
 | 
					                           + (size_t)(east.err.msg != NULL)  // "error"
 | 
				
			||||||
 | 
					                           + (size_t)highlight  // "highlight"
 | 
				
			||||||
 | 
					                           + 0);
 | 
				
			||||||
 | 
					  Dictionary ret = {
 | 
				
			||||||
 | 
					    .items = xmalloc(ret_size * sizeof(ret.items[0])),
 | 
				
			||||||
 | 
					    .size = 0,
 | 
				
			||||||
 | 
					    .capacity = ret_size,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  ret.items[ret.size++] = (KeyValuePair) {
 | 
				
			||||||
 | 
					    .key = STATIC_CSTR_TO_STRING("ast"),
 | 
				
			||||||
 | 
					    .value = NIL,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  ret.items[ret.size++] = (KeyValuePair) {
 | 
				
			||||||
 | 
					    .key = STATIC_CSTR_TO_STRING("len"),
 | 
				
			||||||
 | 
					    .value = INTEGER_OBJ((Integer)(pstate.pos.line == 1
 | 
				
			||||||
 | 
					                                   ? parser_lines[0].size
 | 
				
			||||||
 | 
					                                   : pstate.pos.col)),
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  if (east.err.msg != NULL) {
 | 
				
			||||||
 | 
					    Dictionary err_dict = {
 | 
				
			||||||
 | 
					      .items = xmalloc(2 * sizeof(err_dict.items[0])),
 | 
				
			||||||
 | 
					      .size = 2,
 | 
				
			||||||
 | 
					      .capacity = 2,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    err_dict.items[0] = (KeyValuePair) {
 | 
				
			||||||
 | 
					      .key = STATIC_CSTR_TO_STRING("message"),
 | 
				
			||||||
 | 
					      .value = STRING_OBJ(cstr_to_string(east.err.msg)),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    if (east.err.arg == NULL) {
 | 
				
			||||||
 | 
					      err_dict.items[1] = (KeyValuePair) {
 | 
				
			||||||
 | 
					        .key = STATIC_CSTR_TO_STRING("arg"),
 | 
				
			||||||
 | 
					        .value = STRING_OBJ(STRING_INIT),
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      err_dict.items[1] = (KeyValuePair) {
 | 
				
			||||||
 | 
					        .key = STATIC_CSTR_TO_STRING("arg"),
 | 
				
			||||||
 | 
					        .value = STRING_OBJ(((String) {
 | 
				
			||||||
 | 
					          .data = xmemdupz(east.err.arg, (size_t)east.err.arg_len),
 | 
				
			||||||
 | 
					          .size = (size_t)east.err.arg_len,
 | 
				
			||||||
 | 
					        })),
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    ret.items[ret.size++] = (KeyValuePair) {
 | 
				
			||||||
 | 
					      .key = STATIC_CSTR_TO_STRING("error"),
 | 
				
			||||||
 | 
					      .value = DICTIONARY_OBJ(err_dict),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (highlight) {
 | 
				
			||||||
 | 
					    Array hl = (Array) {
 | 
				
			||||||
 | 
					      .items = xmalloc(kv_size(colors) * sizeof(hl.items[0])),
 | 
				
			||||||
 | 
					      .capacity = kv_size(colors),
 | 
				
			||||||
 | 
					      .size = kv_size(colors),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    for (size_t i = 0 ; i < kv_size(colors) ; i++) {
 | 
				
			||||||
 | 
					      const ParserHighlightChunk chunk = kv_A(colors, i);
 | 
				
			||||||
 | 
					      Array chunk_arr = (Array) {
 | 
				
			||||||
 | 
					        .items = xmalloc(4 * sizeof(chunk_arr.items[0])),
 | 
				
			||||||
 | 
					        .capacity = 4,
 | 
				
			||||||
 | 
					        .size = 4,
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					      chunk_arr.items[0] = INTEGER_OBJ((Integer)chunk.start.line);
 | 
				
			||||||
 | 
					      chunk_arr.items[1] = INTEGER_OBJ((Integer)chunk.start.col);
 | 
				
			||||||
 | 
					      chunk_arr.items[2] = INTEGER_OBJ((Integer)chunk.end_col);
 | 
				
			||||||
 | 
					      chunk_arr.items[3] = STRING_OBJ(cstr_to_string(chunk.group));
 | 
				
			||||||
 | 
					      hl.items[i] = ARRAY_OBJ(chunk_arr);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    ret.items[ret.size++] = (KeyValuePair) {
 | 
				
			||||||
 | 
					      .key = STATIC_CSTR_TO_STRING("highlight"),
 | 
				
			||||||
 | 
					      .value = ARRAY_OBJ(hl),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  kvi_destroy(colors);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Walk over the AST, freeing nodes in process.
 | 
				
			||||||
 | 
					  ExprASTConvStack ast_conv_stack;
 | 
				
			||||||
 | 
					  kvi_init(ast_conv_stack);
 | 
				
			||||||
 | 
					  kvi_push(ast_conv_stack, ((ExprASTConvStackItem) {
 | 
				
			||||||
 | 
					    .node_p = &east.root,
 | 
				
			||||||
 | 
					    .ret_node_p = &ret.items[0].value,
 | 
				
			||||||
 | 
					  }));
 | 
				
			||||||
 | 
					  while (kv_size(ast_conv_stack)) {
 | 
				
			||||||
 | 
					    ExprASTConvStackItem cur_item = kv_last(ast_conv_stack);
 | 
				
			||||||
 | 
					    ExprASTNode *const node = *cur_item.node_p;
 | 
				
			||||||
 | 
					    if (node == NULL) {
 | 
				
			||||||
 | 
					      assert(kv_size(ast_conv_stack) == 1);
 | 
				
			||||||
 | 
					      kv_drop(ast_conv_stack, 1);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      if (cur_item.ret_node_p->type == kObjectTypeNil) {
 | 
				
			||||||
 | 
					        size_t items_size = (size_t)(3  // "type", "start" and "len"
 | 
				
			||||||
 | 
					                                     + (node->children != NULL)  // "children"
 | 
				
			||||||
 | 
					                                     + (node->type == kExprNodeOption
 | 
				
			||||||
 | 
					                                        || node->type == kExprNodePlainIdentifier)  // "scope"
 | 
				
			||||||
 | 
					                                     + (node->type == kExprNodeOption
 | 
				
			||||||
 | 
					                                        || node->type == kExprNodePlainIdentifier
 | 
				
			||||||
 | 
					                                        || node->type == kExprNodePlainKey
 | 
				
			||||||
 | 
					                                        || node->type == kExprNodeEnvironment)  // "ident"
 | 
				
			||||||
 | 
					                                     + (node->type == kExprNodeRegister)  // "name"
 | 
				
			||||||
 | 
					                                     + (3  // "cmp_type", "ccs_strategy", "invert"
 | 
				
			||||||
 | 
					                                        * (node->type == kExprNodeComparison))
 | 
				
			||||||
 | 
					                                     + (node->type == kExprNodeInteger)  // "ivalue"
 | 
				
			||||||
 | 
					                                     + (node->type == kExprNodeFloat)  // "fvalue"
 | 
				
			||||||
 | 
					                                     + (node->type == kExprNodeDoubleQuotedString
 | 
				
			||||||
 | 
					                                        || node->type == kExprNodeSingleQuotedString)  // "svalue"
 | 
				
			||||||
 | 
					                                     + (node->type == kExprNodeAssignment)  // "augmentation"
 | 
				
			||||||
 | 
					                                     + 0);
 | 
				
			||||||
 | 
					        Dictionary ret_node = {
 | 
				
			||||||
 | 
					          .items = xmalloc(items_size * sizeof(ret_node.items[0])),
 | 
				
			||||||
 | 
					          .capacity = items_size,
 | 
				
			||||||
 | 
					          .size = 0,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        *cur_item.ret_node_p = DICTIONARY_OBJ(ret_node);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      Dictionary *ret_node = &cur_item.ret_node_p->data.dictionary;
 | 
				
			||||||
 | 
					      if (node->children != NULL) {
 | 
				
			||||||
 | 
					        const size_t num_children = 1 + (node->children->next != NULL);
 | 
				
			||||||
 | 
					        Array children_array = {
 | 
				
			||||||
 | 
					          .items = xmalloc(num_children * sizeof(children_array.items[0])),
 | 
				
			||||||
 | 
					          .capacity = num_children,
 | 
				
			||||||
 | 
					          .size = num_children,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        for (size_t i = 0; i < num_children; i++) {
 | 
				
			||||||
 | 
					          children_array.items[i] = NIL;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
				
			||||||
 | 
					          .key = STATIC_CSTR_TO_STRING("children"),
 | 
				
			||||||
 | 
					          .value = ARRAY_OBJ(children_array),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        kvi_push(ast_conv_stack, ((ExprASTConvStackItem) {
 | 
				
			||||||
 | 
					          .node_p = &node->children,
 | 
				
			||||||
 | 
					          .ret_node_p = &children_array.items[0],
 | 
				
			||||||
 | 
					        }));
 | 
				
			||||||
 | 
					      } else if (node->next != NULL) {
 | 
				
			||||||
 | 
					        kvi_push(ast_conv_stack, ((ExprASTConvStackItem) {
 | 
				
			||||||
 | 
					          .node_p = &node->next,
 | 
				
			||||||
 | 
					          .ret_node_p = cur_item.ret_node_p + 1,
 | 
				
			||||||
 | 
					        }));
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        kv_drop(ast_conv_stack, 1);
 | 
				
			||||||
 | 
					        ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
				
			||||||
 | 
					          .key = STATIC_CSTR_TO_STRING("type"),
 | 
				
			||||||
 | 
					          .value = STRING_OBJ(cstr_to_string(east_node_type_tab[node->type])),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        Array start_array = {
 | 
				
			||||||
 | 
					          .items = xmalloc(2 * sizeof(start_array.items[0])),
 | 
				
			||||||
 | 
					          .capacity = 2,
 | 
				
			||||||
 | 
					          .size = 2,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        start_array.items[0] = INTEGER_OBJ((Integer)node->start.line);
 | 
				
			||||||
 | 
					        start_array.items[1] = INTEGER_OBJ((Integer)node->start.col);
 | 
				
			||||||
 | 
					        ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
				
			||||||
 | 
					          .key = STATIC_CSTR_TO_STRING("start"),
 | 
				
			||||||
 | 
					          .value = ARRAY_OBJ(start_array),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
				
			||||||
 | 
					          .key = STATIC_CSTR_TO_STRING("len"),
 | 
				
			||||||
 | 
					          .value = INTEGER_OBJ((Integer)node->len),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        switch (node->type) {
 | 
				
			||||||
 | 
					        case kExprNodeDoubleQuotedString:
 | 
				
			||||||
 | 
					        case kExprNodeSingleQuotedString:
 | 
				
			||||||
 | 
					          ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
				
			||||||
 | 
					            .key = STATIC_CSTR_TO_STRING("svalue"),
 | 
				
			||||||
 | 
					            .value = STRING_OBJ(((String) {
 | 
				
			||||||
 | 
					              .data = node->data.str.value,
 | 
				
			||||||
 | 
					              .size = node->data.str.size,
 | 
				
			||||||
 | 
					            })),
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        case kExprNodeOption:
 | 
				
			||||||
 | 
					          ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
				
			||||||
 | 
					            .key = STATIC_CSTR_TO_STRING("scope"),
 | 
				
			||||||
 | 
					            .value = INTEGER_OBJ(node->data.opt.scope),
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					          ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
				
			||||||
 | 
					            .key = STATIC_CSTR_TO_STRING("ident"),
 | 
				
			||||||
 | 
					            .value = STRING_OBJ(((String) {
 | 
				
			||||||
 | 
					              .data = xmemdupz(node->data.opt.ident,
 | 
				
			||||||
 | 
					                               node->data.opt.ident_len),
 | 
				
			||||||
 | 
					              .size = node->data.opt.ident_len,
 | 
				
			||||||
 | 
					            })),
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        case kExprNodePlainIdentifier:
 | 
				
			||||||
 | 
					          ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
				
			||||||
 | 
					            .key = STATIC_CSTR_TO_STRING("scope"),
 | 
				
			||||||
 | 
					            .value = INTEGER_OBJ(node->data.var.scope),
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					          ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
				
			||||||
 | 
					            .key = STATIC_CSTR_TO_STRING("ident"),
 | 
				
			||||||
 | 
					            .value = STRING_OBJ(((String) {
 | 
				
			||||||
 | 
					              .data = xmemdupz(node->data.var.ident,
 | 
				
			||||||
 | 
					                               node->data.var.ident_len),
 | 
				
			||||||
 | 
					              .size = node->data.var.ident_len,
 | 
				
			||||||
 | 
					            })),
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        case kExprNodePlainKey:
 | 
				
			||||||
 | 
					          ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
				
			||||||
 | 
					            .key = STATIC_CSTR_TO_STRING("ident"),
 | 
				
			||||||
 | 
					            .value = STRING_OBJ(((String) {
 | 
				
			||||||
 | 
					              .data = xmemdupz(node->data.var.ident,
 | 
				
			||||||
 | 
					                               node->data.var.ident_len),
 | 
				
			||||||
 | 
					              .size = node->data.var.ident_len,
 | 
				
			||||||
 | 
					            })),
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        case kExprNodeEnvironment:
 | 
				
			||||||
 | 
					          ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
				
			||||||
 | 
					            .key = STATIC_CSTR_TO_STRING("ident"),
 | 
				
			||||||
 | 
					            .value = STRING_OBJ(((String) {
 | 
				
			||||||
 | 
					              .data = xmemdupz(node->data.env.ident,
 | 
				
			||||||
 | 
					                               node->data.env.ident_len),
 | 
				
			||||||
 | 
					              .size = node->data.env.ident_len,
 | 
				
			||||||
 | 
					            })),
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        case kExprNodeRegister:
 | 
				
			||||||
 | 
					          ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
				
			||||||
 | 
					            .key = STATIC_CSTR_TO_STRING("name"),
 | 
				
			||||||
 | 
					            .value = INTEGER_OBJ(node->data.reg.name),
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        case kExprNodeComparison:
 | 
				
			||||||
 | 
					          ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
				
			||||||
 | 
					            .key = STATIC_CSTR_TO_STRING("cmp_type"),
 | 
				
			||||||
 | 
					            .value = STRING_OBJ(cstr_to_string(eltkn_cmp_type_tab[node->data.cmp.type])),
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					          ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
				
			||||||
 | 
					            .key = STATIC_CSTR_TO_STRING("ccs_strategy"),
 | 
				
			||||||
 | 
					            .value = STRING_OBJ(cstr_to_string(ccs_tab[node->data.cmp.ccs])),
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					          ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
				
			||||||
 | 
					            .key = STATIC_CSTR_TO_STRING("invert"),
 | 
				
			||||||
 | 
					            .value = BOOLEAN_OBJ(node->data.cmp.inv),
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        case kExprNodeFloat:
 | 
				
			||||||
 | 
					          ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
				
			||||||
 | 
					            .key = STATIC_CSTR_TO_STRING("fvalue"),
 | 
				
			||||||
 | 
					            .value = FLOAT_OBJ(node->data.flt.value),
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        case kExprNodeInteger:
 | 
				
			||||||
 | 
					          ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
				
			||||||
 | 
					            .key = STATIC_CSTR_TO_STRING("ivalue"),
 | 
				
			||||||
 | 
					            .value = INTEGER_OBJ((Integer)(node->data.num.value > API_INTEGER_MAX
 | 
				
			||||||
 | 
					                                           ? API_INTEGER_MAX
 | 
				
			||||||
 | 
					                                           : (Integer)node->data.num.value)),
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        case kExprNodeAssignment: {
 | 
				
			||||||
 | 
					          const ExprAssignmentType asgn_type = node->data.ass.type;
 | 
				
			||||||
 | 
					          ret_node->items[ret_node->size++] = (KeyValuePair) {
 | 
				
			||||||
 | 
					            .key = STATIC_CSTR_TO_STRING("augmentation"),
 | 
				
			||||||
 | 
					            .value = STRING_OBJ(asgn_type == kExprAsgnPlain
 | 
				
			||||||
 | 
					                                ? (String)STRING_INIT
 | 
				
			||||||
 | 
					                                : cstr_to_string(expr_asgn_type_tab[asgn_type])),
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        case kExprNodeMissing:
 | 
				
			||||||
 | 
					        case kExprNodeOpMissing:
 | 
				
			||||||
 | 
					        case kExprNodeTernary:
 | 
				
			||||||
 | 
					        case kExprNodeTernaryValue:
 | 
				
			||||||
 | 
					        case kExprNodeSubscript:
 | 
				
			||||||
 | 
					        case kExprNodeListLiteral:
 | 
				
			||||||
 | 
					        case kExprNodeUnaryPlus:
 | 
				
			||||||
 | 
					        case kExprNodeBinaryPlus:
 | 
				
			||||||
 | 
					        case kExprNodeNested:
 | 
				
			||||||
 | 
					        case kExprNodeCall:
 | 
				
			||||||
 | 
					        case kExprNodeComplexIdentifier:
 | 
				
			||||||
 | 
					        case kExprNodeUnknownFigure:
 | 
				
			||||||
 | 
					        case kExprNodeLambda:
 | 
				
			||||||
 | 
					        case kExprNodeDictLiteral:
 | 
				
			||||||
 | 
					        case kExprNodeCurlyBracesIdentifier:
 | 
				
			||||||
 | 
					        case kExprNodeComma:
 | 
				
			||||||
 | 
					        case kExprNodeColon:
 | 
				
			||||||
 | 
					        case kExprNodeArrow:
 | 
				
			||||||
 | 
					        case kExprNodeConcat:
 | 
				
			||||||
 | 
					        case kExprNodeConcatOrSubscript:
 | 
				
			||||||
 | 
					        case kExprNodeOr:
 | 
				
			||||||
 | 
					        case kExprNodeAnd:
 | 
				
			||||||
 | 
					        case kExprNodeUnaryMinus:
 | 
				
			||||||
 | 
					        case kExprNodeBinaryMinus:
 | 
				
			||||||
 | 
					        case kExprNodeNot:
 | 
				
			||||||
 | 
					        case kExprNodeMultiplication:
 | 
				
			||||||
 | 
					        case kExprNodeDivision:
 | 
				
			||||||
 | 
					        case kExprNodeMod:
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        assert(cur_item.ret_node_p->data.dictionary.size
 | 
				
			||||||
 | 
					               == cur_item.ret_node_p->data.dictionary.capacity);
 | 
				
			||||||
 | 
					        xfree(*cur_item.node_p);
 | 
				
			||||||
 | 
					        *cur_item.node_p = NULL;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  kvi_destroy(ast_conv_stack);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  assert(ret.size == ret.capacity);
 | 
				
			||||||
 | 
					  // Should be a no-op actually, leaving it in case non-nodes will need to be
 | 
				
			||||||
 | 
					  // freed later.
 | 
				
			||||||
 | 
					  viml_pexpr_free_ast(east);
 | 
				
			||||||
 | 
					  viml_parser_destroy(&pstate);
 | 
				
			||||||
 | 
					  return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										9
									
								
								src/nvim/api/vimscript.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/nvim/api/vimscript.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					#ifndef NVIM_API_VIMSCRIPT_H
 | 
				
			||||||
 | 
					#define NVIM_API_VIMSCRIPT_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "nvim/api/private/defs.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef INCLUDE_GENERATED_DECLARATIONS
 | 
				
			||||||
 | 
					# include "api/vimscript.h.generated.h"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#endif  // NVIM_API_VIMSCRIPT_H
 | 
				
			||||||
@@ -6,6 +6,7 @@
 | 
				
			|||||||
#include "nvim/api/private/converter.h"
 | 
					#include "nvim/api/private/converter.h"
 | 
				
			||||||
#include "nvim/api/private/helpers.h"
 | 
					#include "nvim/api/private/helpers.h"
 | 
				
			||||||
#include "nvim/api/vim.h"
 | 
					#include "nvim/api/vim.h"
 | 
				
			||||||
 | 
					#include "nvim/api/vimscript.h"
 | 
				
			||||||
#include "nvim/context.h"
 | 
					#include "nvim/context.h"
 | 
				
			||||||
#include "nvim/eval/encode.h"
 | 
					#include "nvim/eval/encode.h"
 | 
				
			||||||
#include "nvim/ex_docmd.h"
 | 
					#include "nvim/ex_docmd.h"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,7 +18,7 @@ local type_key = api_helpers.type_key
 | 
				
			|||||||
local obj2lua = api_helpers.obj2lua
 | 
					local obj2lua = api_helpers.obj2lua
 | 
				
			||||||
local func_type = api_helpers.func_type
 | 
					local func_type = api_helpers.func_type
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local api = cimport('./src/nvim/api/private/helpers.h')
 | 
					local api = cimport('./src/nvim/api/private/helpers.h', './src/nvim/api/private/converter.h')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('vim_to_object', function()
 | 
					describe('vim_to_object', function()
 | 
				
			||||||
  local vim_to_object = function(l)
 | 
					  local vim_to_object = function(l)
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user