Patch 8.2.0847
Problem:    Typval related code is spread out.
Solution:   Move code to new typval.c file. (Yegappan Lakshmanan, closes #6093)
Files:      Filelist, src/Make_cyg_ming.mak, src/Make_morph.mak,
            src/Make_mvc.mak, src/Make_vms.mms, src/Makefile, src/README.md,
            src/eval.c, src/evalfunc.c, src/globals.h, src/proto.h,
            src/proto/eval.pro, src/proto/evalfunc.pro, src/proto/typval.pro,
            src/typval.c


*** ../vim-8.2.0846/Filelist    2020-05-17 23:00:48.782969093 +0200
--- Filelist    2020-05-30 16:28:14.589109271 +0200
***************
*** 133,138 ****
--- 133,139 ----
                src/textobject.c \
                src/textprop.c \
                src/time.c \
+               src/typval.c \
                src/ui.c \
                src/undo.c \
                src/usercmd.c \
***************
*** 285,290 ****
--- 286,292 ----
                src/proto/textobject.pro \
                src/proto/textprop.pro \
                src/proto/time.pro \
+               src/proto/typval.pro \
                src/proto/ui.pro \
                src/proto/undo.pro \
                src/proto/usercmd.pro \
***************
*** 347,352 ****
--- 349,355 ----
                src/libvterm/t/29state_fallback.test \
                src/libvterm/t/30state_pen.test \
                src/libvterm/t/31state_rep.test \
+               src/libvterm/t/32state_flow.test \
                src/libvterm/t/60screen_ascii.test \
                src/libvterm/t/61screen_unicode.test \
                src/libvterm/t/62screen_damage.test \
*** ../vim-8.2.0846/src/Make_cyg_ming.mak       2020-05-17 20:52:40.729955174 
+0200
--- src/Make_cyg_ming.mak       2020-05-30 16:28:14.589109271 +0200
***************
*** 791,796 ****
--- 791,797 ----
        $(OUTDIR)/textobject.o \
        $(OUTDIR)/textprop.o \
        $(OUTDIR)/time.o \
+       $(OUTDIR)/typval.o \
        $(OUTDIR)/ui.o \
        $(OUTDIR)/undo.o \
        $(OUTDIR)/usercmd.o \
*** ../vim-8.2.0846/src/Make_morph.mak  2020-05-01 14:26:17.128949276 +0200
--- src/Make_morph.mak  2020-05-30 16:28:14.589109271 +0200
***************
*** 107,112 ****
--- 107,113 ----
        textobject.c                                            \
        textprop.c                                              \
        time.c                                                  \
+       typval.c                                                \
        ui.c                                                    \
        undo.c                                                  \
        usercmd.c                                               \
*** ../vim-8.2.0846/src/Make_mvc.mak    2020-05-17 20:52:40.729955174 +0200
--- src/Make_mvc.mak    2020-05-30 16:28:14.589109271 +0200
***************
*** 811,816 ****
--- 811,817 ----
        $(OUTDIR)\textobject.obj \
        $(OUTDIR)\textprop.obj \
        $(OUTDIR)\time.obj \
+       $(OUTDIR)\typval.obj \
        $(OUTDIR)\ui.obj \
        $(OUTDIR)\undo.obj \
        $(OUTDIR)\usercmd.obj \
***************
*** 1755,1760 ****
--- 1756,1763 ----
  
  $(OUTDIR)/time.obj:   $(OUTDIR) time.c  $(INCL)
  
+ $(OUTDIR)/typval.obj: $(OUTDIR) typval.c  $(INCL)
+ 
  $(OUTDIR)/ui.obj:     $(OUTDIR) ui.c  $(INCL)
  
  $(OUTDIR)/undo.obj:   $(OUTDIR) undo.c  $(INCL)
***************
*** 1954,1959 ****
--- 1957,1963 ----
        proto/textobject.pro \
        proto/textprop.pro \
        proto/time.pro \
+       proto/typval.pro \
        proto/ui.pro \
        proto/undo.pro \
        proto/usercmd.pro \
*** ../vim-8.2.0846/src/Make_vms.mms    2020-05-01 14:26:17.128949276 +0200
--- src/Make_vms.mms    2020-05-30 16:28:14.589109271 +0200
***************
*** 386,391 ****
--- 386,392 ----
        textobject.c \
        textprop.c \
        time.c \
+       typval.c \
        ui.c \
        undo.c \
        usercmd.c \
***************
*** 497,502 ****
--- 498,504 ----
        textobject.obj \
        textprop.obj \
        time.obj \
+       typval.obj \
        ui.obj \
        undo.obj \
        usercmd.obj \
***************
*** 1005,1010 ****
--- 1007,1015 ----
  time.obj : time.c vim.h [.auto]config.h feature.h os_unix.h   \
   ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \
   [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h
+ typval.obj : typval.c vim.h [.auto]config.h feature.h os_unix.h   \
+  ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \
+  [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h
  ui.obj : ui.c vim.h [.auto]config.h feature.h os_unix.h   \
   ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \
   [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h
*** ../vim-8.2.0846/src/Makefile        2020-05-20 19:30:13.828123549 +0200
--- src/Makefile        2020-05-30 16:31:01.464473345 +0200
***************
*** 1686,1691 ****
--- 1686,1692 ----
        textobject.c \
        textprop.c \
        time.c \
+       typval.c \
        ui.c \
        undo.c \
        usercmd.c \
***************
*** 1830,1835 ****
--- 1831,1837 ----
        objects/textobject.o \
        objects/textprop.o \
        objects/time.o \
+       objects/typval.o \
        objects/ui.o \
        objects/undo.o \
        objects/usercmd.o \
***************
*** 2006,2011 ****
--- 2008,2014 ----
        textobject.pro \
        textprop.pro \
        time.pro \
+       typval.pro \
        ui.pro \
        undo.pro \
        usercmd.pro \
***************
*** 3496,3501 ****
--- 3499,3507 ----
  objects/time.o: time.c
        $(CCC) -o $@ time.c
  
+ objects/typval.o: typval.c
+       $(CCC) -o $@ typval.c
+ 
  objects/ui.o: ui.c
        $(CCC) -o $@ ui.c
  
***************
*** 4098,4103 ****
--- 4104,4113 ----
   auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
   proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
   proto.h globals.h
+ objects/typval.o: typval.c vim.h protodef.h auto/config.h feature.h os_unix.h 
\
+  auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
+  proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
+  proto.h globals.h
  objects/ui.o: ui.c vim.h protodef.h auto/config.h feature.h os_unix.h \
   auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
   proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
*** ../vim-8.2.0846/src/README.md       2020-05-01 14:26:17.128949276 +0200
--- src/README.md       2020-05-30 16:28:14.593109254 +0200
***************
*** 84,89 ****
--- 84,90 ----
  textobject.c  | text objects
  textprop.c    | text properties
  time.c                | time and timer functions
+ typval.c      | vim script type/value functions
  undo.c                | undo and redo
  usercmd.c     | user defined commands
  userfunc.c    | user defined functions
*** ../vim-8.2.0846/src/eval.c  2020-05-25 23:01:38.781393409 +0200
--- src/eval.c  2020-05-30 16:28:14.593109254 +0200
***************
*** 21,29 ****
  #endif
  
  static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary");
- #ifdef FEAT_FLOAT
- static char *e_float_as_string = N_("E806: using Float as a String");
- #endif
  
  #define NAMESPACE_CHAR        (char_u *)"abglstvw"
  
--- 21,26 ----
***************
*** 58,64 ****
  
  static int free_unref_items(int copyID);
  static char_u *make_expanded_name(char_u *in_start, char_u *expr_start, 
char_u *expr_end, char_u *in_end);
- static int tv_check_lock(typval_T *tv, char_u *name, int use_gettext);
  
  /*
   * Return "n1" divided by "n2", taking care of dividing by zero.
--- 55,60 ----
***************
*** 3304,3706 ****
  }
  
  /*
-  * Get an option value.
-  * "arg" points to the '&' or '+' before the option name.
-  * "arg" is advanced to character after the option name.
-  * Return OK or FAIL.
-  */
-     int
- get_option_tv(
-     char_u    **arg,
-     typval_T  *rettv, // when NULL, only check if option exists
-     int               evaluate)
- {
-     char_u    *option_end;
-     long      numval;
-     char_u    *stringval;
-     int               opt_type;
-     int               c;
-     int               working = (**arg == '+');    // has("+option")
-     int               ret = OK;
-     int               opt_flags;
- 
-     /*
-      * Isolate the option name and find its value.
-      */
-     option_end = find_option_end(arg, &opt_flags);
-     if (option_end == NULL)
-     {
-       if (rettv != NULL)
-           semsg(_("E112: Option name missing: %s"), *arg);
-       return FAIL;
-     }
- 
-     if (!evaluate)
-     {
-       *arg = option_end;
-       return OK;
-     }
- 
-     c = *option_end;
-     *option_end = NUL;
-     opt_type = get_option_value(*arg, &numval,
-                              rettv == NULL ? NULL : &stringval, opt_flags);
- 
-     if (opt_type == -3)                       // invalid name
-     {
-       if (rettv != NULL)
-           semsg(_(e_unknown_option), *arg);
-       ret = FAIL;
-     }
-     else if (rettv != NULL)
-     {
-       if (opt_type == -2)             // hidden string option
-       {
-           rettv->v_type = VAR_STRING;
-           rettv->vval.v_string = NULL;
-       }
-       else if (opt_type == -1)        // hidden number option
-       {
-           rettv->v_type = VAR_NUMBER;
-           rettv->vval.v_number = 0;
-       }
-       else if (opt_type == 1)         // number option
-       {
-           rettv->v_type = VAR_NUMBER;
-           rettv->vval.v_number = numval;
-       }
-       else                            // string option
-       {
-           rettv->v_type = VAR_STRING;
-           rettv->vval.v_string = stringval;
-       }
-     }
-     else if (working && (opt_type == -2 || opt_type == -1))
-       ret = FAIL;
- 
-     *option_end = c;              // put back for error messages
-     *arg = option_end;
- 
-     return ret;
- }
- 
- /*
-  * Allocate a variable for a number constant.  Also deals with "0z" for blob.
-  * Return OK or FAIL.
-  */
-     int
- get_number_tv(
-       char_u      **arg,
-       typval_T    *rettv,
-       int         evaluate,
-       int         want_string UNUSED)
- {
-     int               len;
- #ifdef FEAT_FLOAT
-     char_u    *p;
-     int               get_float = FALSE;
- 
-     // We accept a float when the format matches
-     // "[0-9]\+\.[0-9]\+\([eE][+-]\?[0-9]\+\)\?".  This is very
-     // strict to avoid backwards compatibility problems.
-     // With script version 2 and later the leading digit can be
-     // omitted.
-     // Don't look for a float after the "." operator, so that
-     // ":let vers = 1.2.3" doesn't fail.
-     if (**arg == '.')
-       p = *arg;
-     else
-       p = skipdigits(*arg + 1);
-     if (!want_string && p[0] == '.' && vim_isdigit(p[1]))
-     {
-       get_float = TRUE;
-       p = skipdigits(p + 2);
-       if (*p == 'e' || *p == 'E')
-       {
-           ++p;
-           if (*p == '-' || *p == '+')
-               ++p;
-           if (!vim_isdigit(*p))
-               get_float = FALSE;
-           else
-               p = skipdigits(p + 1);
-       }
-       if (ASCII_ISALPHA(*p) || *p == '.')
-           get_float = FALSE;
-     }
-     if (get_float)
-     {
-       float_T f;
- 
-       *arg += string2float(*arg, &f);
-       if (evaluate)
-       {
-           rettv->v_type = VAR_FLOAT;
-           rettv->vval.v_float = f;
-       }
-     }
-     else
- #endif
-     if (**arg == '0' && ((*arg)[1] == 'z' || (*arg)[1] == 'Z'))
-     {
-       char_u  *bp;
-       blob_T  *blob = NULL;  // init for gcc
- 
-       // Blob constant: 0z0123456789abcdef
-       if (evaluate)
-           blob = blob_alloc();
-       for (bp = *arg + 2; vim_isxdigit(bp[0]); bp += 2)
-       {
-           if (!vim_isxdigit(bp[1]))
-           {
-               if (blob != NULL)
-               {
-                   emsg(_("E973: Blob literal should have an even number of 
hex characters"));
-                   ga_clear(&blob->bv_ga);
-                   VIM_CLEAR(blob);
-               }
-               return FAIL;
-           }
-           if (blob != NULL)
-               ga_append(&blob->bv_ga,
-                            (hex2nr(*bp) << 4) + hex2nr(*(bp+1)));
-           if (bp[2] == '.' && vim_isxdigit(bp[3]))
-               ++bp;
-       }
-       if (blob != NULL)
-           rettv_blob_set(rettv, blob);
-       *arg = bp;
-     }
-     else
-     {
-       varnumber_T     n;
- 
-       // decimal, hex or octal number
-       vim_str2nr(*arg, NULL, &len, current_sctx.sc_version >= 4
-                     ? STR2NR_NO_OCT + STR2NR_QUOTE
-                     : STR2NR_ALL, &n, NULL, 0, TRUE);
-       if (len == 0)
-       {
-           semsg(_(e_invexpr2), *arg);
-           return FAIL;
-       }
-       *arg += len;
-       if (evaluate)
-       {
-           rettv->v_type = VAR_NUMBER;
-           rettv->vval.v_number = n;
-       }
-     }
-     return OK;
- }
- 
- /*
-  * Allocate a variable for a string constant.
-  * Return OK or FAIL.
-  */
-     int
- get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
- {
-     char_u    *p;
-     char_u    *name;
-     int               extra = 0;
-     int               len;
- 
-     /*
-      * Find the end of the string, skipping backslashed characters.
-      */
-     for (p = *arg + 1; *p != NUL && *p != '"'; MB_PTR_ADV(p))
-     {
-       if (*p == '\\' && p[1] != NUL)
-       {
-           ++p;
-           // A "\<x>" form occupies at least 4 characters, and produces up
-           // to 21 characters (3 * 6 for the char and 3 for a modifier):
-           // reserve space for 18 extra.
-           // Each byte in the char could be encoded as K_SPECIAL K_EXTRA x.
-           if (*p == '<')
-               extra += 18;
-       }
-     }
- 
-     if (*p != '"')
-     {
-       semsg(_("E114: Missing quote: %s"), *arg);
-       return FAIL;
-     }
- 
-     // If only parsing, set *arg and return here
-     if (!evaluate)
-     {
-       *arg = p + 1;
-       return OK;
-     }
- 
-     /*
-      * Copy the string into allocated memory, handling backslashed
-      * characters.
-      */
-     len = (int)(p - *arg + extra);
-     name = alloc(len);
-     if (name == NULL)
-       return FAIL;
-     rettv->v_type = VAR_STRING;
-     rettv->vval.v_string = name;
- 
-     for (p = *arg + 1; *p != NUL && *p != '"'; )
-     {
-       if (*p == '\\')
-       {
-           switch (*++p)
-           {
-               case 'b': *name++ = BS; ++p; break;
-               case 'e': *name++ = ESC; ++p; break;
-               case 'f': *name++ = FF; ++p; break;
-               case 'n': *name++ = NL; ++p; break;
-               case 'r': *name++ = CAR; ++p; break;
-               case 't': *name++ = TAB; ++p; break;
- 
-               case 'X': // hex: "\x1", "\x12"
-               case 'x':
-               case 'u': // Unicode: "\u0023"
-               case 'U':
-                         if (vim_isxdigit(p[1]))
-                         {
-                             int       n, nr;
-                             int       c = toupper(*p);
- 
-                             if (c == 'X')
-                                 n = 2;
-                             else if (*p == 'u')
-                                 n = 4;
-                             else
-                                 n = 8;
-                             nr = 0;
-                             while (--n >= 0 && vim_isxdigit(p[1]))
-                             {
-                                 ++p;
-                                 nr = (nr << 4) + hex2nr(*p);
-                             }
-                             ++p;
-                             // For "\u" store the number according to
-                             // 'encoding'.
-                             if (c != 'X')
-                                 name += (*mb_char2bytes)(nr, name);
-                             else
-                                 *name++ = nr;
-                         }
-                         break;
- 
-                         // octal: "\1", "\12", "\123"
-               case '0':
-               case '1':
-               case '2':
-               case '3':
-               case '4':
-               case '5':
-               case '6':
-               case '7': *name = *p++ - '0';
-                         if (*p >= '0' && *p <= '7')
-                         {
-                             *name = (*name << 3) + *p++ - '0';
-                             if (*p >= '0' && *p <= '7')
-                                 *name = (*name << 3) + *p++ - '0';
-                         }
-                         ++name;
-                         break;
- 
-                           // Special key, e.g.: "\<C-W>"
-               case '<': extra = trans_special(&p, name, TRUE, TRUE,
-                                                                  TRUE, NULL);
-                         if (extra != 0)
-                         {
-                             name += extra;
-                             if (name >= rettv->vval.v_string + len)
-                                 iemsg("get_string_tv() used more space than 
allocated");
-                             break;
-                         }
-                         // FALLTHROUGH
- 
-               default:  MB_COPY_CHAR(p, name);
-                         break;
-           }
-       }
-       else
-           MB_COPY_CHAR(p, name);
- 
-     }
-     *name = NUL;
-     if (*p != NUL) // just in case
-       ++p;
-     *arg = p;
- 
-     return OK;
- }
- 
- /*
-  * Allocate a variable for a 'str''ing' constant.
-  * Return OK or FAIL.
-  */
-     int
- get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate)
- {
-     char_u    *p;
-     char_u    *str;
-     int               reduce = 0;
- 
-     /*
-      * Find the end of the string, skipping ''.
-      */
-     for (p = *arg + 1; *p != NUL; MB_PTR_ADV(p))
-     {
-       if (*p == '\'')
-       {
-           if (p[1] != '\'')
-               break;
-           ++reduce;
-           ++p;
-       }
-     }
- 
-     if (*p != '\'')
-     {
-       semsg(_("E115: Missing quote: %s"), *arg);
-       return FAIL;
-     }
- 
-     // If only parsing return after setting "*arg"
-     if (!evaluate)
-     {
-       *arg = p + 1;
-       return OK;
-     }
- 
-     /*
-      * Copy the string into allocated memory, handling '' to ' reduction.
-      */
-     str = alloc((p - *arg) - reduce);
-     if (str == NULL)
-       return FAIL;
-     rettv->v_type = VAR_STRING;
-     rettv->vval.v_string = str;
- 
-     for (p = *arg + 1; *p != NUL; )
-     {
-       if (*p == '\'')
-       {
-           if (p[1] != '\'')
-               break;
-           ++p;
-       }
-       MB_COPY_CHAR(p, str);
-     }
-     *str = NUL;
-     *arg = p + 1;
- 
-     return OK;
- }
- 
- /*
   * Return the function name of partial "pt".
   */
      char_u *
--- 3300,3305 ----
***************
*** 3761,3924 ****
        partial_free(pt);
  }
  
- static int tv_equal_recurse_limit;
- 
-     static int
- func_equal(
-     typval_T *tv1,
-     typval_T *tv2,
-     int            ic)            // ignore case
- {
-     char_u    *s1, *s2;
-     dict_T    *d1, *d2;
-     int               a1, a2;
-     int               i;
- 
-     // empty and NULL function name considered the same
-     s1 = tv1->v_type == VAR_FUNC ? tv1->vval.v_string
-                                          : partial_name(tv1->vval.v_partial);
-     if (s1 != NULL && *s1 == NUL)
-       s1 = NULL;
-     s2 = tv2->v_type == VAR_FUNC ? tv2->vval.v_string
-                                          : partial_name(tv2->vval.v_partial);
-     if (s2 != NULL && *s2 == NUL)
-       s2 = NULL;
-     if (s1 == NULL || s2 == NULL)
-     {
-       if (s1 != s2)
-           return FALSE;
-     }
-     else if (STRCMP(s1, s2) != 0)
-       return FALSE;
- 
-     // empty dict and NULL dict is different
-     d1 = tv1->v_type == VAR_FUNC ? NULL : tv1->vval.v_partial->pt_dict;
-     d2 = tv2->v_type == VAR_FUNC ? NULL : tv2->vval.v_partial->pt_dict;
-     if (d1 == NULL || d2 == NULL)
-     {
-       if (d1 != d2)
-           return FALSE;
-     }
-     else if (!dict_equal(d1, d2, ic, TRUE))
-       return FALSE;
- 
-     // empty list and no list considered the same
-     a1 = tv1->v_type == VAR_FUNC ? 0 : tv1->vval.v_partial->pt_argc;
-     a2 = tv2->v_type == VAR_FUNC ? 0 : tv2->vval.v_partial->pt_argc;
-     if (a1 != a2)
-       return FALSE;
-     for (i = 0; i < a1; ++i)
-       if (!tv_equal(tv1->vval.v_partial->pt_argv + i,
-                     tv2->vval.v_partial->pt_argv + i, ic, TRUE))
-           return FALSE;
- 
-     return TRUE;
- }
- 
- /*
-  * Return TRUE if "tv1" and "tv2" have the same value.
-  * Compares the items just like "==" would compare them, but strings and
-  * numbers are different.  Floats and numbers are also different.
-  */
-     int
- tv_equal(
-     typval_T *tv1,
-     typval_T *tv2,
-     int            ic,            // ignore case
-     int            recursive)     // TRUE when used recursively
- {
-     char_u    buf1[NUMBUFLEN], buf2[NUMBUFLEN];
-     char_u    *s1, *s2;
-     static int  recursive_cnt = 0;        // catch recursive loops
-     int               r;
- 
-     // Catch lists and dicts that have an endless loop by limiting
-     // recursiveness to a limit.  We guess they are equal then.
-     // A fixed limit has the problem of still taking an awful long time.
-     // Reduce the limit every time running into it. That should work fine for
-     // deeply linked structures that are not recursively linked and catch
-     // recursiveness quickly.
-     if (!recursive)
-       tv_equal_recurse_limit = 1000;
-     if (recursive_cnt >= tv_equal_recurse_limit)
-     {
-       --tv_equal_recurse_limit;
-       return TRUE;
-     }
- 
-     // For VAR_FUNC and VAR_PARTIAL compare the function name, bound dict and
-     // arguments.
-     if ((tv1->v_type == VAR_FUNC
-               || (tv1->v_type == VAR_PARTIAL && tv1->vval.v_partial != NULL))
-           && (tv2->v_type == VAR_FUNC
-               || (tv2->v_type == VAR_PARTIAL && tv2->vval.v_partial != NULL)))
-     {
-       ++recursive_cnt;
-       r = func_equal(tv1, tv2, ic);
-       --recursive_cnt;
-       return r;
-     }
- 
-     if (tv1->v_type != tv2->v_type)
-       return FALSE;
- 
-     switch (tv1->v_type)
-     {
-       case VAR_LIST:
-           ++recursive_cnt;
-           r = list_equal(tv1->vval.v_list, tv2->vval.v_list, ic, TRUE);
-           --recursive_cnt;
-           return r;
- 
-       case VAR_DICT:
-           ++recursive_cnt;
-           r = dict_equal(tv1->vval.v_dict, tv2->vval.v_dict, ic, TRUE);
-           --recursive_cnt;
-           return r;
- 
-       case VAR_BLOB:
-           return blob_equal(tv1->vval.v_blob, tv2->vval.v_blob);
- 
-       case VAR_NUMBER:
-       case VAR_BOOL:
-       case VAR_SPECIAL:
-           return tv1->vval.v_number == tv2->vval.v_number;
- 
-       case VAR_STRING:
-           s1 = tv_get_string_buf(tv1, buf1);
-           s2 = tv_get_string_buf(tv2, buf2);
-           return ((ic ? MB_STRICMP(s1, s2) : STRCMP(s1, s2)) == 0);
- 
-       case VAR_FLOAT:
- #ifdef FEAT_FLOAT
-           return tv1->vval.v_float == tv2->vval.v_float;
- #endif
-       case VAR_JOB:
- #ifdef FEAT_JOB_CHANNEL
-           return tv1->vval.v_job == tv2->vval.v_job;
- #endif
-       case VAR_CHANNEL:
- #ifdef FEAT_JOB_CHANNEL
-           return tv1->vval.v_channel == tv2->vval.v_channel;
- #endif
- 
-       case VAR_PARTIAL:
-           return tv1->vval.v_partial == tv2->vval.v_partial;
- 
-       case VAR_FUNC:
-           return tv1->vval.v_string == tv2->vval.v_string;
- 
-       case VAR_UNKNOWN:
-       case VAR_ANY:
-       case VAR_VOID:
-           break;
-     }
- 
-     // VAR_UNKNOWN can be the result of a invalid expression, let's say it
-     // does not equal anything, not even itself.
-     return FALSE;
- }
- 
  /*
   * Return the next (unique) copy ID.
   * Used for serializing nested structures.
--- 3360,3365 ----
***************
*** 4694,4716 ****
  }
  
  /*
-  * Return a string with the string representation of a variable.
-  * If the memory is allocated "tofree" is set to it, otherwise NULL.
-  * "numbuf" is used for a number.
-  * Puts quotes around strings, so that they can be parsed back by eval().
-  * May return NULL.
-  */
-     char_u *
- tv2string(
-     typval_T  *tv,
-     char_u    **tofree,
-     char_u    *numbuf,
-     int               copyID)
- {
-     return echo_string_core(tv, tofree, numbuf, copyID, FALSE, TRUE, FALSE);
- }
- 
- /*
   * Return string "str" in ' quotes, doubling ' characters.
   * If "str" is NULL an empty string is assumed.
   * If "function" is TRUE make it function('string').
--- 4135,4140 ----
***************
*** 4792,4848 ****
  #endif
  
  /*
-  * Get the value of an environment variable.
-  * "arg" is pointing to the '$'.  It is advanced to after the name.
-  * If the environment variable was not set, silently assume it is empty.
-  * Return FAIL if the name is invalid.
-  */
-     int
- get_env_tv(char_u **arg, typval_T *rettv, int evaluate)
- {
-     char_u    *string = NULL;
-     int               len;
-     int               cc;
-     char_u    *name;
-     int               mustfree = FALSE;
- 
-     ++*arg;
-     name = *arg;
-     len = get_env_len(arg);
-     if (evaluate)
-     {
-       if (len == 0)
-           return FAIL; // invalid empty name
- 
-       cc = name[len];
-       name[len] = NUL;
-       // first try vim_getenv(), fast for normal environment vars
-       string = vim_getenv(name, &mustfree);
-       if (string != NULL && *string != NUL)
-       {
-           if (!mustfree)
-               string = vim_strsave(string);
-       }
-       else
-       {
-           if (mustfree)
-               vim_free(string);
- 
-           // next try expanding things like $VIM and ${HOME}
-           string = expand_env_save(name - 1);
-           if (string != NULL && *string == '$')
-               VIM_CLEAR(string);
-       }
-       name[len] = cc;
- 
-       rettv->v_type = VAR_STRING;
-       rettv->vval.v_string = string;
-     }
- 
-     return OK;
- }
- 
- /*
   * Translate a String variable into a position.
   * Returns NULL when there is an error.
   */
--- 4216,4221 ----
***************
*** 5424,5981 ****
  }
  
  /*
-  * Allocate memory for a variable type-value, and make it empty (0 or NULL
-  * value).
-  */
-     typval_T *
- alloc_tv(void)
- {
-     return ALLOC_CLEAR_ONE(typval_T);
- }
- 
- /*
-  * Allocate memory for a variable type-value, and assign a string to it.
-  * The string "s" must have been allocated, it is consumed.
-  * Return NULL for out of memory, the variable otherwise.
-  */
-     typval_T *
- alloc_string_tv(char_u *s)
- {
-     typval_T  *rettv;
- 
-     rettv = alloc_tv();
-     if (rettv != NULL)
-     {
-       rettv->v_type = VAR_STRING;
-       rettv->vval.v_string = s;
-     }
-     else
-       vim_free(s);
-     return rettv;
- }
- 
- /*
-  * Free the memory for a variable type-value.
-  */
-     void
- free_tv(typval_T *varp)
- {
-     if (varp != NULL)
-     {
-       switch (varp->v_type)
-       {
-           case VAR_FUNC:
-               func_unref(varp->vval.v_string);
-               // FALLTHROUGH
-           case VAR_STRING:
-               vim_free(varp->vval.v_string);
-               break;
-           case VAR_PARTIAL:
-               partial_unref(varp->vval.v_partial);
-               break;
-           case VAR_BLOB:
-               blob_unref(varp->vval.v_blob);
-               break;
-           case VAR_LIST:
-               list_unref(varp->vval.v_list);
-               break;
-           case VAR_DICT:
-               dict_unref(varp->vval.v_dict);
-               break;
-           case VAR_JOB:
- #ifdef FEAT_JOB_CHANNEL
-               job_unref(varp->vval.v_job);
-               break;
- #endif
-           case VAR_CHANNEL:
- #ifdef FEAT_JOB_CHANNEL
-               channel_unref(varp->vval.v_channel);
-               break;
- #endif
-           case VAR_NUMBER:
-           case VAR_FLOAT:
-           case VAR_ANY:
-           case VAR_UNKNOWN:
-           case VAR_VOID:
-           case VAR_BOOL:
-           case VAR_SPECIAL:
-               break;
-       }
-       vim_free(varp);
-     }
- }
- 
- /*
-  * Free the memory for a variable value and set the value to NULL or 0.
-  */
-     void
- clear_tv(typval_T *varp)
- {
-     if (varp != NULL)
-     {
-       switch (varp->v_type)
-       {
-           case VAR_FUNC:
-               func_unref(varp->vval.v_string);
-               // FALLTHROUGH
-           case VAR_STRING:
-               VIM_CLEAR(varp->vval.v_string);
-               break;
-           case VAR_PARTIAL:
-               partial_unref(varp->vval.v_partial);
-               varp->vval.v_partial = NULL;
-               break;
-           case VAR_BLOB:
-               blob_unref(varp->vval.v_blob);
-               varp->vval.v_blob = NULL;
-               break;
-           case VAR_LIST:
-               list_unref(varp->vval.v_list);
-               varp->vval.v_list = NULL;
-               break;
-           case VAR_DICT:
-               dict_unref(varp->vval.v_dict);
-               varp->vval.v_dict = NULL;
-               break;
-           case VAR_NUMBER:
-           case VAR_BOOL:
-           case VAR_SPECIAL:
-               varp->vval.v_number = 0;
-               break;
-           case VAR_FLOAT:
- #ifdef FEAT_FLOAT
-               varp->vval.v_float = 0.0;
-               break;
- #endif
-           case VAR_JOB:
- #ifdef FEAT_JOB_CHANNEL
-               job_unref(varp->vval.v_job);
-               varp->vval.v_job = NULL;
- #endif
-               break;
-           case VAR_CHANNEL:
- #ifdef FEAT_JOB_CHANNEL
-               channel_unref(varp->vval.v_channel);
-               varp->vval.v_channel = NULL;
- #endif
-           case VAR_UNKNOWN:
-           case VAR_ANY:
-           case VAR_VOID:
-               break;
-       }
-       varp->v_lock = 0;
-     }
- }
- 
- /*
-  * Set the value of a variable to NULL without freeing items.
-  */
-     void
- init_tv(typval_T *varp)
- {
-     if (varp != NULL)
-       CLEAR_POINTER(varp);
- }
- 
- /*
-  * Get the number value of a variable.
-  * If it is a String variable, uses vim_str2nr().
-  * For incompatible types, return 0.
-  * tv_get_number_chk() is similar to tv_get_number(), but informs the
-  * caller of incompatible types: it sets *denote to TRUE if "denote"
-  * is not NULL or returns -1 otherwise.
-  */
-     varnumber_T
- tv_get_number(typval_T *varp)
- {
-     int               error = FALSE;
- 
-     return tv_get_number_chk(varp, &error);   // return 0L on error
- }
- 
-     varnumber_T
- tv_get_number_chk(typval_T *varp, int *denote)
- {
-     varnumber_T       n = 0L;
- 
-     switch (varp->v_type)
-     {
-       case VAR_NUMBER:
-           return varp->vval.v_number;
-       case VAR_FLOAT:
- #ifdef FEAT_FLOAT
-           emsg(_("E805: Using a Float as a Number"));
-           break;
- #endif
-       case VAR_FUNC:
-       case VAR_PARTIAL:
-           emsg(_("E703: Using a Funcref as a Number"));
-           break;
-       case VAR_STRING:
-           if (varp->vval.v_string != NULL)
-               vim_str2nr(varp->vval.v_string, NULL, NULL,
-                                           STR2NR_ALL, &n, NULL, 0, FALSE);
-           return n;
-       case VAR_LIST:
-           emsg(_("E745: Using a List as a Number"));
-           break;
-       case VAR_DICT:
-           emsg(_("E728: Using a Dictionary as a Number"));
-           break;
-       case VAR_BOOL:
-       case VAR_SPECIAL:
-           return varp->vval.v_number == VVAL_TRUE ? 1 : 0;
-       case VAR_JOB:
- #ifdef FEAT_JOB_CHANNEL
-           emsg(_("E910: Using a Job as a Number"));
-           break;
- #endif
-       case VAR_CHANNEL:
- #ifdef FEAT_JOB_CHANNEL
-           emsg(_("E913: Using a Channel as a Number"));
-           break;
- #endif
-       case VAR_BLOB:
-           emsg(_("E974: Using a Blob as a Number"));
-           break;
-       case VAR_UNKNOWN:
-       case VAR_ANY:
-       case VAR_VOID:
-           internal_error_no_abort("tv_get_number(UNKNOWN)");
-           break;
-     }
-     if (denote == NULL)               // useful for values that must be 
unsigned
-       n = -1;
-     else
-       *denote = TRUE;
-     return n;
- }
- 
- #ifdef FEAT_FLOAT
-     float_T
- tv_get_float(typval_T *varp)
- {
-     switch (varp->v_type)
-     {
-       case VAR_NUMBER:
-           return (float_T)(varp->vval.v_number);
-       case VAR_FLOAT:
-           return varp->vval.v_float;
-       case VAR_FUNC:
-       case VAR_PARTIAL:
-           emsg(_("E891: Using a Funcref as a Float"));
-           break;
-       case VAR_STRING:
-           emsg(_("E892: Using a String as a Float"));
-           break;
-       case VAR_LIST:
-           emsg(_("E893: Using a List as a Float"));
-           break;
-       case VAR_DICT:
-           emsg(_("E894: Using a Dictionary as a Float"));
-           break;
-       case VAR_BOOL:
-           emsg(_("E362: Using a boolean value as a Float"));
-           break;
-       case VAR_SPECIAL:
-           emsg(_("E907: Using a special value as a Float"));
-           break;
-       case VAR_JOB:
- # ifdef FEAT_JOB_CHANNEL
-           emsg(_("E911: Using a Job as a Float"));
-           break;
- # endif
-       case VAR_CHANNEL:
- # ifdef FEAT_JOB_CHANNEL
-           emsg(_("E914: Using a Channel as a Float"));
-           break;
- # endif
-       case VAR_BLOB:
-           emsg(_("E975: Using a Blob as a Float"));
-           break;
-       case VAR_UNKNOWN:
-       case VAR_ANY:
-       case VAR_VOID:
-           internal_error_no_abort("tv_get_float(UNKNOWN)");
-           break;
-     }
-     return 0;
- }
- #endif
- 
- /*
-  * Get the string value of a variable.
-  * If it is a Number variable, the number is converted into a string.
-  * tv_get_string() uses a single, static buffer.  YOU CAN ONLY USE IT ONCE!
-  * tv_get_string_buf() uses a given buffer.
-  * If the String variable has never been set, return an empty string.
-  * Never returns NULL;
-  * tv_get_string_chk() and tv_get_string_buf_chk() are similar, but return
-  * NULL on error.
-  */
-     char_u *
- tv_get_string(typval_T *varp)
- {
-     static char_u   mybuf[NUMBUFLEN];
- 
-     return tv_get_string_buf(varp, mybuf);
- }
- 
-     char_u *
- tv_get_string_buf(typval_T *varp, char_u *buf)
- {
-     char_u    *res =  tv_get_string_buf_chk(varp, buf);
- 
-     return res != NULL ? res : (char_u *)"";
- }
- 
- /*
-  * Careful: This uses a single, static buffer.  YOU CAN ONLY USE IT ONCE!
-  */
-     char_u *
- tv_get_string_chk(typval_T *varp)
- {
-     static char_u   mybuf[NUMBUFLEN];
- 
-     return tv_get_string_buf_chk(varp, mybuf);
- }
- 
-     char_u *
- tv_get_string_buf_chk(typval_T *varp, char_u *buf)
- {
-     switch (varp->v_type)
-     {
-       case VAR_NUMBER:
-           vim_snprintf((char *)buf, NUMBUFLEN, "%lld",
-                                           (varnumber_T)varp->vval.v_number);
-           return buf;
-       case VAR_FUNC:
-       case VAR_PARTIAL:
-           emsg(_("E729: using Funcref as a String"));
-           break;
-       case VAR_LIST:
-           emsg(_("E730: using List as a String"));
-           break;
-       case VAR_DICT:
-           emsg(_("E731: using Dictionary as a String"));
-           break;
-       case VAR_FLOAT:
- #ifdef FEAT_FLOAT
-           emsg(_(e_float_as_string));
-           break;
- #endif
-       case VAR_STRING:
-           if (varp->vval.v_string != NULL)
-               return varp->vval.v_string;
-           return (char_u *)"";
-       case VAR_BOOL:
-       case VAR_SPECIAL:
-           STRCPY(buf, get_var_special_name(varp->vval.v_number));
-           return buf;
-         case VAR_BLOB:
-           emsg(_("E976: using Blob as a String"));
-           break;
-       case VAR_JOB:
- #ifdef FEAT_JOB_CHANNEL
-           {
-               job_T *job = varp->vval.v_job;
-               char  *status;
- 
-               if (job == NULL)
-                   return (char_u *)"no process";
-               status = job->jv_status == JOB_FAILED ? "fail"
-                               : job->jv_status >= JOB_ENDED ? "dead"
-                               : "run";
- # ifdef UNIX
-               vim_snprintf((char *)buf, NUMBUFLEN,
-                           "process %ld %s", (long)job->jv_pid, status);
- # elif defined(MSWIN)
-               vim_snprintf((char *)buf, NUMBUFLEN,
-                           "process %ld %s",
-                           (long)job->jv_proc_info.dwProcessId,
-                           status);
- # else
-               // fall-back
-               vim_snprintf((char *)buf, NUMBUFLEN, "process ? %s", status);
- # endif
-               return buf;
-           }
- #endif
-           break;
-       case VAR_CHANNEL:
- #ifdef FEAT_JOB_CHANNEL
-           {
-               channel_T *channel = varp->vval.v_channel;
-               char      *status = channel_status(channel, -1);
- 
-               if (channel == NULL)
-                   vim_snprintf((char *)buf, NUMBUFLEN, "channel %s", status);
-               else
-                   vim_snprintf((char *)buf, NUMBUFLEN,
-                                    "channel %d %s", channel->ch_id, status);
-               return buf;
-           }
- #endif
-           break;
-       case VAR_UNKNOWN:
-       case VAR_ANY:
-       case VAR_VOID:
-           emsg(_(e_inval_string));
-           break;
-     }
-     return NULL;
- }
- 
- /*
-  * Turn a typeval into a string.  Similar to tv_get_string_buf() but uses
-  * string() on Dict, List, etc.
-  */
-     static char_u *
- tv_stringify(typval_T *varp, char_u *buf)
- {
-     if (varp->v_type == VAR_LIST
-           || varp->v_type == VAR_DICT
-           || varp->v_type == VAR_BLOB
-           || varp->v_type == VAR_FUNC
-           || varp->v_type == VAR_PARTIAL
-           || varp->v_type == VAR_FLOAT)
-     {
-       typval_T tmp;
- 
-       f_string(varp, &tmp);
-       tv_get_string_buf(&tmp, buf);
-       clear_tv(varp);
-       *varp = tmp;
-       return tmp.vval.v_string;
-     }
-     return tv_get_string_buf(varp, buf);
- }
- 
- /*
-  * Return TRUE if typeval "tv" and its value are set to be locked (immutable).
-  * Also give an error message, using "name" or _("name") when use_gettext is
-  * TRUE.
-  */
-     static int
- tv_check_lock(typval_T *tv, char_u *name, int use_gettext)
- {
-     int       lock = 0;
- 
-     switch (tv->v_type)
-     {
-       case VAR_BLOB:
-           if (tv->vval.v_blob != NULL)
-               lock = tv->vval.v_blob->bv_lock;
-           break;
-       case VAR_LIST:
-           if (tv->vval.v_list != NULL)
-               lock = tv->vval.v_list->lv_lock;
-           break;
-       case VAR_DICT:
-           if (tv->vval.v_dict != NULL)
-               lock = tv->vval.v_dict->dv_lock;
-           break;
-       default:
-           break;
-     }
-     return var_check_lock(tv->v_lock, name, use_gettext)
-                   || (lock != 0 && var_check_lock(lock, name, use_gettext));
- }
- 
- /*
-  * Copy the values from typval_T "from" to typval_T "to".
-  * When needed allocates string or increases reference count.
-  * Does not make a copy of a list, blob or dict but copies the reference!
-  * It is OK for "from" and "to" to point to the same item.  This is used to
-  * make a copy later.
-  */
-     void
- copy_tv(typval_T *from, typval_T *to)
- {
-     to->v_type = from->v_type;
-     to->v_lock = 0;
-     switch (from->v_type)
-     {
-       case VAR_NUMBER:
-       case VAR_BOOL:
-       case VAR_SPECIAL:
-           to->vval.v_number = from->vval.v_number;
-           break;
-       case VAR_FLOAT:
- #ifdef FEAT_FLOAT
-           to->vval.v_float = from->vval.v_float;
-           break;
- #endif
-       case VAR_JOB:
- #ifdef FEAT_JOB_CHANNEL
-           to->vval.v_job = from->vval.v_job;
-           if (to->vval.v_job != NULL)
-               ++to->vval.v_job->jv_refcount;
-           break;
- #endif
-       case VAR_CHANNEL:
- #ifdef FEAT_JOB_CHANNEL
-           to->vval.v_channel = from->vval.v_channel;
-           if (to->vval.v_channel != NULL)
-               ++to->vval.v_channel->ch_refcount;
-           break;
- #endif
-       case VAR_STRING:
-       case VAR_FUNC:
-           if (from->vval.v_string == NULL)
-               to->vval.v_string = NULL;
-           else
-           {
-               to->vval.v_string = vim_strsave(from->vval.v_string);
-               if (from->v_type == VAR_FUNC)
-                   func_ref(to->vval.v_string);
-           }
-           break;
-       case VAR_PARTIAL:
-           if (from->vval.v_partial == NULL)
-               to->vval.v_partial = NULL;
-           else
-           {
-               to->vval.v_partial = from->vval.v_partial;
-               ++to->vval.v_partial->pt_refcount;
-           }
-           break;
-       case VAR_BLOB:
-           if (from->vval.v_blob == NULL)
-               to->vval.v_blob = NULL;
-           else
-           {
-               to->vval.v_blob = from->vval.v_blob;
-               ++to->vval.v_blob->bv_refcount;
-           }
-           break;
-       case VAR_LIST:
-           if (from->vval.v_list == NULL)
-               to->vval.v_list = NULL;
-           else
-           {
-               to->vval.v_list = from->vval.v_list;
-               ++to->vval.v_list->lv_refcount;
-           }
-           break;
-       case VAR_DICT:
-           if (from->vval.v_dict == NULL)
-               to->vval.v_dict = NULL;
-           else
-           {
-               to->vval.v_dict = from->vval.v_dict;
-               ++to->vval.v_dict->dv_refcount;
-           }
-           break;
-       case VAR_UNKNOWN:
-       case VAR_ANY:
-       case VAR_VOID:
-           internal_error_no_abort("copy_tv(UNKNOWN)");
-           break;
-     }
- }
- 
- /*
   * Make a copy of an item.
   * Lists and Dictionaries are also copied.  A deep copy if "deep" is set.
   * For deepcopy() "copyID" is zero for a full copy or the ID for when a
--- 4797,4802 ----
***************
*** 6356,6610 ****
      }
  }
  
- /*
-  * Compare "typ1" and "typ2".  Put the result in "typ1".
-  */
-     int
- typval_compare(
-     typval_T  *typ1,   // first operand
-     typval_T  *typ2,   // second operand
-     exptype_T type,    // operator
-     int               ic)      // ignore case
- {
-     int               i;
-     varnumber_T       n1, n2;
-     char_u    *s1, *s2;
-     char_u    buf1[NUMBUFLEN], buf2[NUMBUFLEN];
-     int               type_is = type == EXPR_IS || type == EXPR_ISNOT;
- 
-     if (type_is && typ1->v_type != typ2->v_type)
-     {
-       // For "is" a different type always means FALSE, for "notis"
-       // it means TRUE.
-       n1 = (type == EXPR_ISNOT);
-     }
-     else if (typ1->v_type == VAR_BLOB || typ2->v_type == VAR_BLOB)
-     {
-       if (type_is)
-       {
-           n1 = (typ1->v_type == typ2->v_type
-                           && typ1->vval.v_blob == typ2->vval.v_blob);
-           if (type == EXPR_ISNOT)
-               n1 = !n1;
-       }
-       else if (typ1->v_type != typ2->v_type
-               || (type != EXPR_EQUAL && type != EXPR_NEQUAL))
-       {
-           if (typ1->v_type != typ2->v_type)
-               emsg(_("E977: Can only compare Blob with Blob"));
-           else
-               emsg(_(e_invalblob));
-           clear_tv(typ1);
-           return FAIL;
-       }
-       else
-       {
-           // Compare two Blobs for being equal or unequal.
-           n1 = blob_equal(typ1->vval.v_blob, typ2->vval.v_blob);
-           if (type == EXPR_NEQUAL)
-               n1 = !n1;
-       }
-     }
-     else if (typ1->v_type == VAR_LIST || typ2->v_type == VAR_LIST)
-     {
-       if (type_is)
-       {
-           n1 = (typ1->v_type == typ2->v_type
-                           && typ1->vval.v_list == typ2->vval.v_list);
-           if (type == EXPR_ISNOT)
-               n1 = !n1;
-       }
-       else if (typ1->v_type != typ2->v_type
-               || (type != EXPR_EQUAL && type != EXPR_NEQUAL))
-       {
-           if (typ1->v_type != typ2->v_type)
-               emsg(_("E691: Can only compare List with List"));
-           else
-               emsg(_("E692: Invalid operation for List"));
-           clear_tv(typ1);
-           return FAIL;
-       }
-       else
-       {
-           // Compare two Lists for being equal or unequal.
-           n1 = list_equal(typ1->vval.v_list, typ2->vval.v_list,
-                                                           ic, FALSE);
-           if (type == EXPR_NEQUAL)
-               n1 = !n1;
-       }
-     }
- 
-     else if (typ1->v_type == VAR_DICT || typ2->v_type == VAR_DICT)
-     {
-       if (type_is)
-       {
-           n1 = (typ1->v_type == typ2->v_type
-                           && typ1->vval.v_dict == typ2->vval.v_dict);
-           if (type == EXPR_ISNOT)
-               n1 = !n1;
-       }
-       else if (typ1->v_type != typ2->v_type
-               || (type != EXPR_EQUAL && type != EXPR_NEQUAL))
-       {
-           if (typ1->v_type != typ2->v_type)
-               emsg(_("E735: Can only compare Dictionary with Dictionary"));
-           else
-               emsg(_("E736: Invalid operation for Dictionary"));
-           clear_tv(typ1);
-           return FAIL;
-       }
-       else
-       {
-           // Compare two Dictionaries for being equal or unequal.
-           n1 = dict_equal(typ1->vval.v_dict, typ2->vval.v_dict,
-                                                           ic, FALSE);
-           if (type == EXPR_NEQUAL)
-               n1 = !n1;
-       }
-     }
- 
-     else if (typ1->v_type == VAR_FUNC || typ2->v_type == VAR_FUNC
-       || typ1->v_type == VAR_PARTIAL || typ2->v_type == VAR_PARTIAL)
-     {
-       if (type != EXPR_EQUAL && type != EXPR_NEQUAL
-               && type != EXPR_IS && type != EXPR_ISNOT)
-       {
-           emsg(_("E694: Invalid operation for Funcrefs"));
-           clear_tv(typ1);
-           return FAIL;
-       }
-       if ((typ1->v_type == VAR_PARTIAL
-                                       && typ1->vval.v_partial == NULL)
-               || (typ2->v_type == VAR_PARTIAL
-                                       && typ2->vval.v_partial == NULL))
-           // When both partials are NULL, then they are equal.
-           // Otherwise they are not equal.
-           n1 = (typ1->vval.v_partial == typ2->vval.v_partial);
-       else if (type_is)
-       {
-           if (typ1->v_type == VAR_FUNC && typ2->v_type == VAR_FUNC)
-               // strings are considered the same if their value is
-               // the same
-               n1 = tv_equal(typ1, typ2, ic, FALSE);
-           else if (typ1->v_type == VAR_PARTIAL
-                                       && typ2->v_type == VAR_PARTIAL)
-               n1 = (typ1->vval.v_partial == typ2->vval.v_partial);
-           else
-               n1 = FALSE;
-       }
-       else
-           n1 = tv_equal(typ1, typ2, ic, FALSE);
-       if (type == EXPR_NEQUAL || type == EXPR_ISNOT)
-           n1 = !n1;
-     }
- 
- #ifdef FEAT_FLOAT
-     /*
-       * If one of the two variables is a float, compare as a float.
-       * When using "=~" or "!~", always compare as string.
-       */
-     else if ((typ1->v_type == VAR_FLOAT || typ2->v_type == VAR_FLOAT)
-           && type != EXPR_MATCH && type != EXPR_NOMATCH)
-     {
-       float_T f1, f2;
- 
-       f1 = tv_get_float(typ1);
-       f2 = tv_get_float(typ2);
-       n1 = FALSE;
-       switch (type)
-       {
-           case EXPR_IS:
-           case EXPR_EQUAL:    n1 = (f1 == f2); break;
-           case EXPR_ISNOT:
-           case EXPR_NEQUAL:   n1 = (f1 != f2); break;
-           case EXPR_GREATER:  n1 = (f1 > f2); break;
-           case EXPR_GEQUAL:   n1 = (f1 >= f2); break;
-           case EXPR_SMALLER:  n1 = (f1 < f2); break;
-           case EXPR_SEQUAL:   n1 = (f1 <= f2); break;
-           case EXPR_UNKNOWN:
-           case EXPR_MATCH:
-           default:  break;  // avoid gcc warning
-       }
-     }
- #endif
- 
-     /*
-      * If one of the two variables is a number, compare as a number.
-      * When using "=~" or "!~", always compare as string.
-      */
-     else if ((typ1->v_type == VAR_NUMBER || typ2->v_type == VAR_NUMBER)
-           && type != EXPR_MATCH && type != EXPR_NOMATCH)
-     {
-       n1 = tv_get_number(typ1);
-       n2 = tv_get_number(typ2);
-       switch (type)
-       {
-           case EXPR_IS:
-           case EXPR_EQUAL:    n1 = (n1 == n2); break;
-           case EXPR_ISNOT:
-           case EXPR_NEQUAL:   n1 = (n1 != n2); break;
-           case EXPR_GREATER:  n1 = (n1 > n2); break;
-           case EXPR_GEQUAL:   n1 = (n1 >= n2); break;
-           case EXPR_SMALLER:  n1 = (n1 < n2); break;
-           case EXPR_SEQUAL:   n1 = (n1 <= n2); break;
-           case EXPR_UNKNOWN:
-           case EXPR_MATCH:
-           default:  break;  // avoid gcc warning
-       }
-     }
-     else
-     {
-       s1 = tv_get_string_buf(typ1, buf1);
-       s2 = tv_get_string_buf(typ2, buf2);
-       if (type != EXPR_MATCH && type != EXPR_NOMATCH)
-           i = ic ? MB_STRICMP(s1, s2) : STRCMP(s1, s2);
-       else
-           i = 0;
-       n1 = FALSE;
-       switch (type)
-       {
-           case EXPR_IS:
-           case EXPR_EQUAL:    n1 = (i == 0); break;
-           case EXPR_ISNOT:
-           case EXPR_NEQUAL:   n1 = (i != 0); break;
-           case EXPR_GREATER:  n1 = (i > 0); break;
-           case EXPR_GEQUAL:   n1 = (i >= 0); break;
-           case EXPR_SMALLER:  n1 = (i < 0); break;
-           case EXPR_SEQUAL:   n1 = (i <= 0); break;
- 
-           case EXPR_MATCH:
-           case EXPR_NOMATCH:
-                   n1 = pattern_match(s2, s1, ic);
-                   if (type == EXPR_NOMATCH)
-                       n1 = !n1;
-                   break;
- 
-           default:  break;  // avoid gcc warning
-       }
-     }
-     clear_tv(typ1);
-     typ1->v_type = VAR_NUMBER;
-     typ1->vval.v_number = n1;
- 
-     return OK;
- }
- 
-     char_u *
- typval_tostring(typval_T *arg)
- {
-     char_u    *tofree;
-     char_u    numbuf[NUMBUFLEN];
-     char_u    *ret = NULL;
- 
-     if (arg == NULL)
-       return vim_strsave((char_u *)"(does not exist)");
-     ret = tv2string(arg, &tofree, numbuf, 0);
-     // Make a copy if we have a value but it's not in allocated memory.
-     if (ret != NULL && tofree == NULL)
-       ret = vim_strsave(ret);
-     return ret;
- }
- 
  #endif // FEAT_EVAL
  
  /*
--- 5177,5182 ----
*** ../vim-8.2.0846/src/evalfunc.c      2020-05-23 19:30:01.814746167 +0200
--- src/evalfunc.c      2020-05-30 16:28:14.593109254 +0200
***************
*** 1269,1312 ****
                && *argvars[0].vval.v_string != NUL));
  }
  
- /*
-  * Get the lnum from the first argument.
-  * Also accepts ".", "$", etc., but that only works for the current buffer.
-  * Returns -1 on error.
-  */
-     linenr_T
- tv_get_lnum(typval_T *argvars)
- {
-     linenr_T  lnum;
- 
-     lnum = (linenr_T)tv_get_number_chk(&argvars[0], NULL);
-     if (lnum == 0)  // no valid number, try using arg like line()
-     {
-       int     fnum;
-       pos_T   *fp = var2fpos(&argvars[0], TRUE, &fnum);
- 
-       if (fp != NULL)
-           lnum = fp->lnum;
-     }
-     return lnum;
- }
- 
- /*
-  * Get the lnum from the first argument.
-  * Also accepts "$", then "buf" is used.
-  * Returns 0 on error.
-  */
-     linenr_T
- tv_get_lnum_buf(typval_T *argvars, buf_T *buf)
- {
-     if (argvars[0].v_type == VAR_STRING
-           && argvars[0].vval.v_string != NULL
-           && argvars[0].vval.v_string[0] == '$'
-           && buf != NULL)
-       return buf->b_ml.ml_line_count;
-     return (linenr_T)tv_get_number_chk(&argvars[0], NULL);
- }
- 
  #ifdef FEAT_FLOAT
  /*
   * Get the float value of "argvars[0]" into "f".
--- 1269,1274 ----
***************
*** 1501,1533 ****
  #endif
  
  /*
-  * Get buffer by number or pattern.
-  */
-     buf_T *
- tv_get_buf(typval_T *tv, int curtab_only)
- {
-     char_u    *name = tv->vval.v_string;
-     buf_T     *buf;
- 
-     if (tv->v_type == VAR_NUMBER)
-       return buflist_findnr((int)tv->vval.v_number);
-     if (tv->v_type != VAR_STRING)
-       return NULL;
-     if (name == NULL || *name == NUL)
-       return curbuf;
-     if (name[0] == '$' && name[1] == NUL)
-       return lastbuf;
- 
-     buf = buflist_find_by_name(name, curtab_only);
- 
-     // If not found, try expanding the name, like done for bufexists().
-     if (buf == NULL)
-       buf = find_buffer(tv);
- 
-     return buf;
- }
- 
- /*
   * Get the buffer from "arg" and give an error and return NULL if it is not
   * valid.
   */
--- 1463,1468 ----
***************
*** 5106,5127 ****
  }
  
  /*
-  * Return TRUE if typeval "tv" is locked: Either that value is locked itself
-  * or it refers to a List or Dictionary that is locked.
-  */
-     static int
- tv_islocked(typval_T *tv)
- {
-     return (tv->v_lock & VAR_LOCKED)
-       || (tv->v_type == VAR_LIST
-               && tv->vval.v_list != NULL
-               && (tv->vval.v_list->lv_lock & VAR_LOCKED))
-       || (tv->v_type == VAR_DICT
-               && tv->vval.v_dict != NULL
-               && (tv->vval.v_dict->dv_lock & VAR_LOCKED));
- }
- 
- /*
   * "islocked()" function
   */
      static void
--- 5041,5046 ----
*** ../vim-8.2.0846/src/globals.h       2020-05-29 22:41:36.929691032 +0200
--- src/globals.h       2020-05-30 16:28:14.593109254 +0200
***************
*** 1748,1753 ****
--- 1748,1756 ----
  #ifndef FEAT_CLIPBOARD
  EXTERN char e_invalidreg[]    INIT(= N_("E850: Invalid register name"));
  #endif
+ #ifdef FEAT_FLOAT
+ EXTERN char e_float_as_string[] INIT(= N_("E806: using Float as a String"));
+ #endif
  EXTERN char e_dirnotf[]       INIT(= N_("E919: Directory not found in '%s': 
\"%s\""));
  EXTERN char e_au_recursive[]  INIT(= N_("E952: Autocommand caused recursive 
behavior"));
  #ifdef FEAT_MENU
*** ../vim-8.2.0846/src/proto.h 2020-05-01 14:26:17.132949262 +0200
--- src/proto.h 2020-05-30 16:28:14.593109254 +0200
***************
*** 226,231 ****
--- 226,232 ----
  # include "textobject.pro"
  # include "textformat.pro"
  # include "time.pro"
+ # include "typval.pro"
  # include "ui.pro"
  # include "undo.pro"
  # include "usercmd.pro"
*** ../vim-8.2.0846/src/proto/eval.pro  2020-01-26 15:52:33.023833239 +0100
--- src/proto/eval.pro  2020-05-30 16:28:14.593109254 +0200
***************
*** 29,41 ****
  int eval1(char_u **arg, typval_T *rettv, int evaluate);
  void eval_addblob(typval_T *tv1, typval_T *tv2);
  int eval_addlist(typval_T *tv1, typval_T *tv2);
- int get_option_tv(char_u **arg, typval_T *rettv, int evaluate);
- int get_number_tv(char_u **arg, typval_T *rettv, int evaluate, int 
want_string);
- int get_string_tv(char_u **arg, typval_T *rettv, int evaluate);
- int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate);
  char_u *partial_name(partial_T *pt);
  void partial_unref(partial_T *pt);
- int tv_equal(typval_T *tv1, typval_T *tv2, int ic, int recursive);
  int get_copyID(void);
  int garbage_collect(int testing);
  int set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack);
--- 29,36 ----
***************
*** 45,54 ****
  int set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, 
list_stack_T **list_stack);
  char_u *echo_string_core(typval_T *tv, char_u **tofree, char_u *numbuf, int 
copyID, int echo_style, int restore_copyID, int composite_val);
  char_u *echo_string(typval_T *tv, char_u **tofree, char_u *numbuf, int 
copyID);
- char_u *tv2string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID);
  char_u *string_quote(char_u *str, int function);
  int string2float(char_u *text, float_T *value);
- int get_env_tv(char_u **arg, typval_T *rettv, int evaluate);
  pos_T *var2fpos(typval_T *varp, int dollar_lnum, int *fnum);
  int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp);
  int get_env_len(char_u **arg);
