]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 8.2.0847: typval related code is spread out v8.2.0847
authorBram Moolenaar <Bram@vim.org>
Sat, 30 May 2020 15:06:14 +0000 (17:06 +0200)
committerBram Moolenaar <Bram@vim.org>
Sat, 30 May 2020 15:06:14 +0000 (17:06 +0200)
Problem:    Typval related code is spread out.
Solution:   Move code to new typval.c file. (Yegappan Lakshmanan, closes #6093)

16 files changed:
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 [new file with mode: 0644]
src/typval.c [new file with mode: 0644]
src/version.c

index 422f18dd280d3f63a93c38fde354683deda048f9..b3ec17aa692b735203ca19e6a2ffbf977c444ece 100644 (file)
--- a/Filelist
+++ b/Filelist
@@ -133,6 +133,7 @@ SRC_ALL =   \
                src/textobject.c \
                src/textprop.c \
                src/time.c \
+               src/typval.c \
                src/ui.c \
                src/undo.c \
                src/usercmd.c \
@@ -285,6 +286,7 @@ SRC_ALL =   \
                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,6 +349,7 @@ SRC_ALL =   \
                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 \
index 517ed2bfbc1d26c36f861def2c7ebf78bf53492e..d0b82f1b80c0c3374203bc6b78348a6a17c3dd75 100644 (file)
@@ -791,6 +791,7 @@ OBJ = \
        $(OUTDIR)/textobject.o \
        $(OUTDIR)/textprop.o \
        $(OUTDIR)/time.o \
+       $(OUTDIR)/typval.o \
        $(OUTDIR)/ui.o \
        $(OUTDIR)/undo.o \
        $(OUTDIR)/usercmd.o \
index 43414e72cf3572f0360451f69b1628f3c7d7385e..4efc1d67ab0b6f31c3aacb601a52b95780706925 100644 (file)
@@ -107,6 +107,7 @@ SRC =       arabic.c                                                \
        textobject.c                                            \
        textprop.c                                              \
        time.c                                                  \
+       typval.c                                                \
        ui.c                                                    \
        undo.c                                                  \
        usercmd.c                                               \
index 8240f505688ec6971e0f96bac5b895ec403f76cd..aec94a981db6ddf8bc89ab92834922e5a9874ed5 100644 (file)
@@ -811,6 +811,7 @@ OBJ = \
        $(OUTDIR)\textobject.obj \
        $(OUTDIR)\textprop.obj \
        $(OUTDIR)\time.obj \
+       $(OUTDIR)\typval.obj \
        $(OUTDIR)\ui.obj \
        $(OUTDIR)\undo.obj \
        $(OUTDIR)\usercmd.obj \
@@ -1755,6 +1756,8 @@ $(OUTDIR)/textprop.obj:   $(OUTDIR) textprop.c  $(INCL)
 
 $(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,6 +1957,7 @@ proto.h: \
        proto/textobject.pro \
        proto/textprop.pro \
        proto/time.pro \
+       proto/typval.pro \
        proto/ui.pro \
        proto/undo.pro \
        proto/usercmd.pro \
index 954d50a14fe9522bc90549804990eb978336e74a..e5ff857518bd1f1a6da9371a6ebdece998a6b223 100644 (file)
@@ -386,6 +386,7 @@ SRC = \
        textobject.c \
        textprop.c \
        time.c \
+       typval.c \
        ui.c \
        undo.c \
        usercmd.c \
@@ -497,6 +498,7 @@ OBJ = \
        textobject.obj \
        textprop.obj \
        time.obj \
+       typval.obj \
        ui.obj \
        undo.obj \
        usercmd.obj \
@@ -1005,6 +1007,9 @@ textprop.obj : textprop.c vim.h [.auto]config.h feature.h os_unix.h   \
 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
index 9b50eca6d9229dad202bdbde50604d5576ee92d4..c4bce687dc47f3e6273c5baaa7114b024ad803bd 100644 (file)
@@ -1686,6 +1686,7 @@ BASIC_SRC = \
        textobject.c \
        textprop.c \
        time.c \
+       typval.c \
        ui.c \
        undo.c \
        usercmd.c \
@@ -1830,6 +1831,7 @@ OBJ_COMMON = \
        objects/textobject.o \
        objects/textprop.o \
        objects/time.o \
+       objects/typval.o \
        objects/ui.o \
        objects/undo.o \
        objects/usercmd.o \
@@ -2006,6 +2008,7 @@ PRO_AUTO = \
        textobject.pro \
        textprop.pro \
        time.pro \
+       typval.pro \
        ui.pro \
        undo.pro \
        usercmd.pro \
@@ -3496,6 +3499,9 @@ objects/textprop.o: textprop.c
 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,6 +4104,10 @@ objects/time.o: time.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/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 \
index 624bd6a82955a71b0a0564bb3e9c3e14e4813930..6cc070472793f02751fd87a8c4d34d94e10236e5 100644 (file)
@@ -84,6 +84,7 @@ textformat.c  | text formatting
 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
index 87b7644251353d3a47f4376bb1840e6fbd578583..6f89030a6069fedf711b4b71932f8b2227849784 100644 (file)
@@ -21,9 +21,6 @@
 #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"
 
@@ -58,7 +55,6 @@ static int eval7_leader(typval_T *rettv, char_u *start_leader, char_u **end_lead
 
 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.
@@ -3303,403 +3299,6 @@ eval_index(
     return OK;
 }
 
-/*
- * 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".
  */
@@ -3761,164 +3360,6 @@ partial_unref(partial_T *pt)
        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.
@@ -4693,23 +4134,6 @@ echo_string(
     return echo_string_core(tv, tofree, numbuf, copyID, TRUE, FALSE, FALSE);
 }
 
-/*
- * 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.
@@ -4791,57 +4215,6 @@ string2float(
 }
 #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.
@@ -5423,558 +4796,6 @@ handle_subscript(
     return ret;
 }
 
-/*
- * 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.
@@ -6356,255 +5177,6 @@ last_set_msg(sctx_T script_ctx)
     }
 }
 
-/*
- * 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
 
 /*
index 11452ce07823fb999e97a993d72ac2036b526322..907815f2d0568f5e49cb61429893c2284393bc90 100644 (file)
@@ -1269,44 +1269,6 @@ non_zero_arg(typval_T *argvars)
                && *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".
@@ -1500,33 +1462,6 @@ f_balloon_split(typval_T *argvars, typval_T *rettv UNUSED)
 # endif
 #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.
@@ -5105,22 +5040,6 @@ f_invert(typval_T *argvars, typval_T *rettv)
     rettv->vval.v_number = ~tv_get_number_chk(&argvars[0], NULL);
 }
 
-/*
- * 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
  */
index 387c62e7199a91e01a1d97b4c5ce7b45a78e7e49..89aeb9640d79e7130ff6649f581bdcddd4f4151f 100644 (file)
@@ -1748,6 +1748,9 @@ EXTERN char e_notset[]    INIT(= N_("E764: Option '%s' is not set"));
 #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
index f8aabc25cd178d332010435f31604b857eda5e60..7a5cc4157e6cca625562257a2accc14ef32b88a5 100644 (file)
@@ -226,6 +226,7 @@ void mbyte_im_set_active(int active_arg);
 # include "textobject.pro"
 # include "textformat.pro"
 # include "time.pro"
+# include "typval.pro"
 # include "ui.pro"
 # include "undo.pro"
 # include "usercmd.pro"
index 14b5f261db68a3c4ee1b796170c9483202ad7365..983309948fbd304d15cdd6d4180429956674e70f 100644 (file)
@@ -29,13 +29,8 @@ int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate);
 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);
@@ -45,10 +40,8 @@ int set_ref_in_list_items(list_T *l, int copyID, ht_stack_T **ht_stack);
 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);
@@ -58,19 +51,6 @@ char_u *find_name_end(char_u *arg, char_u **expr_start, char_u **expr_end, int f
 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);
@@ -79,7 +59,5 @@ int get_echo_attr(void);
 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 : */
index 33dea2c7fccdc6ff2a78c6dbebad6781a3ef1f4f..f68527f9ad99c566b7e3bb6f4fe04a3e2a6f2320 100644 (file)
@@ -10,9 +10,6 @@ int call_internal_func(char_u *name, int argcount, typval_T *argvars, typval_T *
 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);
diff --git a/src/proto/typval.pro b/src/proto/typval.pro
new file mode 100644 (file)
index 0000000..6eebde2
--- /dev/null
@@ -0,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 : */
diff --git a/src/typval.c b/src/typval.c
new file mode 100644 (file)
index 0000000..cd2a862
--- /dev/null
@@ -0,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
index 0adfc266061e8a1e8743d46b5a68dd0132e99b77..7eafcc1df61a843a5295854656ae8bd355f4b074 100644 (file)
@@ -746,6 +746,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    847,
 /**/
     846,
 /**/