--- 40,47 ----
***************
*** 58,76 ****
  int eval_isnamec(int c);
  int eval_isnamec1(int c);
  int handle_subscript(char_u **arg, typval_T *rettv, int evaluate, int 
verbose, char_u *start_leader, char_u **end_leaderp);
- typval_T *alloc_tv(void);
- typval_T *alloc_string_tv(char_u *s);
- void free_tv(typval_T *varp);
- void clear_tv(typval_T *varp);
- void init_tv(typval_T *varp);
- varnumber_T tv_get_number(typval_T *varp);
- varnumber_T tv_get_number_chk(typval_T *varp, int *denote);
- float_T tv_get_float(typval_T *varp);
- char_u *tv_get_string(typval_T *varp);
- char_u *tv_get_string_buf(typval_T *varp, char_u *buf);
- char_u *tv_get_string_chk(typval_T *varp);
- char_u *tv_get_string_buf_chk(typval_T *varp, char_u *buf);
- void copy_tv(typval_T *from, typval_T *to);
  int item_copy(typval_T *from, typval_T *to, int deep, int copyID);
  void echo_one(typval_T *rettv, int with_space, int *atstart, int *needclr);
  void ex_echo(exarg_T *eap);
--- 51,56 ----
***************
*** 79,85 ****
  void ex_execute(exarg_T *eap);
  char_u *find_option_end(char_u **arg, int *opt_flags);
  void last_set_msg(sctx_T script_ctx);
- int typval_compare(typval_T *typ1, typval_T *typ2, exptype_T type, int ic);
- char_u *typval_tostring(typval_T *arg);
  char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, typval_T *expr, 
char_u *flags);
  /* vim: set ft=c : */
--- 59,63 ----
*** ../vim-8.2.0846/src/proto/evalfunc.pro      2020-03-01 14:04:42.224689018 
+0100
--- src/proto/evalfunc.pro      2020-05-30 16:28:14.593109254 +0200
***************
*** 10,18 ****
  void call_internal_func_by_idx(int idx, typval_T *argvars, typval_T *rettv);
  int call_internal_method(char_u *name, int argcount, typval_T *argvars, 
typval_T *rettv, typval_T *basetv);
  int non_zero_arg(typval_T *argvars);
- linenr_T tv_get_lnum(typval_T *argvars);
- linenr_T tv_get_lnum_buf(typval_T *argvars, buf_T *buf);
- buf_T *tv_get_buf(typval_T *tv, int curtab_only);
  buf_T *get_buf_arg(typval_T *arg);
  win_T *get_optional_window(typval_T *argvars, int idx);
  void execute_redir_str(char_u *value, int value_len);
--- 10,15 ----
*** ../vim-8.2.0846/src/proto/typval.pro        2020-05-30 17:04:40.172983436 
+0200
--- src/proto/typval.pro        2020-05-30 16:28:14.593109254 +0200
***************
*** 0 ****
--- 1,30 ----
+ /* typval.c */
+ typval_T *alloc_tv(void);
+ typval_T *alloc_string_tv(char_u *s);
+ void free_tv(typval_T *varp);
+ void clear_tv(typval_T *varp);
+ void init_tv(typval_T *varp);
+ int tv_check_lock(typval_T *tv, char_u *name, int use_gettext);
+ void copy_tv(typval_T *from, typval_T *to);
+ int typval_compare(typval_T *typ1, typval_T *typ2, exptype_T type, int ic);
+ char_u *typval_tostring(typval_T *arg);
+ int tv_islocked(typval_T *tv);
+ int tv_equal(typval_T *tv1, typval_T *tv2, int ic, int recursive);
+ int get_option_tv(char_u **arg, typval_T *rettv, int evaluate);
+ int get_number_tv(char_u **arg, typval_T *rettv, int evaluate, int 
want_string);
+ int get_string_tv(char_u **arg, typval_T *rettv, int evaluate);
+ int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate);
+ char_u *tv2string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID);
+ int get_env_tv(char_u **arg, typval_T *rettv, int evaluate);
+ varnumber_T tv_get_number(typval_T *varp);
+ varnumber_T tv_get_number_chk(typval_T *varp, int *denote);
+ float_T tv_get_float(typval_T *varp);
+ char_u *tv_get_string(typval_T *varp);
+ char_u *tv_get_string_buf(typval_T *varp, char_u *buf);
+ char_u *tv_get_string_chk(typval_T *varp);
+ char_u *tv_get_string_buf_chk(typval_T *varp, char_u *buf);
+ linenr_T tv_get_lnum(typval_T *argvars);
+ linenr_T tv_get_lnum_buf(typval_T *argvars, buf_T *buf);
+ buf_T *tv_get_buf(typval_T *tv, int curtab_only);
+ char_u *tv_stringify(typval_T *varp, char_u *buf);
+ /* vim: set ft=c : */
*** ../vim-8.2.0846/src/typval.c        2020-05-30 17:04:40.176983421 +0200
--- src/typval.c        2020-05-30 17:02:01.113583526 +0200
***************
*** 0 ****
--- 1,1508 ----
+ /* vi:set ts=8 sts=4 sw=4 noet:
+  *
+  * VIM - Vi IMproved  by Bram Moolenaar
+  *
+  * Do ":help uganda"  in Vim to read copying and usage conditions.
+  * Do ":help credits" in Vim to see a list of people who contributed.
+  * See README.txt for an overview of the Vim source code.
+  */
+ 
+ /*
+  * typval.c: functions that deal with a typval
+  */
+ 
+ #include "vim.h"
+ 
+ #if defined(FEAT_EVAL) || defined(PROTO)
+ 
+ /*
+  * Allocate memory for a variable type-value, and make it empty (0 or NULL
+  * value).
+  */
+     typval_T *
+ alloc_tv(void)
+ {
+     return ALLOC_CLEAR_ONE(typval_T);
+ }
+ 
+ /*
+  * Allocate memory for a variable type-value, and assign a string to it.
+  * The string "s" must have been allocated, it is consumed.
+  * Return NULL for out of memory, the variable otherwise.
+  */
+     typval_T *
+ alloc_string_tv(char_u *s)
+ {
+     typval_T  *rettv;
+ 
+     rettv = alloc_tv();
+     if (rettv != NULL)
+     {
+       rettv->v_type = VAR_STRING;
+       rettv->vval.v_string = s;
+     }
+     else
+       vim_free(s);
+     return rettv;
+ }
+ 
+ /*
+  * Free the memory for a variable type-value.
+  */
+     void
+ free_tv(typval_T *varp)
+ {
+     if (varp != NULL)
+     {
+       switch (varp->v_type)
+       {
+           case VAR_FUNC:
+               func_unref(varp->vval.v_string);
+               // FALLTHROUGH
+           case VAR_STRING:
+               vim_free(varp->vval.v_string);
+               break;
+           case VAR_PARTIAL:
+               partial_unref(varp->vval.v_partial);
+               break;
+           case VAR_BLOB:
+               blob_unref(varp->vval.v_blob);
+               break;
+           case VAR_LIST:
+               list_unref(varp->vval.v_list);
+               break;
+           case VAR_DICT:
+               dict_unref(varp->vval.v_dict);
+               break;
+           case VAR_JOB:
+ #ifdef FEAT_JOB_CHANNEL
+               job_unref(varp->vval.v_job);
+               break;
+ #endif
+           case VAR_CHANNEL:
+ #ifdef FEAT_JOB_CHANNEL
+               channel_unref(varp->vval.v_channel);
+               break;
+ #endif
+           case VAR_NUMBER:
+           case VAR_FLOAT:
+           case VAR_ANY:
+           case VAR_UNKNOWN:
+           case VAR_VOID:
+           case VAR_BOOL:
+           case VAR_SPECIAL:
+               break;
+       }
+       vim_free(varp);
+     }
+ }
+ 
+ /*
+  * Free the memory for a variable value and set the value to NULL or 0.
+  */
+     void
+ clear_tv(typval_T *varp)
+ {
+     if (varp != NULL)
+     {
+       switch (varp->v_type)
+       {
+           case VAR_FUNC:
+               func_unref(varp->vval.v_string);
+               // FALLTHROUGH
+           case VAR_STRING:
+               VIM_CLEAR(varp->vval.v_string);
+               break;
+           case VAR_PARTIAL:
+               partial_unref(varp->vval.v_partial);
+               varp->vval.v_partial = NULL;
+               break;
+           case VAR_BLOB:
+               blob_unref(varp->vval.v_blob);
+               varp->vval.v_blob = NULL;
+               break;
+           case VAR_LIST:
+               list_unref(varp->vval.v_list);
+               varp->vval.v_list = NULL;
+               break;
+           case VAR_DICT:
+               dict_unref(varp->vval.v_dict);
+               varp->vval.v_dict = NULL;
+               break;
+           case VAR_NUMBER:
+           case VAR_BOOL:
+           case VAR_SPECIAL:
+               varp->vval.v_number = 0;
+               break;
+           case VAR_FLOAT:
+ #ifdef FEAT_FLOAT
+               varp->vval.v_float = 0.0;
+               break;
+ #endif
+           case VAR_JOB:
+ #ifdef FEAT_JOB_CHANNEL
+               job_unref(varp->vval.v_job);
+               varp->vval.v_job = NULL;
+ #endif
+               break;
+           case VAR_CHANNEL:
+ #ifdef FEAT_JOB_CHANNEL
+               channel_unref(varp->vval.v_channel);
+               varp->vval.v_channel = NULL;
+ #endif
+           case VAR_UNKNOWN:
+           case VAR_ANY:
+           case VAR_VOID:
+               break;
+       }
+       varp->v_lock = 0;
+     }
+ }
+ 
+ /*
+  * Set the value of a variable to NULL without freeing items.
+  */
+     void
+ init_tv(typval_T *varp)
+ {
+     if (varp != NULL)
+       CLEAR_POINTER(varp);
+ }
+ 
+ /*
+  * Get the number value of a variable.
+  * If it is a String variable, uses vim_str2nr().
+  * For incompatible types, return 0.
+  * tv_get_number_chk() is similar to tv_get_number(), but informs the
+  * caller of incompatible types: it sets *denote to TRUE if "denote"
+  * is not NULL or returns -1 otherwise.
+  */
+     varnumber_T
+ tv_get_number(typval_T *varp)
+ {
+     int               error = FALSE;
+ 
+     return tv_get_number_chk(varp, &error);   // return 0L on error
+ }
+ 
+     varnumber_T
+ tv_get_number_chk(typval_T *varp, int *denote)
+ {
+     varnumber_T       n = 0L;
+ 
+     switch (varp->v_type)
+     {
+       case VAR_NUMBER:
+           return varp->vval.v_number;
+       case VAR_FLOAT:
+ #ifdef FEAT_FLOAT
+           emsg(_("E805: Using a Float as a Number"));
+           break;
+ #endif
+       case VAR_FUNC:
+       case VAR_PARTIAL:
+           emsg(_("E703: Using a Funcref as a Number"));
+           break;
+       case VAR_STRING:
+           if (varp->vval.v_string != NULL)
+               vim_str2nr(varp->vval.v_string, NULL, NULL,
+                                           STR2NR_ALL, &n, NULL, 0, FALSE);
+           return n;
+       case VAR_LIST:
+           emsg(_("E745: Using a List as a Number"));
+           break;
+       case VAR_DICT:
+           emsg(_("E728: Using a Dictionary as a Number"));
+           break;
+       case VAR_BOOL:
+       case VAR_SPECIAL:
+           return varp->vval.v_number == VVAL_TRUE ? 1 : 0;
+       case VAR_JOB:
+ #ifdef FEAT_JOB_CHANNEL
+           emsg(_("E910: Using a Job as a Number"));
+           break;
+ #endif
+       case VAR_CHANNEL:
+ #ifdef FEAT_JOB_CHANNEL
+           emsg(_("E913: Using a Channel as a Number"));
+           break;
+ #endif
+       case VAR_BLOB:
+           emsg(_("E974: Using a Blob as a Number"));
+           break;
+       case VAR_UNKNOWN:
+       case VAR_ANY:
+       case VAR_VOID:
+           internal_error_no_abort("tv_get_number(UNKNOWN)");
+           break;
+     }
+     if (denote == NULL)               // useful for values that must be 
unsigned
+       n = -1;
+     else
+       *denote = TRUE;
+     return n;
+ }
+ 
+ #ifdef FEAT_FLOAT
+     float_T
+ tv_get_float(typval_T *varp)
+ {
+     switch (varp->v_type)
+     {
+       case VAR_NUMBER:
+           return (float_T)(varp->vval.v_number);
+       case VAR_FLOAT:
+           return varp->vval.v_float;
+       case VAR_FUNC:
+       case VAR_PARTIAL:
+           emsg(_("E891: Using a Funcref as a Float"));
+           break;
+       case VAR_STRING:
+           emsg(_("E892: Using a String as a Float"));
+           break;
+       case VAR_LIST:
+           emsg(_("E893: Using a List as a Float"));
+           break;
+       case VAR_DICT:
+           emsg(_("E894: Using a Dictionary as a Float"));
+           break;
+       case VAR_BOOL:
+           emsg(_("E362: Using a boolean value as a Float"));
+           break;
+       case VAR_SPECIAL:
+           emsg(_("E907: Using a special value as a Float"));
+           break;
+       case VAR_JOB:
+ # ifdef FEAT_JOB_CHANNEL
+           emsg(_("E911: Using a Job as a Float"));
+           break;
+ # endif
+       case VAR_CHANNEL:
+ # ifdef FEAT_JOB_CHANNEL
+           emsg(_("E914: Using a Channel as a Float"));
+           break;
+ # endif
+       case VAR_BLOB:
+           emsg(_("E975: Using a Blob as a Float"));
+           break;
+       case VAR_UNKNOWN:
+       case VAR_ANY:
+       case VAR_VOID:
+           internal_error_no_abort("tv_get_float(UNKNOWN)");
+           break;
+     }
+     return 0;
+ }
+ #endif
+ 
+ /*
+  * Get the string value of a variable.
+  * If it is a Number variable, the number is converted into a string.
+  * tv_get_string() uses a single, static buffer.  YOU CAN ONLY USE IT ONCE!
+  * tv_get_string_buf() uses a given buffer.
+  * If the String variable has never been set, return an empty string.
+  * Never returns NULL;
+  * tv_get_string_chk() and tv_get_string_buf_chk() are similar, but return
+  * NULL on error.
+  */
+     char_u *
+ tv_get_string(typval_T *varp)
+ {
+     static char_u   mybuf[NUMBUFLEN];
+ 
+     return tv_get_string_buf(varp, mybuf);
+ }
+ 
+     char_u *
+ tv_get_string_buf(typval_T *varp, char_u *buf)
+ {
+     char_u    *res =  tv_get_string_buf_chk(varp, buf);
+ 
+     return res != NULL ? res : (char_u *)"";
+ }
+ 
+ /*
+  * Careful: This uses a single, static buffer.  YOU CAN ONLY USE IT ONCE!
+  */
+     char_u *
+ tv_get_string_chk(typval_T *varp)
+ {
+     static char_u   mybuf[NUMBUFLEN];
+ 
+     return tv_get_string_buf_chk(varp, mybuf);
+ }
+ 
+     char_u *
+ tv_get_string_buf_chk(typval_T *varp, char_u *buf)
+ {
+     switch (varp->v_type)
+     {
+       case VAR_NUMBER:
+           vim_snprintf((char *)buf, NUMBUFLEN, "%lld",
+                                           (varnumber_T)varp->vval.v_number);
+           return buf;
+       case VAR_FUNC:
+       case VAR_PARTIAL:
+           emsg(_("E729: using Funcref as a String"));
+           break;
+       case VAR_LIST:
+           emsg(_("E730: using List as a String"));
+           break;
+       case VAR_DICT:
+           emsg(_("E731: using Dictionary as a String"));
+           break;
+       case VAR_FLOAT:
+ #ifdef FEAT_FLOAT
+           emsg(_(e_float_as_string));
+           break;
+ #endif
+       case VAR_STRING:
+           if (varp->vval.v_string != NULL)
+               return varp->vval.v_string;
+           return (char_u *)"";
+       case VAR_BOOL:
+       case VAR_SPECIAL:
+           STRCPY(buf, get_var_special_name(varp->vval.v_number));
+           return buf;
+         case VAR_BLOB:
+           emsg(_("E976: using Blob as a String"));
+           break;
+       case VAR_JOB:
+ #ifdef FEAT_JOB_CHANNEL
+           {
+               job_T *job = varp->vval.v_job;
+               char  *status;
+ 
+               if (job == NULL)
+                   return (char_u *)"no process";
+               status = job->jv_status == JOB_FAILED ? "fail"
+                               : job->jv_status >= JOB_ENDED ? "dead"
+                               : "run";
+ # ifdef UNIX
+               vim_snprintf((char *)buf, NUMBUFLEN,
+                           "process %ld %s", (long)job->jv_pid, status);
+ # elif defined(MSWIN)
+               vim_snprintf((char *)buf, NUMBUFLEN,
+                           "process %ld %s",
+                           (long)job->jv_proc_info.dwProcessId,
+                           status);
+ # else
+               // fall-back
+               vim_snprintf((char *)buf, NUMBUFLEN, "process ? %s", status);
+ # endif
+               return buf;
+           }
+ #endif
+           break;
+       case VAR_CHANNEL:
+ #ifdef FEAT_JOB_CHANNEL
+           {
+               channel_T *channel = varp->vval.v_channel;
+               char      *status = channel_status(channel, -1);
+ 
+               if (channel == NULL)
+                   vim_snprintf((char *)buf, NUMBUFLEN, "channel %s", status);
+               else
+                   vim_snprintf((char *)buf, NUMBUFLEN,
+                                    "channel %d %s", channel->ch_id, status);
+               return buf;
+           }
+ #endif
+           break;
+       case VAR_UNKNOWN:
+       case VAR_ANY:
+       case VAR_VOID:
+           emsg(_(e_inval_string));
+           break;
+     }
+     return NULL;
+ }
+ 
+ /*
+  * Turn a typeval into a string.  Similar to tv_get_string_buf() but uses
+  * string() on Dict, List, etc.
+  */
+     char_u *
+ tv_stringify(typval_T *varp, char_u *buf)
+ {
+     if (varp->v_type == VAR_LIST
+           || varp->v_type == VAR_DICT
+           || varp->v_type == VAR_BLOB
+           || varp->v_type == VAR_FUNC
+           || varp->v_type == VAR_PARTIAL
+           || varp->v_type == VAR_FLOAT)
+     {
+       typval_T tmp;
+ 
+       f_string(varp, &tmp);
+       tv_get_string_buf(&tmp, buf);
+       clear_tv(varp);
+       *varp = tmp;
+       return tmp.vval.v_string;
+     }
+     return tv_get_string_buf(varp, buf);
+ }
+ 
+ /*
+  * Return TRUE if typeval "tv" and its value are set to be locked (immutable).
+  * Also give an error message, using "name" or _("name") when use_gettext is
+  * TRUE.
+  */
+     int
+ tv_check_lock(typval_T *tv, char_u *name, int use_gettext)
+ {
+     int       lock = 0;
+ 
+     switch (tv->v_type)
+     {
+       case VAR_BLOB:
+           if (tv->vval.v_blob != NULL)
+               lock = tv->vval.v_blob->bv_lock;
+           break;
+       case VAR_LIST:
+           if (tv->vval.v_list != NULL)
+               lock = tv->vval.v_list->lv_lock;
+           break;
+       case VAR_DICT:
+           if (tv->vval.v_dict != NULL)
+               lock = tv->vval.v_dict->dv_lock;
+           break;
+       default:
+           break;
+     }
+     return var_check_lock(tv->v_lock, name, use_gettext)
+                   || (lock != 0 && var_check_lock(lock, name, use_gettext));
+ }
+ 
+ /*
+  * Copy the values from typval_T "from" to typval_T "to".
+  * When needed allocates string or increases reference count.
+  * Does not make a copy of a list, blob or dict but copies the reference!
+  * It is OK for "from" and "to" to point to the same item.  This is used to
+  * make a copy later.
+  */
+     void
+ copy_tv(typval_T *from, typval_T *to)
+ {
+     to->v_type = from->v_type;
+     to->v_lock = 0;
+     switch (from->v_type)
+     {
+       case VAR_NUMBER:
+       case VAR_BOOL:
+       case VAR_SPECIAL:
+           to->vval.v_number = from->vval.v_number;
+           break;
+       case VAR_FLOAT:
+ #ifdef FEAT_FLOAT
+           to->vval.v_float = from->vval.v_float;
+           break;
+ #endif
+       case VAR_JOB:
+ #ifdef FEAT_JOB_CHANNEL
+           to->vval.v_job = from->vval.v_job;
+           if (to->vval.v_job != NULL)
+               ++to->vval.v_job->jv_refcount;
+           break;
+ #endif
+       case VAR_CHANNEL:
+ #ifdef FEAT_JOB_CHANNEL
+           to->vval.v_channel = from->vval.v_channel;
+           if (to->vval.v_channel != NULL)
+               ++to->vval.v_channel->ch_refcount;
+           break;
+ #endif
+       case VAR_STRING:
+       case VAR_FUNC:
+           if (from->vval.v_string == NULL)
+               to->vval.v_string = NULL;
+           else
+           {
+               to->vval.v_string = vim_strsave(from->vval.v_string);
+               if (from->v_type == VAR_FUNC)
+                   func_ref(to->vval.v_string);
+           }
+           break;
+       case VAR_PARTIAL:
+           if (from->vval.v_partial == NULL)
+               to->vval.v_partial = NULL;
+           else
+           {
+               to->vval.v_partial = from->vval.v_partial;
+               ++to->vval.v_partial->pt_refcount;
+           }
+           break;
+       case VAR_BLOB:
+           if (from->vval.v_blob == NULL)
+               to->vval.v_blob = NULL;
+           else
+           {
+               to->vval.v_blob = from->vval.v_blob;
+               ++to->vval.v_blob->bv_refcount;
+           }
+           break;
+       case VAR_LIST:
+           if (from->vval.v_list == NULL)
+               to->vval.v_list = NULL;
+           else
+           {
+               to->vval.v_list = from->vval.v_list;
+               ++to->vval.v_list->lv_refcount;
+           }
+           break;
+       case VAR_DICT:
+           if (from->vval.v_dict == NULL)
+               to->vval.v_dict = NULL;
+           else
+           {
+               to->vval.v_dict = from->vval.v_dict;
+               ++to->vval.v_dict->dv_refcount;
+           }
+           break;
+       case VAR_UNKNOWN:
+       case VAR_ANY:
+       case VAR_VOID:
+           internal_error_no_abort("copy_tv(UNKNOWN)");
+           break;
+     }
+ }
+ 
+ /*
+  * Compare "typ1" and "typ2".  Put the result in "typ1".
+  */
+     int
+ typval_compare(
+     typval_T  *typ1,   // first operand
+     typval_T  *typ2,   // second operand
+     exptype_T type,    // operator
+     int               ic)      // ignore case
+ {
+     int               i;
+     varnumber_T       n1, n2;
+     char_u    *s1, *s2;
+     char_u    buf1[NUMBUFLEN], buf2[NUMBUFLEN];
+     int               type_is = type == EXPR_IS || type == EXPR_ISNOT;
+ 
+     if (type_is && typ1->v_type != typ2->v_type)
+     {
+       // For "is" a different type always means FALSE, for "notis"
+       // it means TRUE.
+       n1 = (type == EXPR_ISNOT);
+     }
+     else if (typ1->v_type == VAR_BLOB || typ2->v_type == VAR_BLOB)
+     {
+       if (type_is)
+       {
+           n1 = (typ1->v_type == typ2->v_type
+                           && typ1->vval.v_blob == typ2->vval.v_blob);
+           if (type == EXPR_ISNOT)
+               n1 = !n1;
+       }
+       else if (typ1->v_type != typ2->v_type
+               || (type != EXPR_EQUAL && type != EXPR_NEQUAL))
+       {
+           if (typ1->v_type != typ2->v_type)
+               emsg(_("E977: Can only compare Blob with Blob"));
+           else
+               emsg(_(e_invalblob));
+           clear_tv(typ1);
+           return FAIL;
+       }
+       else
+       {
+           // Compare two Blobs for being equal or unequal.
+           n1 = blob_equal(typ1->vval.v_blob, typ2->vval.v_blob);
+           if (type == EXPR_NEQUAL)
+               n1 = !n1;
+       }
+     }
+     else if (typ1->v_type == VAR_LIST || typ2->v_type == VAR_LIST)
+     {
+       if (type_is)
+       {
+           n1 = (typ1->v_type == typ2->v_type
+                           && typ1->vval.v_list == typ2->vval.v_list);
+           if (type == EXPR_ISNOT)
+               n1 = !n1;
+       }
+       else if (typ1->v_type != typ2->v_type
+               || (type != EXPR_EQUAL && type != EXPR_NEQUAL))
+       {
+           if (typ1->v_type != typ2->v_type)
+               emsg(_("E691: Can only compare List with List"));
+           else
+               emsg(_("E692: Invalid operation for List"));
+           clear_tv(typ1);
+           return FAIL;
+       }
+       else
+       {
+           // Compare two Lists for being equal or unequal.
+           n1 = list_equal(typ1->vval.v_list, typ2->vval.v_list,
+                                                           ic, FALSE);
+           if (type == EXPR_NEQUAL)
+               n1 = !n1;
+       }
+     }
+ 
+     else if (typ1->v_type == VAR_DICT || typ2->v_type == VAR_DICT)
+     {
+       if (type_is)
+       {
+           n1 = (typ1->v_type == typ2->v_type
+                           && typ1->vval.v_dict == typ2->vval.v_dict);
+           if (type == EXPR_ISNOT)
+               n1 = !n1;
+       }
+       else if (typ1->v_type != typ2->v_type
+               || (type != EXPR_EQUAL && type != EXPR_NEQUAL))
+       {
+           if (typ1->v_type != typ2->v_type)
+               emsg(_("E735: Can only compare Dictionary with Dictionary"));
+           else
+               emsg(_("E736: Invalid operation for Dictionary"));
+           clear_tv(typ1);
+           return FAIL;
+       }
+       else
+       {
+           // Compare two Dictionaries for being equal or unequal.
+           n1 = dict_equal(typ1->vval.v_dict, typ2->vval.v_dict,
+                                                           ic, FALSE);
+           if (type == EXPR_NEQUAL)
+               n1 = !n1;
+       }
+     }
+ 
+     else if (typ1->v_type == VAR_FUNC || typ2->v_type == VAR_FUNC
+       || typ1->v_type == VAR_PARTIAL || typ2->v_type == VAR_PARTIAL)
+     {
+       if (type != EXPR_EQUAL && type != EXPR_NEQUAL
+               && type != EXPR_IS && type != EXPR_ISNOT)
+       {
+           emsg(_("E694: Invalid operation for Funcrefs"));
+           clear_tv(typ1);
+           return FAIL;
+       }
+       if ((typ1->v_type == VAR_PARTIAL
+                                       && typ1->vval.v_partial == NULL)
+               || (typ2->v_type == VAR_PARTIAL
+                                       && typ2->vval.v_partial == NULL))
+           // When both partials are NULL, then they are equal.
+           // Otherwise they are not equal.
+           n1 = (typ1->vval.v_partial == typ2->vval.v_partial);
+       else if (type_is)
+       {
+           if (typ1->v_type == VAR_FUNC && typ2->v_type == VAR_FUNC)
+               // strings are considered the same if their value is
+               // the same
+               n1 = tv_equal(typ1, typ2, ic, FALSE);
+           else if (typ1->v_type == VAR_PARTIAL
+                                       && typ2->v_type == VAR_PARTIAL)
+               n1 = (typ1->vval.v_partial == typ2->vval.v_partial);
+           else
+               n1 = FALSE;
+       }
+       else
+           n1 = tv_equal(typ1, typ2, ic, FALSE);
+       if (type == EXPR_NEQUAL || type == EXPR_ISNOT)
+           n1 = !n1;
+     }
+ 
+ #ifdef FEAT_FLOAT
+     // If one of the two variables is a float, compare as a float.
+     // When using "=~" or "!~", always compare as string.
+     else if ((typ1->v_type == VAR_FLOAT || typ2->v_type == VAR_FLOAT)
+           && type != EXPR_MATCH && type != EXPR_NOMATCH)
+     {
+       float_T f1, f2;
+ 
+       f1 = tv_get_float(typ1);
+       f2 = tv_get_float(typ2);
+       n1 = FALSE;
+       switch (type)
+       {
+           case EXPR_IS:
+           case EXPR_EQUAL:    n1 = (f1 == f2); break;
+           case EXPR_ISNOT:
+           case EXPR_NEQUAL:   n1 = (f1 != f2); break;
+           case EXPR_GREATER:  n1 = (f1 > f2); break;
+           case EXPR_GEQUAL:   n1 = (f1 >= f2); break;
+           case EXPR_SMALLER:  n1 = (f1 < f2); break;
+           case EXPR_SEQUAL:   n1 = (f1 <= f2); break;
+           case EXPR_UNKNOWN:
+           case EXPR_MATCH:
+           default:  break;  // avoid gcc warning
+       }
+     }
+ #endif
+ 
+     // If one of the two variables is a number, compare as a number.
+     // When using "=~" or "!~", always compare as string.
+     else if ((typ1->v_type == VAR_NUMBER || typ2->v_type == VAR_NUMBER)
+           && type != EXPR_MATCH && type != EXPR_NOMATCH)
+     {
+       n1 = tv_get_number(typ1);
+       n2 = tv_get_number(typ2);
+       switch (type)
+       {
+           case EXPR_IS:
+           case EXPR_EQUAL:    n1 = (n1 == n2); break;
+           case EXPR_ISNOT:
+           case EXPR_NEQUAL:   n1 = (n1 != n2); break;
+           case EXPR_GREATER:  n1 = (n1 > n2); break;
+           case EXPR_GEQUAL:   n1 = (n1 >= n2); break;
+           case EXPR_SMALLER:  n1 = (n1 < n2); break;
+           case EXPR_SEQUAL:   n1 = (n1 <= n2); break;
+           case EXPR_UNKNOWN:
+           case EXPR_MATCH:
+           default:  break;  // avoid gcc warning
+       }
+     }
+     else
+     {
+       s1 = tv_get_string_buf(typ1, buf1);
+       s2 = tv_get_string_buf(typ2, buf2);
+       if (type != EXPR_MATCH && type != EXPR_NOMATCH)
+           i = ic ? MB_STRICMP(s1, s2) : STRCMP(s1, s2);
+       else
+           i = 0;
+       n1 = FALSE;
+       switch (type)
+       {
+           case EXPR_IS:
+           case EXPR_EQUAL:    n1 = (i == 0); break;
+           case EXPR_ISNOT:
+           case EXPR_NEQUAL:   n1 = (i != 0); break;
+           case EXPR_GREATER:  n1 = (i > 0); break;
+           case EXPR_GEQUAL:   n1 = (i >= 0); break;
+           case EXPR_SMALLER:  n1 = (i < 0); break;
+           case EXPR_SEQUAL:   n1 = (i <= 0); break;
+ 
+           case EXPR_MATCH:
+           case EXPR_NOMATCH:
+                   n1 = pattern_match(s2, s1, ic);
+                   if (type == EXPR_NOMATCH)
+                       n1 = !n1;
+                   break;
+ 
+           default:  break;  // avoid gcc warning
+       }
+     }
+     clear_tv(typ1);
+     typ1->v_type = VAR_NUMBER;
+     typ1->vval.v_number = n1;
+ 
+     return OK;
+ }
+ 
+     char_u *
+ typval_tostring(typval_T *arg)
+ {
+     char_u    *tofree;
+     char_u    numbuf[NUMBUFLEN];
+     char_u    *ret = NULL;
+ 
+     if (arg == NULL)
+       return vim_strsave((char_u *)"(does not exist)");
+     ret = tv2string(arg, &tofree, numbuf, 0);
+     // Make a copy if we have a value but it's not in allocated memory.
+     if (ret != NULL && tofree == NULL)
+       ret = vim_strsave(ret);
+     return ret;
+ }
+ 
+ /*
+  * Return TRUE if typeval "tv" is locked: Either that value is locked itself
+  * or it refers to a List or Dictionary that is locked.
+  */
+     int
+ tv_islocked(typval_T *tv)
+ {
+     return (tv->v_lock & VAR_LOCKED)
+       || (tv->v_type == VAR_LIST
+               && tv->vval.v_list != NULL
+               && (tv->vval.v_list->lv_lock & VAR_LOCKED))
+       || (tv->v_type == VAR_DICT
+               && tv->vval.v_dict != NULL
+               && (tv->vval.v_dict->dv_lock & VAR_LOCKED));
+ }
+ 
+     static int
+ func_equal(
+     typval_T *tv1,
+     typval_T *tv2,
+     int            ic)            // ignore case
+ {
+     char_u    *s1, *s2;
+     dict_T    *d1, *d2;
+     int               a1, a2;
+     int               i;
+ 
+     // empty and NULL function name considered the same
+     s1 = tv1->v_type == VAR_FUNC ? tv1->vval.v_string
+                                          : partial_name(tv1->vval.v_partial);
+     if (s1 != NULL && *s1 == NUL)
+       s1 = NULL;
+     s2 = tv2->v_type == VAR_FUNC ? tv2->vval.v_string
+                                          : partial_name(tv2->vval.v_partial);
+     if (s2 != NULL && *s2 == NUL)
+       s2 = NULL;
+     if (s1 == NULL || s2 == NULL)
+     {
+       if (s1 != s2)
+           return FALSE;
+     }
+     else if (STRCMP(s1, s2) != 0)
+       return FALSE;
+ 
+     // empty dict and NULL dict is different
+     d1 = tv1->v_type == VAR_FUNC ? NULL : tv1->vval.v_partial->pt_dict;
+     d2 = tv2->v_type == VAR_FUNC ? NULL : tv2->vval.v_partial->pt_dict;
+     if (d1 == NULL || d2 == NULL)
+     {
+       if (d1 != d2)
+           return FALSE;
+     }
+     else if (!dict_equal(d1, d2, ic, TRUE))
+       return FALSE;
+ 
+     // empty list and no list considered the same
+     a1 = tv1->v_type == VAR_FUNC ? 0 : tv1->vval.v_partial->pt_argc;
+     a2 = tv2->v_type == VAR_FUNC ? 0 : tv2->vval.v_partial->pt_argc;
+     if (a1 != a2)
+       return FALSE;
+     for (i = 0; i < a1; ++i)
+       if (!tv_equal(tv1->vval.v_partial->pt_argv + i,
+                     tv2->vval.v_partial->pt_argv + i, ic, TRUE))
+           return FALSE;
+ 
+     return TRUE;
+ }
+ 
+ /*
+  * Return TRUE if "tv1" and "tv2" have the same value.
+  * Compares the items just like "==" would compare them, but strings and
+  * numbers are different.  Floats and numbers are also different.
+  */
+     int
+ tv_equal(
+     typval_T *tv1,
+     typval_T *tv2,
+     int            ic,            // ignore case
+     int            recursive)     // TRUE when used recursively
+ {
+     char_u    buf1[NUMBUFLEN], buf2[NUMBUFLEN];
+     char_u    *s1, *s2;
+     static int  recursive_cnt = 0;        // catch recursive loops
+     int               r;
+     static int        tv_equal_recurse_limit;
+ 
+     // Catch lists and dicts that have an endless loop by limiting
+     // recursiveness to a limit.  We guess they are equal then.
+     // A fixed limit has the problem of still taking an awful long time.
+     // Reduce the limit every time running into it. That should work fine for
+     // deeply linked structures that are not recursively linked and catch
+     // recursiveness quickly.
+     if (!recursive)
+       tv_equal_recurse_limit = 1000;
+     if (recursive_cnt >= tv_equal_recurse_limit)
+     {
+       --tv_equal_recurse_limit;
+       return TRUE;
+     }
+ 
+     // For VAR_FUNC and VAR_PARTIAL compare the function name, bound dict and
+     // arguments.
+     if ((tv1->v_type == VAR_FUNC
+               || (tv1->v_type == VAR_PARTIAL && tv1->vval.v_partial != NULL))
+           && (tv2->v_type == VAR_FUNC
+               || (tv2->v_type == VAR_PARTIAL && tv2->vval.v_partial != NULL)))
+     {
+       ++recursive_cnt;
+       r = func_equal(tv1, tv2, ic);
+       --recursive_cnt;
+       return r;
+     }
+ 
+     if (tv1->v_type != tv2->v_type)
+       return FALSE;
+ 
+     switch (tv1->v_type)
+     {
+       case VAR_LIST:
+           ++recursive_cnt;
+           r = list_equal(tv1->vval.v_list, tv2->vval.v_list, ic, TRUE);
+           --recursive_cnt;
+           return r;
+ 
+       case VAR_DICT:
+           ++recursive_cnt;
+           r = dict_equal(tv1->vval.v_dict, tv2->vval.v_dict, ic, TRUE);
+           --recursive_cnt;
+           return r;
+ 
+       case VAR_BLOB:
+           return blob_equal(tv1->vval.v_blob, tv2->vval.v_blob);
+ 
+       case VAR_NUMBER:
+       case VAR_BOOL:
+       case VAR_SPECIAL:
+           return tv1->vval.v_number == tv2->vval.v_number;
+ 
+       case VAR_STRING:
+           s1 = tv_get_string_buf(tv1, buf1);
+           s2 = tv_get_string_buf(tv2, buf2);
+           return ((ic ? MB_STRICMP(s1, s2) : STRCMP(s1, s2)) == 0);
+ 
+       case VAR_FLOAT:
+ #ifdef FEAT_FLOAT
+           return tv1->vval.v_float == tv2->vval.v_float;
+ #endif
+       case VAR_JOB:
+ #ifdef FEAT_JOB_CHANNEL
+           return tv1->vval.v_job == tv2->vval.v_job;
+ #endif
+       case VAR_CHANNEL:
+ #ifdef FEAT_JOB_CHANNEL
+           return tv1->vval.v_channel == tv2->vval.v_channel;
+ #endif
+ 
+       case VAR_PARTIAL:
+           return tv1->vval.v_partial == tv2->vval.v_partial;
+ 
+       case VAR_FUNC:
+           return tv1->vval.v_string == tv2->vval.v_string;
+ 
+       case VAR_UNKNOWN:
+       case VAR_ANY:
+       case VAR_VOID:
+           break;
+     }
+ 
+     // VAR_UNKNOWN can be the result of a invalid expression, let's say it
+     // does not equal anything, not even itself.
+     return FALSE;
+ }
+ 
+ /*
+  * Get an option value.
+  * "arg" points to the '&' or '+' before the option name.
+  * "arg" is advanced to character after the option name.
+  * Return OK or FAIL.
+  */
+     int
+ get_option_tv(
+     char_u    **arg,
+     typval_T  *rettv, // when NULL, only check if option exists
+     int               evaluate)
+ {
+     char_u    *option_end;
+     long      numval;
+     char_u    *stringval;
+     int               opt_type;
+     int               c;
+     int               working = (**arg == '+');    // has("+option")
+     int               ret = OK;
+     int               opt_flags;
+ 
+     // Isolate the option name and find its value.
+     option_end = find_option_end(arg, &opt_flags);
+     if (option_end == NULL)
+     {
+       if (rettv != NULL)
+           semsg(_("E112: Option name missing: %s"), *arg);
+       return FAIL;
+     }
+ 
+     if (!evaluate)
+     {
+       *arg = option_end;
+       return OK;
+     }
+ 
+     c = *option_end;
+     *option_end = NUL;
+     opt_type = get_option_value(*arg, &numval,
+                              rettv == NULL ? NULL : &stringval, opt_flags);
+ 
+     if (opt_type == -3)                       // invalid name
+     {
+       if (rettv != NULL)
+           semsg(_(e_unknown_option), *arg);
+       ret = FAIL;
+     }
+     else if (rettv != NULL)
+     {
+       if (opt_type == -2)             // hidden string option
+       {
+           rettv->v_type = VAR_STRING;
+           rettv->vval.v_string = NULL;
+       }
+       else if (opt_type == -1)        // hidden number option
+       {
+           rettv->v_type = VAR_NUMBER;
+           rettv->vval.v_number = 0;
+       }
+       else if (opt_type == 1)         // number option
+       {
+           rettv->v_type = VAR_NUMBER;
+           rettv->vval.v_number = numval;
+       }
+       else                            // string option
+       {
+           rettv->v_type = VAR_STRING;
+           rettv->vval.v_string = stringval;
+       }
+     }
+     else if (working && (opt_type == -2 || opt_type == -1))
+       ret = FAIL;
+ 
+     *option_end = c;              // put back for error messages
+     *arg = option_end;
+ 
+     return ret;
+ }
+ 
+ /*
+  * Allocate a variable for a number constant.  Also deals with "0z" for blob.
+  * Return OK or FAIL.
+  */
+     int
+ get_number_tv(
+       char_u      **arg,
+       typval_T    *rettv,
+       int         evaluate,
+       int         want_string UNUSED)
+ {
+     int               len;
+ #ifdef FEAT_FLOAT
+     char_u    *p;
+     int               get_float = FALSE;
+ 
+     // We accept a float when the format matches
+     // "[0-9]\+\.[0-9]\+\([eE][+-]\?[0-9]\+\)\?".  This is very
+     // strict to avoid backwards compatibility problems.
+     // With script version 2 and later the leading digit can be
+     // omitted.
+     // Don't look for a float after the "." operator, so that
+     // ":let vers = 1.2.3" doesn't fail.
+     if (**arg == '.')
+       p = *arg;
+     else
+       p = skipdigits(*arg + 1);
+     if (!want_string && p[0] == '.' && vim_isdigit(p[1]))
+     {
+       get_float = TRUE;
+       p = skipdigits(p + 2);
+       if (*p == 'e' || *p == 'E')
+       {
+           ++p;
+           if (*p == '-' || *p == '+')
+               ++p;
+           if (!vim_isdigit(*p))
+               get_float = FALSE;
+           else
+               p = skipdigits(p + 1);
+       }
+       if (ASCII_ISALPHA(*p) || *p == '.')
+           get_float = FALSE;
+     }
+     if (get_float)
+     {
+       float_T f;
+ 
+       *arg += string2float(*arg, &f);
+       if (evaluate)
+       {
+           rettv->v_type = VAR_FLOAT;
+           rettv->vval.v_float = f;
+       }
+     }
+     else
+ #endif
+     if (**arg == '0' && ((*arg)[1] == 'z' || (*arg)[1] == 'Z'))
+     {
+       char_u  *bp;
+       blob_T  *blob = NULL;  // init for gcc
+ 
+       // Blob constant: 0z0123456789abcdef
+       if (evaluate)
+           blob = blob_alloc();
+       for (bp = *arg + 2; vim_isxdigit(bp[0]); bp += 2)
+       {
+           if (!vim_isxdigit(bp[1]))
+           {
+               if (blob != NULL)
+               {
+                   emsg(_("E973: Blob literal should have an even number of 
hex characters"));
+                   ga_clear(&blob->bv_ga);
+                   VIM_CLEAR(blob);
+               }
+               return FAIL;
+           }
+           if (blob != NULL)
+               ga_append(&blob->bv_ga,
+                            (hex2nr(*bp) << 4) + hex2nr(*(bp+1)));
+           if (bp[2] == '.' && vim_isxdigit(bp[3]))
+               ++bp;
+       }
+       if (blob != NULL)
+           rettv_blob_set(rettv, blob);
+       *arg = bp;
+     }
+     else
+     {
+       varnumber_T     n;
+ 
+       // decimal, hex or octal number
+       vim_str2nr(*arg, NULL, &len, current_sctx.sc_version >= 4
+                     ? STR2NR_NO_OCT + STR2NR_QUOTE
+                     : STR2NR_ALL, &n, NULL, 0, TRUE);
+       if (len == 0)
+       {
+           semsg(_(e_invexpr2), *arg);
+           return FAIL;
+       }
+       *arg += len;
+       if (evaluate)
+       {
+           rettv->v_type = VAR_NUMBER;
+           rettv->vval.v_number = n;
+       }
+     }
+     return OK;
+ }
+ 
+ /*
+  * Allocate a variable for a string constant.
+  * Return OK or FAIL.
+  */
+     int
+ get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
+ {
+     char_u    *p;
+     char_u    *name;
+     int               extra = 0;
+     int               len;
+ 
+     // Find the end of the string, skipping backslashed characters.
+     for (p = *arg + 1; *p != NUL && *p != '"'; MB_PTR_ADV(p))
+     {
+       if (*p == '\\' && p[1] != NUL)
+       {
+           ++p;
+           // A "\<x>" form occupies at least 4 characters, and produces up
+           // to 21 characters (3 * 6 for the char and 3 for a modifier):
+           // reserve space for 18 extra.
+           // Each byte in the char could be encoded as K_SPECIAL K_EXTRA x.
+           if (*p == '<')
+               extra += 18;
+       }
+     }
+ 
+     if (*p != '"')
+     {
+       semsg(_("E114: Missing quote: %s"), *arg);
+       return FAIL;
+     }
+ 
+     // If only parsing, set *arg and return here
+     if (!evaluate)
+     {
+       *arg = p + 1;
+       return OK;
+     }
+ 
+     // Copy the string into allocated memory, handling backslashed
+     // characters.
+     len = (int)(p - *arg + extra);
+     name = alloc(len);
+     if (name == NULL)
+       return FAIL;
+     rettv->v_type = VAR_STRING;
+     rettv->vval.v_string = name;
+ 
+     for (p = *arg + 1; *p != NUL && *p != '"'; )
+     {
+       if (*p == '\\')
+       {
+           switch (*++p)
+           {
+               case 'b': *name++ = BS; ++p; break;
+               case 'e': *name++ = ESC; ++p; break;
+               case 'f': *name++ = FF; ++p; break;
+               case 'n': *name++ = NL; ++p; break;
+               case 'r': *name++ = CAR; ++p; break;
+               case 't': *name++ = TAB; ++p; break;
+ 
+               case 'X': // hex: "\x1", "\x12"
+               case 'x':
+               case 'u': // Unicode: "\u0023"
+               case 'U':
+                         if (vim_isxdigit(p[1]))
+                         {
+                             int       n, nr;
+                             int       c = toupper(*p);
+ 
+                             if (c == 'X')
+                                 n = 2;
+                             else if (*p == 'u')
+                                 n = 4;
+                             else
+                                 n = 8;
+                             nr = 0;
+                             while (--n >= 0 && vim_isxdigit(p[1]))
+                             {
+                                 ++p;
+                                 nr = (nr << 4) + hex2nr(*p);
+                             }
+                             ++p;
+                             // For "\u" store the number according to
+                             // 'encoding'.
+                             if (c != 'X')
+                                 name += (*mb_char2bytes)(nr, name);
+                             else
+                                 *name++ = nr;
+                         }
+                         break;
+ 
+                         // octal: "\1", "\12", "\123"
+               case '0':
+               case '1':
+               case '2':
+               case '3':
+               case '4':
+               case '5':
+               case '6':
+               case '7': *name = *p++ - '0';
+                         if (*p >= '0' && *p <= '7')
+                         {
+                             *name = (*name << 3) + *p++ - '0';
+                             if (*p >= '0' && *p <= '7')
+                                 *name = (*name << 3) + *p++ - '0';
+                         }
+                         ++name;
+                         break;
+ 
+                           // Special key, e.g.: "\<C-W>"
+               case '<': extra = trans_special(&p, name, TRUE, TRUE,
+                                                                  TRUE, NULL);
+                         if (extra != 0)
+                         {
+                             name += extra;
+                             if (name >= rettv->vval.v_string + len)
+                                 iemsg("get_string_tv() used more space than 
allocated");
+                             break;
+                         }
+                         // FALLTHROUGH
+ 
+               default:  MB_COPY_CHAR(p, name);
+                         break;
+           }
+       }
+       else
+           MB_COPY_CHAR(p, name);
+ 
+     }
+     *name = NUL;
+     if (*p != NUL) // just in case
+       ++p;
+     *arg = p;
+ 
+     return OK;
+ }
+ 
+ /*
+  * Allocate a variable for a 'str''ing' constant.
+  * Return OK or FAIL.
+  */
+     int
+ get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate)
+ {
+     char_u    *p;
+     char_u    *str;
+     int               reduce = 0;
+ 
+     // Find the end of the string, skipping ''.
+     for (p = *arg + 1; *p != NUL; MB_PTR_ADV(p))
+     {
+       if (*p == '\'')
+       {
+           if (p[1] != '\'')
+               break;
+           ++reduce;
+           ++p;
+       }
+     }
+ 
+     if (*p != '\'')
+     {
+       semsg(_("E115: Missing quote: %s"), *arg);
+       return FAIL;
+     }
+ 
+     // If only parsing return after setting "*arg"
+     if (!evaluate)
+     {
+       *arg = p + 1;
+       return OK;
+     }
+ 
+     // Copy the string into allocated memory, handling '' to ' reduction.
+     str = alloc((p - *arg) - reduce);
+     if (str == NULL)
+       return FAIL;
+     rettv->v_type = VAR_STRING;
+     rettv->vval.v_string = str;
+ 
+     for (p = *arg + 1; *p != NUL; )
+     {
+       if (*p == '\'')
+       {
+           if (p[1] != '\'')
+               break;
+           ++p;
+       }
+       MB_COPY_CHAR(p, str);
+     }
+     *str = NUL;
+     *arg = p + 1;
+ 
+     return OK;
+ }
+ 
+ /*
+  * Return a string with the string representation of a variable.
+  * If the memory is allocated "tofree" is set to it, otherwise NULL.
+  * "numbuf" is used for a number.
+  * Puts quotes around strings, so that they can be parsed back by eval().
+  * May return NULL.
+  */
+     char_u *
+ tv2string(
+     typval_T  *tv,
+     char_u    **tofree,
+     char_u    *numbuf,
+     int               copyID)
+ {
+     return echo_string_core(tv, tofree, numbuf, copyID, FALSE, TRUE, FALSE);
+ }
+ 
+ /*
+  * Get the value of an environment variable.
+  * "arg" is pointing to the '$'.  It is advanced to after the name.
+  * If the environment variable was not set, silently assume it is empty.
+  * Return FAIL if the name is invalid.
+  */
+     int
+ get_env_tv(char_u **arg, typval_T *rettv, int evaluate)
+ {
+     char_u    *string = NULL;
+     int               len;
+     int               cc;
+     char_u    *name;
+     int               mustfree = FALSE;
+ 
+     ++*arg;
+     name = *arg;
+     len = get_env_len(arg);
+     if (evaluate)
+     {
+       if (len == 0)
+           return FAIL; // invalid empty name
+ 
+       cc = name[len];
+       name[len] = NUL;
+       // first try vim_getenv(), fast for normal environment vars
+       string = vim_getenv(name, &mustfree);
+       if (string != NULL && *string != NUL)
+       {
+           if (!mustfree)
+               string = vim_strsave(string);
+       }
+       else
+       {
+           if (mustfree)
+               vim_free(string);
+ 
+           // next try expanding things like $VIM and ${HOME}
+           string = expand_env_save(name - 1);
+           if (string != NULL && *string == '$')
+               VIM_CLEAR(string);
+       }
+       name[len] = cc;
+ 
+       rettv->v_type = VAR_STRING;
+       rettv->vval.v_string = string;
+     }
+ 
+     return OK;
+ }
+ 
+ /*
+  * Get the lnum from the first argument.
+  * Also accepts ".", "$", etc., but that only works for the current buffer.
+  * Returns -1 on error.
+  */
+     linenr_T
+ tv_get_lnum(typval_T *argvars)
+ {
+     linenr_T  lnum;
+ 
+     lnum = (linenr_T)tv_get_number_chk(&argvars[0], NULL);
+     if (lnum == 0)  // no valid number, try using arg like line()
+     {
+       int     fnum;
+       pos_T   *fp = var2fpos(&argvars[0], TRUE, &fnum);
+ 
+       if (fp != NULL)
+           lnum = fp->lnum;
+     }
+     return lnum;
+ }
+ 
+ /*
+  * Get the lnum from the first argument.
+  * Also accepts "$", then "buf" is used.
+  * Returns 0 on error.
+  */
+     linenr_T
+ tv_get_lnum_buf(typval_T *argvars, buf_T *buf)
+ {
+     if (argvars[0].v_type == VAR_STRING
+           && argvars[0].vval.v_string != NULL
+           && argvars[0].vval.v_string[0] == '$'
+           && buf != NULL)
+       return buf->b_ml.ml_line_count;
+     return (linenr_T)tv_get_number_chk(&argvars[0], NULL);
+ }
+ 
+ /*
+  * Get buffer by number or pattern.
+  */
+     buf_T *
+ tv_get_buf(typval_T *tv, int curtab_only)
+ {
+     char_u    *name = tv->vval.v_string;
+     buf_T     *buf;
+ 
+     if (tv->v_type == VAR_NUMBER)
+       return buflist_findnr((int)tv->vval.v_number);
+     if (tv->v_type != VAR_STRING)
+       return NULL;
+     if (name == NULL || *name == NUL)
+       return curbuf;
+     if (name[0] == '$' && name[1] == NUL)
+       return lastbuf;
+ 
+     buf = buflist_find_by_name(name, curtab_only);
+ 
+     // If not found, try expanding the name, like done for bufexists().
+     if (buf == NULL)
+       buf = find_buffer(tv);
+ 
+     return buf;
+ }
+ 
+ #endif // FEAT_EVAL
*** ../vim-8.2.0846/src/version.c       2020-05-30 16:17:30.771468466 +0200
--- src/version.c       2020-05-30 16:29:46.756758812 +0200
***************
*** 748,749 ****
--- 748,751 ----
  {   /* Add new patch number below this line */
+ /**/
+     847,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
216. Your pet rock leaves home.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/202005301506.04UF6jke364379%40masaka.moolenaar.net.

Raspunde prin e-mail lui